OOPI (an acronym from "object-oriented, programming and importer) is a WordPress plugin providing an object-oriented library to ease and give structure for importing data into WordPress from external sources.
Install the plugin with Composer:
composer require devgeniem/wp-oopi
Then activate the plugin from the WordPress dashboard or with the WP-CLI.
wp plugin activate wp-oopi
The plugin provides functionalities for importing various data types. You can think of the importer as an integration layer between your custom data source and the WordPress database. Your job is to format the data into OOPI objects by manually using class constructors or the provided helper factories. If your external data source can provide the data in the OOPI data format, your job is as simple as decoding the data into a PHP object and then passing it for a create
method in a importable factory.
OOPI provides the saving process for your data by using WordPress functions to interact with the database. With OOPI you get a standardized way of building developer friendly importers and it helps you write better code by providing important features such as error handling and logging.
An import is a two step process. First step is to create an importable object. If it is a post you are importing, then you create a \Geniem\Oopi\Importable\PostImportable
object. You can use the class constructor directly or use the easy way and use a provided factory method. The PostFactory::create
method takes a unique OOPI id and a full importer object as a parameter, validates all fields and converts the data into strictly typed objects. During the import process, the OOPI id is stored in (post) meta and it is used to identify your imported object after it is initially created. Subsequent imports with the same OOPI id will update the matching post.
After your data is set, the actual importing is done by calling the importable's import()
method. This method uses composition to call the actual import method of an importer. All importers implement the \Geniem\Oopi\Intefaces\Importer
inteface and all importables should contain an importer. The import
method will handle saving all the data attached to the importable object into WordPress database.
Importables allow changing the import process by passing an importer object for the class constructor. If none is set, all importables get a default importer provided by OOPI during the instantiation.
See the post example.
You can import terms along your posts. But if you require more control over your term importing, for example to prevent importing posts after erroneous term imports, you can use the OOPI importing process for terms.
To get started, create an importable by instantiating a \Geniem\Oopi\Importable\TermImportable
directly and use setters to set your data or use the \Geniem\Oopi\Factory\Importable\TermFactory::create
method. After your data is set, import the term object by calling its import
method. Just like with a post imortable, this will call the import
method of the attached term importer.
See the term example.
Attachments can be imported along post importables, but if required, you can import attachments in a dedicated process. Importing attachments follows the same process as posts and terms. Create an attachment importable by instantiating the \Geniem\Oopi\Importable\AttachmentImportable
class or by calling the \Geniem\Oopi\Factory\Importable\AttachmentFactory::create
method. After setting all the data, call the import
method of the importable. This will trigger the import process by calling the import
method in the attached importer.
Attachments are problematic when importing data into WordPress. OOPI attempts to make it as simple as possible by handling the file transfering from the provided source (src
attribute), but bear in mind, downloading and uploading files from external sources and into WordPress will take time. With OOPI, you are fully in control of your importing process and you might run into timeouts if your server configurations are not properly set to handle your workloads.
Note! OOPI supports only image MIME types currently.
To customize the term importing process, create your own importable class extending the corresponding importer provided by OOPI or by directly implementing the \Geniem\Oopi\Interfaces\Importable
interface. For example, to customize the attachment import process, extend the \Geniem\Oopi\Importer\AttachmentImporter
and override its methods as needed. To override the default importer instance in an importable, instantiate your custom importer and pass it for your importable constructor or factory.
Importables have attributes that are saved during the import process. Each attribute class should have a corresponding saver for handling the database interactions. For example, post meta is an attribute you can attach to post importables. When the post is imported, the post meta attribute is saved with the post meta saver using the WordPress post meta functions.
See the list of all available attributes here.
OOPI supports localization with the \Geniem\Oopi\Attribute\Language
attribute. Currently, OOPI provides a language saver for Polylang and is able to localize posts, terms and attachments.
The properties for a language attribute are:
importable
(Required)
- The related importable object.locale
(Required)
- The locale string (e.q. 'en').main_oopi_id
(Optional)
- The OOPI id of importable in the main language. It is used to map translations.saver
(Optional)
- The language saver.
To automatically map your importables as translations of one another, set the main_oopi_id
for importables in other than the main language. For example, if your WordPress site's main language is English, set the OOPI id of the English importable as the main OOPI id for its translations. Import the main language always first for OOPI to be able to map the main object for other languages.
OOPI supports importing Advanced Custom Fields field data with the \Geniem\Oopi\Attribute\AcfField
attribute. The \Geniem\Oopi\Attribute\Saver\AcfFieldSaver
controls the saving process and can handle post and term fields. Note that the field saving is limited by the functionalities of the ACF's update_field
function.
$oopi_id
(string) (Required)
The unique external identifier for your importable.$data
(array|object) (Required)
An array/object containing the following keys:post
(object) (Required)
AWP_Post
instance or an array/object containing the keys and values forWP_Post
class properties.attachments
(array) (Optional)
An array of attachment objects containing:oopi_id
(string) (Required)
An unique id identifying the object in the external data source.src
(string) (Required)
The source from which to upload the image into WordPress.alt
(string) (Optional)
The alt text for the image. This is saved into postmeta.caption
(string) (Optional)
The file caption text.description
(string) (Optional)
The file description text.is_thumbnail
(bool) (Optional)
Defines if the attachment should set as the post thumbnail.
meta
(array) (Optional)
An array of arrays or objects containing:key
(string) (Required)
The meta key.value
(mixed) (Required)
The meta value.
terms
(array) (Optional)
An array containing either-or:(Geniem\Oopi\Importable\TermImportable)
OOPI Term importable.- If the OOPI term importable holds a WP_Term object, importing will override existing term data.
(array|object)
oopi_id
(string) (Required)
All terms must contain an id.term
WP_Term|object|array (Optional)
An object/array containing properties of a WP_Term object or aWP_Term
object. If set, data for the Term importable will be mapped from the object.slug
(string) (Required)
The taxonomy term slug. The term slugs must be unique, ie. they can not collide between different language versions.name
(string) (Required)
The taxonomy term display name.taxonomy
(string) (Required)
The taxonomy name, for examplecategory
.
acf
(array) (Optional)
An array of Advanced Custom Fields data objects containing:type
(string) (Required)
The ACF field type (types).key
(string) (Required)
The ACF field key. This must be the unique key defined for the field.value
(mixed) (Required)
The data value matching the field type specifications.
language
(Geniem\Oopi\Attribute\Language|object|array) (Optional)
Localization information in an OOPI Language object or raw data. Raw data will be converted into a Language instance.
See: TermFactory.
See: AttachmentFactory.
The \Geniem\Oopi\Settings\
class is used to set and load all plugin settings. It uses the following settings that are overridable with constants.
- Setting key
id_prefix
, constantOOPI_ID_PREFIX
, default value'oopi_id_'
. - Setting key
attachment_prefix
, constantOOPI_ATTACHMENT_PREFIX
, default value'oopi_attachment_'
. - Setting key
log_errors
, constantOOPI_LOG_ERRORS
, default valuefalse
. - Setting key
transient_key
, constantOOPI_TRANSIENT_KEY
, default value'oopi_'
. - Setting key
transient_expiration
, constantOOPI_TRANSIENT_EXPIRATION
, default valueHOUR_IN_SECOND
. - Setting key
tmp_folder
, constantOOPI_TMP_FOLDER
, default value'/tmp/'
. Used for handling attachments. - Setting key
table_name
, constantOOPI_TABLE_NAME
, default valuegeniem_importer_log
. - Setting key
log_status_ok
, constantOOPI_LOG_STATUS_OK
, default value'OK'
. - Setting key
log_status_fail
, constantOOPI_LOG_STATUS_FAIL
, default value'FAIL'
. - Setting key
cron_interval_clean_log
, constantOOPI_CRON_INTERVAL_CLEAN_LOG
, default'daily'
.
Example usage:
$oopi_id_prefix = \Geniem\Oopi\Settings::get( 'id_prefix' );
To override the transient expiration time set the following constant before the setting is used the first time.
define( 'OOPI_TRANSIENT_EXPIRATION', MONTH_IN_SECONDS );
To ignore SSL Certificate verification errors in your importer, you can define OOPI_IGNORE_SSL
as true.
define( 'OOPI_IGNORE_SSL', true );
See \Geniem\Oopi\Post::insert_attachment_from_url
.
OOPI uses a generalized error handling. An instance of the error handler is passed as a dependecy to all instances attached to an importable. If an error occurs, the error handler logs (by default logging is enabled) and stores the error in the handler instance. If there where errors during the import process, an exception is thrown for you to catch.
You can customize the error handling process by overriding the default error handler. To do so, create a class implementing the Geniem\Oopi\Interfaces\ErrorHandler
interface and override the default handler instance with your instance by passing it for factories or class constructors.
The plugin creates a custom table into the WordPress database called oopi_log
. This table holds log entries of imports (currently only for posts) and contains the following columns:
id
Log entry id.oopi_id
The importer object id.wp_id
The WordPress post id of the importer object. Stored only if thesave()
is run successfully.import_date_gmt
A GMT timestamp of the import date in MySQL datetime format.data
The importer object data containing all properties including errors.status
The import status:OK|FAIL
.
The log provides a rollback feature. If an import fails the importer tries to roll back the previous successful import. If no previous imports with the OK
status are found, the imported object is set into draft
state to prevent front-end users from accessing posts with malformed data.
To disable the rollback feature set the OOPI_ROLLBACK_DISABLE
constant with a value of true
.
The plugin registers a log cleaner cronjob on plugin activation. The cronjob deletes all rows from the log table by status and import date threshold. Both the statuses and the threshold can be modified with filters oopi_cron_log_cleaner_statuses
and oopi_cron_log_cleaner_threshold
.
The cronjob is run with 'daily'
interval by default, it can be changed with OOPI_CRON_INTERVAL_CLEAN_LOG
constant. Cronjob scheduling can be disabled by defining the constant as false
.
The plugin code is tested using PHPUnit. Local test can be run using the provided Docker container with Docker Compose. The following command will build the container and start it. The container's CMD script will run PHPUnit once and then set up pywathc to watch changes in the ./tests
directory. If changes are made, test are rerun.
$ docker compose up
If you need to debug the tests, the container comes with Xdebug 3 for step debugging. You need to set up PHPUnit using the provided docker container as a remote interpreter. For this, use the php
service in the Docker Compose file and the PHPUnit executable vendor/bin/phpunit
. The full path to the executable inside the container is /usr/src/app/vendor/bin/phpunit
.
This repository also contains a GitHub integration for Travic CI. All commits to the master
branch will be automatically tested with Travis.