PHPackages                             mutoco/silverstripe-mplus-import - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. mutoco/silverstripe-mplus-import

ActiveSilverstripe-vendormodule[Utility &amp; Helpers](/categories/utility)

mutoco/silverstripe-mplus-import
================================

Import data-records from MuseumPlus into SilverStripe.

4147PHP

Since Apr 3Pushed 1y ago2 watchersCompare

[ Source](https://github.com/mutoco/silverstripe-mplus-import)[ Packagist](https://packagist.org/packages/mutoco/silverstripe-mplus-import)[ RSS](/packages/mutoco-silverstripe-mplus-import/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (1)Used By (0)

SilverStripe importer for Museum Plus
=====================================

[](#silverstripe-importer-for-museum-plus)

[![Code Coverage](https://camo.githubusercontent.com/177b71a352b0f40eac5ee2f2da50aea1943f4f761129242a0abdb8d417518d3b/68747470733a2f2f636f6465636f762e696f2f67682f6d75746f636f2f73696c7665727374726970652d6d706c75732d696d706f72742f6272616e63682f6d61696e2f67726170682f62616467652e737667)](https://codecov.io/gh/mutoco/silverstripe-mplus-import)[![Build Status](https://camo.githubusercontent.com/74f2a2c51a339d2e035474d23c57530f628431be9cb400636f9912a0713e92af/68747470733a2f2f7472617669732d63692e636f6d2f6d75746f636f2f73696c7665727374726970652d6d706c75732d696d706f72742e7376673f6272616e63683d6d61696e)](https://travis-ci.com/mutoco/silverstripe-mplus-import)[![Latest Stable Version](https://camo.githubusercontent.com/0cd9808d65823306ec17be24cc5ab8528ac614cf506c24e70955627a0d47ff26/68747470733a2f2f706f7365722e707567782e6f72672f6d75746f636f2f73696c7665727374726970652d6d706c75732d696d706f72742f762f737461626c65)](https://packagist.org/packages/mutoco/silverstripe-mplus-import)[![Monthly Downloads](https://camo.githubusercontent.com/9f21c12d6d43c7c16b0ac1349ce7828d4e57db4fdcf05dc02931609e05b3e09b/68747470733a2f2f706f7365722e707567782e6f72672f6d75746f636f2f73696c7665727374726970652d6d706c75732d696d706f72742f642f6d6f6e74686c79)](https://packagist.org/packages/mutoco/silverstripe-mplus-import)

This is an importer to import data-records from [museumplus by zetcom](https://www.zetcom.com/en/museumplus-en/). It is implemented as a queued-job for the QueuedJobs Module.

Requirements
------------

[](#requirements)

- SilverStripe ^4.7
- [QueuedJobs Module](https://github.com/symbiote/silverstripe-queuedjobs) ^4
- PHP 7.4 or newer

Installation
------------

[](#installation)

Install using composer

```
composer require mutoco/silverstripe-mplus-import 1.x-dev

```

License
-------

[](#license)

See [License](license.md)

Project status
--------------

[](#project-status)

This is not a stable release (yet). The API might change and there are still several things [todo](todo.md)

API configuration
-----------------

[](#api-configuration)

Use the injector to set your credentials for the Mplus API.

```
SilverStripe\Core\Injector\Injector:
  Mutoco\Mplus\Api\Client:
    properties:
      BaseUrl: 'https://example.com/endpoint'
      Username: 'username'
      Password: '`MPLUS_PASSWORD`'
```

Here, an environment variable named `MPLUS_PASSWORD` is used for the actual password. Make sure you set this environment variable in your system or in your `.env` file.

Configuring the data-mapping
----------------------------

[](#configuring-the-data-mapping)

The `ImportEngine` class is responsible for importing Mplus Datarecords into SilverStripe DataObjects. You have to configure a mapping from the remote "modules" to DataObjects by adding the following to your configuration (ideally, this is stored in `app/_config/mplus.yml` or similar):

```
Mutoco\Mplus\Import\ImportEngine:
  modules:
    ModuleName:
      modelClass: DataObjectClass
      fields:
        Field: Path
      relations:
        RelationName:
          name: Path
          type: ModuleName
          fields:
            RelationField: Path
      attachment: RelationName
```

In this simplified example, `ModuleName` stands for a module name in Mplus, eg. `Person`, `Exhibition`, `Object`, etc. Then you specify the mapping for this module. The allowed keys for each Module are:

- `modelClass`: Fully qualified classname of the SilverStripe DataObject
- `fields`: Mapping from SilverStripe field names (eg. typically what you have configured for `$db`) to paths in Mplus. These *have* to start at the "root" level, eg. nested paths need to be written with dot syntax. Example: `AdrContactGrp.ValueTxt`
- `relations`: Mapping from SilverStripe relations to collections in Mplus. Usually, collections in Mplus are either `repeatableGroup` or `moduleReference` nodes. The `RelationName` is the name of the relation in the SilverStripe ORM. Then you set the name (path) and type (name of the referenced module) and optional `fields` that should be stored with the relation (eg. for `many_many_extraFields` or similar).
- `attachment`: If set, an attachment will be stored in this relation. Eg. if you set: `attachment: Image`, the attachment will be stored as the `Image` relation on the DataObject.

Assuming you have a DataObject that represents a Person in SilverStripe, with the FQCN `MyProject\Person`, then your Configuration might look like this:

```
Mutoco\Mplus\Import\ImportEngine:
  modules:
    Person:
      modelClass: MyProject\Person
      fields:
        Firstname: PerFirstNameTxt
        Lastname: PerLastNameTxt
        PlaceOfBirth: PerPlaceBirthTxt
        PlaceOfDeath: PerPlaceDeathTxt
        DateOfBirth: PerDateFromBeginDat
        DateOfDeath: PerDateToEndDat
```

Of course, the `modules` can contain multiple entries, which is necessary for more complex setups with relations. Here's an example:

```
Mutoco\Mplus\Import\ImportEngine:
  modules:
    Person:
      modelClass: MyProjectPerson
      fields:
        Firstname: PerFirstNameTxt
        Lastname: PerLastNameTxt
        PlaceOfBirth: PerPlaceBirthTxt
        PlaceOfDeath: PerPlaceDeathTxt
        DateOfBirth: PerDateFromBeginDat
        DateOfDeath: PerDateToEndDat
    TextBlock:
      modelClass: MyProject\TextBlock
      fields:
        Text: TextClb
        Author: AuthorTxt
    Exhibition:
      modelClass: MyProject\Exhibition
      fields:
        Title: ExhTitleTxt
      relations:
        Texts:
          name: ExhTextGrp
          type: TextBlock
        Person:
          name: ExhPersonRef
          type: Person
```

In this example, you can import "Exhibitions", which will also import all related Persons and Text-Blocks (TextBlock isn't a module in Mplus, it's an arbitrarily named Model, so that a mapping in the relation is possible at all).

Configuring the import job
--------------------------

[](#configuring-the-import-job)

To tell the `ImportJob` which records to import, configure an initial search to get the records to import. The following config would import the Exhibitions with ID `1` and `2`.

The `search` configuration is pretty much an YML to XML mapping of the [Mplus Search API](http://docs.zetcom.com/ws/#Perform_an_ad-hoc_search_for_modules_items).

```
Mutoco\Mplus\Job\ImportJob:
  imports:
    Exhibition:
      search:
        or:
          - type: equalsField
            fieldPath: __id
            operand: 1
          - type: equalsField
            fieldPath: __id
            operand: 2
```

Import flow
-----------

[](#import-flow)

The data-import is implemented as different import steps, which are placed inside a priority queue and executed by the `ImportEngine`.

Usually, the first step to be added to the queue is a `LoadSearchStep`. In this step, a search on the M+ API is performed to load results for the requested model. For each search-result, a `LoadModuleStep` will be created in the queue.

The `LoadModuleStep` is responsible to resolve a complete import tree. That means, that the initial XML Data of the Model gets parsed into a tree instance. The `LoadModuleStep` then follows all relations and completes the tree with data from relations, so that after completion of one `LoadModuleStep`, the importer has a complete Data-Tree for the current model.

The `LoadModuleStep` will enqueue `ImportModuleStep` for the Module that needs to be imported. It can also trigger further `LoadModuleSteps` if more relations need to be loaded.

The `ImportModuleStep` will perform the actual import of the tree-data into the SilverStripe DataObjects. It will traverse the imported tree data and enqueue further `ImportModuleStep` instances for relations.

Assets will be imported with an `ImportAttachmentStep`, which will also be triggered from a `ImportModuleStep`.

Relations will be linked with the `LinkRelationStep` *after* all related Modules of one Module have been imported.

Obsolete records will be removed with the `CleanupRecordsStep` and relations will be cleaned with the `CleanupRelationStep`.

### Extension hooks

[](#extension-hooks)

Some aspects of the import-process can be controlled with methods on the imported records (either as method on the DataObject itself, or as method of an Extension).

#### Callbacks during `LoadModuleStep`

[](#callbacks-during-loadmodulestep)

These callbacks will always get called on your affected DataObject instance. So if you mapped `Exhibition` to a `MyExhibition` DataObject, the callback/extension method will be called on a `MyExhibition` DataObject.

- `transformMplusResultTree`: After the whole import-tree has been resolved, it can be modified by a method on the model that is currently being imported. Parameters are `$tree: TreeNode` and `$engine: ImportEngine`.
- `shouldImportMplusModule`: This method will be called on the currently imported model. If you return `false`, the current model will skip import. Parameters are `$tree: TreeNode` (the fully resolved tree of the current module) and `$engine: ImportEngine`.
- `beforeMplusSearchRelated`: This method will be called before a search is issued to the M+ API to load related modules. Parameters are: `$search: SearchBuilder` (the SearchBuilder that will be used to issue the search), `$module: string` (the name of the related module that will be searched), `$cleanedIds: int[]` the numeric IDs of the modules that should be loaded.

#### Callbacks during `ImportModuleStep`

[](#callbacks-during-importmodulestep)

These callbacks will always get called on your affected DataObject instance. So if you mapped `Exhibition` to a `MyExhibition` DataObject, the callback/extension method will be called on a `MyExhibition` DataObject.

- `beforeMplusSkip`: If the engine determined that a DataObject should skip import (because it wasn't modified), you can return `false` from this method to *not* skip importing. E.g. force an import. Parameters to the callback are Parameters are `$step: ImportModuleStep` and `$engine: ImportEngine`.
- `beforeMplusImport`: Method that will be called *before* import starts. Parameters are `$step: ImportModuleStep` and `$engine: ImportEngine`.
- `transformMplusFieldValue`: Method that will be called to apply any transforms to imported fields values. Parameters are: `$fieldName: string` (the name of the field of the DataObject), `$fieldNode: TreeNode` (the imported Data from the XML node) and `$engine: ImportEngine`. If you return any value from this callback, it should be the new field-value!
- `afterMplusImport`: Method that will be called *after* the import has finished and `write()` has been called on the imported DataObject. Parameters are `$step: ImportModuleStep` and `$engine: ImportEngine`.
- `mplusShouldImportAttachment`: If there's an attachment configured for the current DataObject, you can use this callback to skip import of an attachment. Parameters are: `$attachment: string` (the name of the attachment), `$node: TreeNode` (the current object XML node) and `$engine: ImportEngine`.
- `shouldImportMplusRelation`: Relation nodes in the tree can be excluded from the import process with this callback by returning `false`. Parameters are: `$relationName: string` (the name of the relation), `$child: TreeNode` (tree node of the relation to import), `$engine: ImportEngine`
- `transformMplusRelationField`: Allows you to transform a relation import. You can return a custom value for the relation that should be saved. E.g. look up an ID yourself. Params are: `$field: string` (the name of the imported DataObject relation field), `$node: TreeNode` (the tree node for the relation), `$engine: ImportEngine`

#### Callbacks during `ImportAttachmentStep`

[](#callbacks-during-importattachmentstep)

These callbacks will always get called on your affected DataObject instance. So if you mapped `Exhibition` to a `MyExhibition` DataObject, the callback/extension method will be called on a `MyExhibition` DataObject.

- `shouldImportMplusAttachment`: This callback will fire right after the headers from the remote File are present and allows you to cancel download/import of an attachment by returning `false`. By returning `true`, the import will be forced. If nothing is returned, the default behavior will trigger, which is to only import if the file doesn't exist yet. Parameters are: `$fileName: string` (name of the file), `$field: string` (name of the field on the DataObject), `$step: ImportAttachmentStep` (the current import step) and `$engine: ImportEngine`.

#### Callbacks during `LinkRelationStep` and `CleanupRelationStep`

[](#callbacks-during-linkrelationstep-and-cleanuprelationstep)

These callbacks will always get called on your affected DataObject instance. E.g. the DataObject that has the affected relation.

- `beforeMplusRelationStep`: This callback will fire before the logic of the relation-step runs. Parameters are: `$type: string` (the type of relation, e.g. `has_one`, `many_many` etc.), `$relationName: string` (name of the relation), `$relationIds: array` (the IDs of this relation. This is always an array, also for `has_one`), `$step: AbstractRelationStep` (the relation step instance that is running), `$engine: ImportEngine`.
- `afterMplusRelationStep`: Called *after* relations have been linked. The params are identical to the ones from `beforeMplusRelationStep`.

#### Callbacks during `CleanupRecordsStep`

[](#callbacks-during-cleanuprecordsstep)

This callback will always get called on your affected DataObject instance.

- `beforeMplusDelete`: Called before a DataObject should get deleted (because it was no longer part of the import dataset). You can return `false` from this callback to prevent deletion.

#### Callbacks on `VocabularyItem`

[](#callbacks-on-vocabularyitem)

The `VocabularyItem` is a model for the `VocabularyItem` Module in M+. It is widely used and therefore part of this codebase. You can add an Extension to this model to customise it. There's one extension method that you can use to customise import:

- `onUpdateFromNode`: is called whenever this `VocabularyItem` should get updated by the import process. If you return a `falsy` value, the new `VocabularyItem` data will not get written. Params are: `$node: TreeNode` (the imported tree-node for the current `VocabularyItem`), `$engine: ImportEngine`.

Maintainers
-----------

[](#maintainers)

- Roman Schmid

Bugtracker
----------

[](#bugtracker)

Bugs are tracked in the issues section of this repository. Before submitting an issue please read over existing issues to ensure yours is unique.

If the issue does look like a new bug:

- Create a new issue
- Describe the steps required to reproduce your issue, and the expected outcome. Unit tests, screenshots and screencasts can help here.
- Describe your environment as detailed as possible: SilverStripe version, Browser, PHP version, Operating System, any installed SilverStripe modules.

Please report security issues to the module maintainers directly. Please don't file security issues in the bugtracker.

Development and contribution
----------------------------

[](#development-and-contribution)

If you would like to make contributions to the module please ensure you raise a pull request and discuss with the module maintainers.

###  Health Score

19

—

LowBetter than 10% of packages

Maintenance35

Infrequent updates — may be unmaintained

Popularity14

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity15

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/ace339d9696dfdf9e2f3c6abc212a3abb390b0b51fff48285036b7da8f0be098?d=identicon)[bummzack](/maintainers/bummzack)

---

Top Contributors

[![bummzack](https://avatars.githubusercontent.com/u/1006185?v=4)](https://github.com/bummzack "bummzack (163 commits)")

### Embed Badge

![Health badge](/badges/mutoco-silverstripe-mplus-import/health.svg)

```
[![Health](https://phpackages.com/badges/mutoco-silverstripe-mplus-import/health.svg)](https://phpackages.com/packages/mutoco-silverstripe-mplus-import)
```

###  Alternatives

[anlutro/l4-settings

Persistent settings in Laravel.

9312.4M19](/packages/anlutro-l4-settings)[godruoyi/php-snowflake

An ID Generator for PHP based on Snowflake Algorithm (Twitter announced).

8582.3M61](/packages/godruoyi-php-snowflake)[tuupola/ksuid

K-Sortable Globally Unique IDs

1081.2M4](/packages/tuupola-ksuid)[mcordingley/linearalgebra

Matrix math for PHP.

85146.3k1](/packages/mcordingley-linearalgebra)[hyva-themes/magento2-wysiwyg-svg

Allow SVGs and all tailwind classes in CMS block and page content.

17130.8k](/packages/hyva-themes-magento2-wysiwyg-svg)[syrian-open-source/laravel-youtube-iframe-generator

Laravel package allows you to generate an iframe tag with a video player depending on a youtube URL.

184.2k](/packages/syrian-open-source-laravel-youtube-iframe-generator)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
