partkeepr

fork of partkeepr
git clone https://git.e1e0.net/partkeepr.git
Log | Files | Refs | Submodules | README | LICENSE

commit 82925f5283828209ffbfad6551bbc4e7e3f40bcd
parent 8eb0b24379c31b47e5bb715eb2b4a1183ce3a991
Author: Felicia Hummel <felicitus@felicitus.org>
Date:   Fri,  9 Dec 2016 17:19:27 +0100

Merge pull request #751 from partkeepr/PartKeepr-163

Added OneToMany relationships and save function
Diffstat:
Mapp/config/config_partkeepr.yml | 13+++++++++++++
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Importer/Importer.js | 222++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Importer/ImporterOneToManyConfiguration.js | 203++-----------------------------------------------------------------------------
Msrc/PartKeepr/ImportBundle/Configuration/BaseConfiguration.php | 25+++++++++++++++++++++++++
Msrc/PartKeepr/ImportBundle/Configuration/Configuration.php | 50+++++++++++++++++++++++++++++++++++++++-----------
Msrc/PartKeepr/ImportBundle/Configuration/FieldConfiguration.php | 10+++++-----
Msrc/PartKeepr/ImportBundle/Configuration/ManyToOneConfiguration.php | 68+++++++++++++++++++++++++++++++++++++++++++-------------------------
Asrc/PartKeepr/ImportBundle/Configuration/OneToManyConfiguration.php | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/PartKeepr/ImportBundle/Entity/ImportPreset.php | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/PartKeepr/ImportBundle/Service/ImporterService.php | 10+++++++---
Msrc/PartKeepr/ProjectBundle/Entity/ProjectPart.php | 50+++++++++++++++++++++++++++-----------------------
11 files changed, 519 insertions(+), 290 deletions(-)

diff --git a/app/config/config_partkeepr.yml b/app/config/config_partkeepr.yml @@ -266,6 +266,19 @@ services: arguments: - { groups: [ "default" ] } + resource.import_preset: + parent: "api.resource" + arguments: [ "PartKeepr\ImportBundle\Entity\ImportPreset" ] + tags: [ { name: "api.resource" } ] + calls: + - method: "initFilters" + arguments: [ [ "@doctrine_reflection_service.search_filter" ] ] + - method: "initNormalizationContext" + arguments: [ { groups: [ "default" ] } ] + - method: "initDenormalizationContext" + arguments: + - { groups: [ "default" ] } + resource.part.collection_operation.custom_post: class: "Dunglas\ApiBundle\Api\Operation\Operation" public: false diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Importer/Importer.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Importer/Importer.js @@ -11,7 +11,28 @@ Ext.define("PartKeepr.Importer.Importer", { xtype: 'button', itemId: "executeImport", text: i18n("Execute import") - } + }, + { + xtype: 'tbseparator', + }, + { + xtype: 'combo', + store: { + model: 'PartKeepr.ImportBundle.Entity.ImportPreset', + autoLoad: true + }, + itemId: 'importerPresetCombo', + displayField: 'name', + valueField: '@id' + }, { + xtype: 'button', + itemId: "savePreset", + text: i18n("Save Preset") + }, { + xtype: 'button', + itemId: "deletePreset", + text: i18n("Delete Preset") + }, ], items: [ { @@ -87,8 +108,16 @@ Ext.define("PartKeepr.Importer.Importer", { region: 'east', width: 300, split: true, - itemId: "debugger", - scollable: true + layout: 'fit', + + items: [ + { + xtype: 'textarea', + itemId: "debugger", + scrollable: true, + style: 'font-family: monospace;' + } + ] }, { xtype: 'tabpanel', @@ -104,6 +133,28 @@ Ext.define("PartKeepr.Importer.Importer", { title: i18n("Preview"), itemId: 'preview', bodyStyle: "overflow: scroll;" + }, { + title: i18n("Errors"), + itemId: 'errorsGrid', + xtype: 'grid', + columns: [ + { + text: i18n("Path"), + flex: 1, + dataIndex: "node", + renderer: function (val) + { + return val.getPath("text", "/"); + } + }, { + text: i18n("Error"), + flex: 1, + dataIndex: "error" + } + ], + store: { + fields: ["node", "error"] + } } ] } @@ -124,7 +175,41 @@ Ext.define("PartKeepr.Importer.Importer", { this.importConfiguration = {}; + this.applyConfiguration(); + + this.importColumnsStore = Ext.create("Ext.data.Store", { + fields: ["headerIndex", "headerName"], + storeId: "importColumns" + }); + + this.down("#importerPresetCombo").getStore().addFilter({ + property: "baseEntity", + operator: "=", + value: this.model.getName() + }); + + this.down("#importerEntityConfiguration").setModel(this.model); + + this.down("#fieldTree").on("selectionchange", this.onFieldChange, this); + this.down("#fieldTree").on("beforeselect", this.onBeforeSelect, this); + this.down("#selectImportFile").on("click", this.uploadCSVFile, this); + this.down("#executeImport").on("click", this.executeImport, this); + this.down("#importerEntityConfiguration").on("configChanged", this.onConfigChange, this); + this.down("#importerFieldConfiguration").on("configChanged", this.onConfigChange, this); + this.down("#importerOneToManyConfiguration").on("configChanged", this.onConfigChange, this); + this.down("#importerManyToOneConfiguration").on("configChanged", this.onConfigChange, this); + + this.down("#savePreset").on("click", this.onPresetSave, this); + this.down("#importerPresetCombo").on("select", this.onPresetSelect, this); + + this.loadData("/~felicitus/PartKeepr/web/app_dev.php/api/temp_uploaded_files/668"); + }, + applyConfiguration: function () + { var rootNode = this.down("#fieldTree").getRootNode(); + + rootNode.removeAll(); + rootNode.set("text", this.model.getName()); rootNode.set("data", { name: "", @@ -139,22 +224,36 @@ Ext.define("PartKeepr.Importer.Importer", { treeMaker.make(rootNode, this.model, "", Ext.bind(this.appendFieldData, this)); rootNode.expand(); + }, + onPresetSelect: function (combo, record) + { + this.importConfiguration = Ext.decode(record.get("configuration")); - this.down("#importerEntityConfiguration").setModel(this.model); + // @todo check if the json string is correct + // @todo refresh active panels - this.importColumnsStore = Ext.create("Ext.data.Store", { - fields: ["headerIndex", "headerName"], - storeId: "importColumns" - }); + this.applyConfiguration(); + this.refreshPreview(); + + }, + onPresetSave: function () + { + var presetName = this.down("#importerPresetCombo").getRawValue(); + + if (presetName === "") { + Ext.Msg.alert(i18n("Preset Name Empty"), + i18n("The preset name cannot be empty. Type a preset name in the box to the left.")); + return; + } + + var config = Ext.encode(this.importConfiguration); + + var j = Ext.create("PartKeepr.ImportBundle.Entity.ImportPreset"); + j.set("name", presetName); + j.set("configuration", config); + j.set("baseEntity", this.model.getName()); + j.save(); - this.down("#fieldTree").on("selectionchange", this.onFieldChange, this); - this.down("#fieldTree").on("beforeselect", this.onBeforeSelect, this); - this.down("#selectImportFile").on("click", this.uploadCSVFile, this); - this.down("#executeImport").on("click", this.executeImport, this); - this.down("#importerEntityConfiguration").on("configChanged", this.onConfigChange, this); - this.down("#importerFieldConfiguration").on("configChanged", this.onConfigChange, this); - this.down("#importerOneToManyConfiguration").on("configChanged", this.onConfigChange, this); - this.down("#importerManyToOneConfiguration").on("configChanged", this.onConfigChange, this); }, executeImport: function () { @@ -180,7 +279,8 @@ Ext.define("PartKeepr.Importer.Importer", { xtype: 'panel', itemId: 'resultPanel', listeners: { - render: function (p) { + render: function (p) + { p.getEl().dom.innerHTML = "<pre><strong>Import Results</strong>\n\n" + response.logs + "</pre>"; } } @@ -211,17 +311,16 @@ Ext.define("PartKeepr.Importer.Importer", { }, onConfigChange: function () { - //this.importConfiguration[activeItem.getImporterField()] = activeItem.getImporterConfig(); - var str = JSON.stringify(this.importConfiguration, undefined, 4); - this.down("#debugger").body.dom.innerHTML = "<pre>" + str + "</pre>"; + this.down("#debugger").setValue(str); Ext.Function.defer(this.refreshPreview, 100, this); }, refreshPreview: function () { + this.validateConfig(); Ext.Ajax.request({ @@ -234,7 +333,10 @@ Ext.define("PartKeepr.Importer.Importer", { success: function (response) { var response = Ext.decode(response.responseText); - this.down("#preview").body.dom.innerHTML = "<pre>" + response.logs + "</pre>"; + + if (this.down("#preview").body !== undefined) { + this.down("#preview").body.dom.innerHTML = "<pre>" + response.logs + "</pre>"; + } }, scope: this }); @@ -251,8 +353,6 @@ Ext.define("PartKeepr.Importer.Importer", { } else { if (selected[0].data.data.type === "onetomany") { this.down("#configurationCards").setActiveItem(this.down("#importerOneToManyConfiguration")); - this.down("#importerOneToManyConfiguration").setModel(selected[0].data.data.reference, - selected[0].parentNode.data.data.reference); } else { this.down("#configurationCards").setActiveItem(this.down("#importerManyToOneConfiguration")); this.down("#importerManyToOneConfiguration").setModel(selected[0].data.data.reference); @@ -263,7 +363,7 @@ Ext.define("PartKeepr.Importer.Importer", { this.down("#configurationCards").getLayout().getActiveItem().setImporterConfig( selected[0].data.data.configuration); - if (this.down("#configurationCards").getLayout().getActiveItem().reconfigureColumns !== null) { + if (Ext.isFunction(this.down("#configurationCards").getLayout().getActiveItem().reconfigureColumns)) { this.down("#configurationCards").getLayout().getActiveItem().reconfigureColumns( this.importColumnsStore); } @@ -395,5 +495,79 @@ Ext.define("PartKeepr.Importer.Importer", { store.add(recordData); this.down("#sourceFileGrid").reconfigure(store, columns); + }, + validateConfig: function () + { + this.down("#errorsGrid").setTitle(i18n("Errors")); + this.down("#errorsGrid").getStore().removeAll(); + this.validateConfigNode(this.down("#fieldTree").getRootNode()); + }, + validateConfigNode: function (node) + { + var configuration = node.data.data.configuration; + var recurse = false; + + switch (node.data.data.type) { + case "field": + if (node.data.required) { + if (configuration.fieldConfiguration) { + switch (configuration.fieldConfiguration) { + case "ignore": + this.appendError(node, i18n("The field must be set to a value, but it is ignored")); + break; + case "copyFrom": + if (configuration.copyFromField === "") { + this.appendError(node, i18n( + "The field is configured to copy a value from the source file, but no source file field was configured")); + } + break; + default: + break; + } + } else { + this.appendError(node, i18n("The field is required, but it is not configured")); + } + } + break; + case "manytoone": + if (node.data.required) { + switch (configuration.importBehaviour) { + case "dontSet": + this.appendError(node, i18n("The entity is required, but it is not configured")); + break; + case "matchData": + if (configuration.notFoundBehaviour === "createEntity") { + recurse = true; + } + break; + + } + } else { + recurse = false; + } + break; + case "onetomany": + recurse = false; + break; + default: + recurse = true; + break; + } + + if (recurse) { + for (var i = 0; i < node.childNodes.length; i++) { + this.validateConfigNode(node.childNodes[i]); + } + } + }, + appendError: function (node, error) + { + this.down("#errorsGrid").getStore().add({node: node, error: error}); + + var title = i18n("Errors") + " (" + + this.down("#errorsGrid").getStore().getCount() + ")"; + + + this.down("#errorsGrid").setTitle(title); } }); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Importer/ImporterOneToManyConfiguration.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Importer/ImporterOneToManyConfiguration.js @@ -11,10 +11,6 @@ Ext.define("PartKeepr.Importer.ImporterOneToManyConfiguration", { defaultListenerScope: true, defaultConfig: { importBehaviour: "ignore", - updateBehaviour: "dontUpdate", - notFoundBehaviour: "stopImport", - notFoundSetToEntity: "", - matchers: [], }, items: [ { @@ -30,174 +26,25 @@ Ext.define("PartKeepr.Importer.ImporterOneToManyConfiguration", { }, { xtype: 'radio', - boxLabel: i18n("Match import data with existing data using:"), + boxLabel: i18n("Create new item"), name: 'importBehaviour', checked: false, - inputValue: 'matchData', - itemId: 'matchData', + inputValue: 'createNew', + itemId: 'createNew', listeners: { change: "onChange" } }, - { - xtype: 'fieldcontainer', - layout: { - type: 'vbox', - align: 'stretch' - }, - margin: { - left: 20, - }, - items: [ - { - disabled: true, - xtype: 'importFieldMatcherGrid', - itemId: 'importFieldMatcherGrid', - height: 100, - listeners: { - change: "onChange" - } - }, - { - xtype: 'fieldset', - title: i18n("Behaviour when item exists"), - items: [ - { - - xtype: 'radio', - boxLabel: i18n("Don't update data if an item exists"), - disabled: true, - inputValue: 'dontUpdate', - itemId: 'dontUpdateData', - name: 'updateBehaviour', - listeners: { - change: "onChange" - } - }, - { - xtype: 'radio', - boxLabel: i18n("Update data if an item exists"), - disabled: true, - checked: true, - inputValue: 'update', - itemId: 'updateData', - name: 'updateBehaviour', - listeners: { - change: "onChange" - } - - - } - ] - }, - { - xtype: 'fieldset', - title: i18n("Behaviour when item does not exist"), - items: [ - { - xtype: 'radio', - boxLabel: i18n("Stop import"), - disabled: true, - inputValue: 'stopImport', - itemId: 'stopImport', - checked: true, - name: 'notFoundBehaviour', - listeners: { - change: "onChange" - } - }, - { - xtype: 'radio', - boxLabel: i18n("Create new entity"), - disabled: true, - inputValue: 'createEntity', - itemId: 'createEntity', - checked: false, - name: 'notFoundBehaviour', - listeners: { - change: "onChange" - } - }, - { - xtype: 'fieldcontainer', - layout: { - type: 'hbox' - }, - items: [ - { - xtype: 'radio', - boxLabel: i18n("Set to:"), - name: 'notFoundBehaviour', - inputValue: 'setToEntity', - disabled: true, - checked: false, - itemId: 'setTo', - listeners: { - change: "onChange" - } - }, { - xtype: 'textfield', - itemId: 'notFoundSetToEntity', - name: 'notFoundSetToEntity', - disabled: true, - readOnly: true, - listeners: { - change: "onChange" - } - }, { - xtype: 'button', - text: i18n("Select entity…"), - disabled: true, - itemId: 'notFoundSelectEntity' - } - ] - }, - ] - } - - ] - } ], initComponent: function () { this.callParent(arguments); - var importBehaviourChangeListeners = ["#matchData", "#dontUpdateData", "#updateData", "#setTo", "#stopImport"]; + var importBehaviourChangeListeners = ["#createNew", "#ignore"]; for (var i = 0; i < importBehaviourChangeListeners.length; i++) { this.down(importBehaviourChangeListeners[i]).on("change", this.onImportBehaviourChange, this, {delay: 50}); } - - this.down("#notFoundSelectEntity").on("click", this.onEntitySelectClick, this); - - }, - onEntitySelectClick: function () - { - this.entitySelector = Ext.create("Ext.window.Window", { - items: Ext.create("PartKeepr.Widgets.EntityPicker", { - model: this.model, - listeners: { - entityselect: this.onEntitySelect, - scope: this - }, - ittemId: "entitySelectorPanel" - }), - title: i18n("Select entity"), - width: "80%", - height: "80%", - modal: true, - layout: 'fit', - maximizable: true, - closeAction: 'destroy' - }); - - this.entitySelector.show(); - }, - onEntitySelect: function (entity) - { - - this.down("#notFoundSetToEntity").setValue(entity.getId()); - this.entitySelector.destroy(); }, onChange: function () { @@ -211,53 +58,17 @@ Ext.define("PartKeepr.Importer.ImporterOneToManyConfiguration", { onImportBehaviourChange: function () { var fieldValues = this.getForm().getFieldValues(); - - if (fieldValues.importBehaviour === "matchData") { - this.down("#importFieldMatcherGrid").enable(); - this.down("#dontUpdateData").enable(); - this.down("#updateData").enable(); - this.down("#stopImport").enable(); - this.down("#createEntity").enable(); - this.down("#setTo").enable(); - - if (fieldValues.notFoundBehaviour === "setToEntity") { - this.down("#notFoundSetToEntity").enable(); - this.down("#notFoundSelectEntity").enable(); - } else { - this.down("#notFoundSetToEntity").disable(); - this.down("#notFoundSelectEntity").disable(); - } - } else { - this.down("#createEntity").disable(); - this.down("#importFieldMatcherGrid").disable(); - this.down("#dontUpdateData").disable(); - this.down("#updateData").disable(); - this.down("#setTo").disable(); - this.down("#stopImport").disable(); - } }, setModel: function (model, ignoreModel) { - var ignoreModelName = null; - if (ignoreModel !== undefined) { - ignoreModelName = ignoreModel.getName(); - } - - this.down("#importFieldMatcherGrid").setModel(model, ignoreModelName); - this.model = model; }, getImporterConfig: function () { var config = this.getForm().getFieldValues(); - config.matchers = this.down("#importFieldMatcherGrid").getImporterConfig(); return config; }, - reconfigureColumns: function (columnsStore) - { - this.down("#importFieldMatcherGrid").reconfigureColumns(columnsStore); - }, setImporterConfig: function (config) { this.importerConfig = config; @@ -266,12 +77,6 @@ Ext.define("PartKeepr.Importer.ImporterOneToManyConfiguration", { this.loading = true; this.getForm().setValues(this.importerConfig); - if (this.importerConfig.hasOwnProperty("matchers")) { - this.down("#importFieldMatcherGrid").setImporterConfig(this.importerConfig.matchers); - } else { - this.down("#importFieldMatcherGrid").setImporterConfig({}); - } - this.loading = false; } diff --git a/src/PartKeepr/ImportBundle/Configuration/BaseConfiguration.php b/src/PartKeepr/ImportBundle/Configuration/BaseConfiguration.php @@ -24,6 +24,10 @@ class BaseConfiguration protected $iriConverter; + static $logs = []; + + static $persistEntities = []; + public function __construct(ClassMetadata $classMetadata, $baseEntity, ReflectionService $reflectionService, EntityManager $em, AdvancedSearchFilter $advancedSearchFilter, IriConverter $iriConverter) { $this->classMetadata = $classMetadata; @@ -37,4 +41,25 @@ class BaseConfiguration public function import ($row) { } + + public function persist ($entity) { + self::$persistEntities[] = $entity; + } + + public function getPersistEntities () { + return self::$persistEntities; + } + + + public function log ($message) { + self::$logs[] = $message; + } + + public function getLog () { + return self::$logs; + } + + public function clearLog () { + self::$logs = []; + } } diff --git a/src/PartKeepr/ImportBundle/Configuration/Configuration.php b/src/PartKeepr/ImportBundle/Configuration/Configuration.php @@ -1,7 +1,9 @@ <?php namespace PartKeepr\ImportBundle\Configuration; +use Symfony\Component\Finder\Tests\Iterator\Iterator; use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\PropertyAccess\Tests\Fixtures\TraversableArrayObject; class Configuration extends BaseConfiguration @@ -16,12 +18,16 @@ class Configuration extends BaseConfiguration */ private $manyToOneAssociations = []; + /** + * @var OneToManyConfiguration[] + */ + private $oneToManyAssociations = []; + public function parseConfiguration($importConfiguration) { if (property_exists($importConfiguration, "fields")) { foreach ($importConfiguration->fields as $field => $configuration) { if ($this->classMetadata->hasField($field)) { - $fieldConfiguration = new FieldConfiguration($this->classMetadata, $this->baseEntity, $this->reflectionService, $this->em, $this->advancedSearchFilter, $this->iriConverter); $fieldConfiguration->setFieldName($field); @@ -29,7 +35,7 @@ class Configuration extends BaseConfiguration $this->fields[] = $fieldConfiguration; } } else { - throw new \Exception("Field $field not found in ".$this->baseEntity); + //throw new \Exception("Field $field not found in ".$this->baseEntity); } } } @@ -47,7 +53,25 @@ class Configuration extends BaseConfiguration $this->manyToOneAssociations[] = $manyToOneconfiguration; } } else { - throw new \Exception("Association $manyToOne not found in ".$this->baseEntity); + //throw new \Exception("Association $manyToOne not found in ".$this->baseEntity); + } + } + } + + if (property_exists($importConfiguration, "onetomany")) { + foreach ($importConfiguration->onetomany as $oneToMany => $configuration) { + if ($this->classMetadata->hasAssociation($oneToMany)) { + $targetClass = $this->classMetadata->getAssociationTargetClass($oneToMany); + $cm = $this->em->getClassMetadata($targetClass); + $oneToManyConfiguration = new OneToManyConfiguration($cm, $targetClass, + $this->reflectionService, $this->em, $this->advancedSearchFilter, $this->iriConverter); + $oneToManyConfiguration->setAssociationName($oneToMany); + + if ($oneToManyConfiguration->parseConfiguration($configuration) !== false) { + $this->oneToManyAssociations[] = $oneToManyConfiguration; + } + } else { + //throw new \Exception("Association $oneToMany not found in ".$this->baseEntity); } } } @@ -56,16 +80,13 @@ class Configuration extends BaseConfiguration } public function import ($row) { - $logs = []; - $obj = new $this->baseEntity; + $this->persist($obj); $accessor = PropertyAccess::createPropertyAccessor(); foreach ($this->fields as $field) { $name = $field->getFieldName(); - list($data, $log) = $field->import($row); - - $logs[] = $log; + $data = $field->import($row); if ($data !== null) { $accessor->setValue($obj, $name, $data); @@ -74,13 +95,20 @@ class Configuration extends BaseConfiguration foreach ($this->manyToOneAssociations as $manyToOneAssociation) { $name = $manyToOneAssociation->getAssociationName(); - list($data, $log) = $manyToOneAssociation->import($row); - $logs[] = $log; + $data = $manyToOneAssociation->import($row); if ($data !== null) { $accessor->setValue($obj, $name, $data); } } - return [$obj, $logs]; + foreach ($this->oneToManyAssociations as $oneToManyAssociation) { + $name = $oneToManyAssociation->getAssociationName(); + $data = $oneToManyAssociation->import($row); + if ($data !== null) { + $accessor->setValue($obj, $name, array($data)); + } + } + + return $obj; } } diff --git a/src/PartKeepr/ImportBundle/Configuration/FieldConfiguration.php b/src/PartKeepr/ImportBundle/Configuration/FieldConfiguration.php @@ -74,15 +74,15 @@ class FieldConfiguration extends BaseConfiguration { switch ($this->fieldConfiguration) { case self::FIELDCONFIGURATION_FIXEDVALUE: - return [$this->fixedValue, - sprintf("Would set field %s to fixed value %s", $this->fieldName, $this->fixedValue)]; + $this->log(sprintf("Would set field %s to fixed value %s", $this->fieldName, $this->fixedValue)); + return $this->fixedValue; break; case self::FIELDCONFIGURATION_COPYFROM: - return [$row[$this->copyFromField], - sprintf("Would set field %s to value %s (import column %s)", $this->fieldName, $row[$this->copyFromField], $this->copyFromField)]; + $this->log(sprintf("Would set field %s to value %s (import column %s)", $this->fieldName, $row[$this->copyFromField], $this->copyFromField)); + return $row[$this->copyFromField]; break; default: - return [null, ""]; + return null; } } } diff --git a/src/PartKeepr/ImportBundle/Configuration/ManyToOneConfiguration.php b/src/PartKeepr/ImportBundle/Configuration/ManyToOneConfiguration.php @@ -31,14 +31,6 @@ class ManyToOneConfiguration extends Configuration protected $associationName; - /** - * @return mixed - */ - public function getAssociationName() - { - return $this->associationName; - } - protected $importBehaviour; protected $matchers = []; @@ -51,14 +43,6 @@ class ManyToOneConfiguration extends Configuration protected $setToEntity; - /** - * @param mixed $associationName - */ - public function setAssociationName($associationName) - { - $this->associationName = $associationName; - } - public function parseConfiguration($importConfiguration) { if (!property_exists($importConfiguration, "importBehaviour")) { @@ -142,9 +126,8 @@ class ManyToOneConfiguration extends Configuration switch ($this->importBehaviour) { case self::IMPORTBEHAVIOUR_ALWAYSSETTO: $targetEntity = $this->iriConverter->getItemFromIri($this->setToEntity); - - return [$targetEntity, - sprintf("Would set %s to %s#%s", $this->associationName, $this->baseEntity, $targetEntity->getId())]; + $this->log(sprintf("Would set %s to %s#%s", $this->associationName, $this->baseEntity, $targetEntity->getId())); + return $targetEntity; break; case self::IMPORTBEHAVIOUR_MATCHDATA: $configuration = []; @@ -161,7 +144,6 @@ class ManyToOneConfiguration extends Configuration $configuration = $this->advancedSearchFilter->extractConfiguration($configuration, []); - $filters = $configuration['filters']; $sorters = $configuration['sorters']; $qb = new \Doctrine\ORM\QueryBuilder($this->em); @@ -172,16 +154,52 @@ class ManyToOneConfiguration extends Configuration try { $result = $qb->getQuery()->getSingleResult(); - return [$result, - sprintf("Would set %s to %s#%s", $this->associationName, $this->baseEntity, $result->getId())]; + if ($this->updateBehaviour === self::UPDATEBEHAVIOUR_UPDATEDATA) { + // @todo Update the entity with the specified values + } + + $this->log(sprintf("Would set %s to %s#%s", $this->associationName, $this->baseEntity, $result->getId())); + return $result; } catch (\Exception $e) { - // @todo implement not found cases - return [null, sprintf("Would stop import as the match %s for association %s was not found", implode(",",$descriptions), $this->getAssociationName())]; + } + + switch ($this->notFoundBehaviour) { + case self::NOTFOUNDBEHAVIOUR_STOPIMPORT: + $this->log(sprintf("Would stop import as the match %s for association %s was not found", implode(",",$descriptions), $this->getAssociationName())); + break; + case self::NOTFOUNDBEHAVIOUR_SETTOENTITY: + $targetEntity = $this->iriConverter->getItemFromIri($this->notFoundSetToEntity); + $this->log(sprintf("Would set the association %s to %s, since the match %s for association %s was not found", $this->getAssociationName(), $this->notFoundSetToEntity, implode(",",$descriptions))); + return $targetEntity; + break; + case self::NOTFOUNDBEHAVIOUR_CREATEENTITY: + $this->log(sprintf("Would create a new entity of type %s", $this->baseEntity)); + return parent::import($row); + break; + + } + break; } - return [null, ""]; + return null; + } + + /** + * @return mixed + */ + public function getAssociationName() + { + return $this->associationName; + } + + /** + * @param mixed $associationName + */ + public function setAssociationName($associationName) + { + $this->associationName = $associationName; } } diff --git a/src/PartKeepr/ImportBundle/Configuration/OneToManyConfiguration.php b/src/PartKeepr/ImportBundle/Configuration/OneToManyConfiguration.php @@ -0,0 +1,65 @@ +<?php +namespace PartKeepr\ImportBundle\Configuration; + + +class OneToManyConfiguration extends Configuration +{ + const IMPORTBEHAVIOUR_IGNORE = "ignore"; + const IMPORTBEHAVIOUR_CREATENEW = "createNew"; + + const importBehaviours = [ + self::IMPORTBEHAVIOUR_IGNORE, + self::IMPORTBEHAVIOUR_CREATENEW, + ]; + + protected $associationName; + + protected $importBehaviour; + + public function parseConfiguration($importConfiguration) + { + if (!property_exists($importConfiguration, "importBehaviour")) { + return false; + } + + if (!in_array($importConfiguration->importBehaviour, self::importBehaviours)) { + throw new \Exception("The key importBehaviour contains an invalid value!"); + } + + $this->importBehaviour = $importConfiguration->importBehaviour; + + return parent::parseConfiguration($importConfiguration); + } + + public function import($row) + { + switch ($this->importBehaviour) { + case self::IMPORTBEHAVIOUR_IGNORE: + return null; + break; + case self::IMPORTBEHAVIOUR_CREATENEW: + $this->log(sprintf("Would create a new entity of type %s for relation %s", $this->baseEntity, $this->getAssociationName())); + return parent::import($row); + break; + } + + return null; + } + + /** + * @return mixed + */ + public function getAssociationName() + { + return $this->associationName; + } + + /** + * @param mixed $associationName + */ + public function setAssociationName($associationName) + { + $this->associationName = $associationName; + } + +} diff --git a/src/PartKeepr/ImportBundle/Entity/ImportPreset.php b/src/PartKeepr/ImportBundle/Entity/ImportPreset.php @@ -0,0 +1,93 @@ +<?php + +namespace PartKeepr\ImportBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; +use PartKeepr\CoreBundle\Entity\BaseEntity; +use PartKeepr\DoctrineReflectionBundle\Annotation\TargetService; +use Symfony\Component\Serializer\Annotation\Groups; + +/** + * @ORM\Entity + * @TargetService(uri="/api/import_presets") + */ +class ImportPreset extends BaseEntity +{ + /** + * Holds the base entity + * + * @ORM\Column(length=255) + * @Groups({"default"}) + * + * @var string + */ + private $baseEntity; + + /** + * Holds the import preset name. + * + * @ORM\Column(length=255) + * @Groups({"default"}) + * + * @var string + */ + private $name; + + /** + * Defines the preset configuration + * + * @ORM\Column(type="text") + * @Groups({"default"}) + * + * @var string + */ + private $configuration; + + /** + * @return string + */ + public function getBaseEntity() + { + return $this->baseEntity; + } + + /** + * @param string $baseEntity + */ + public function setBaseEntity($baseEntity) + { + $this->baseEntity = $baseEntity; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * @return string + */ + public function getConfiguration() + { + return $this->configuration; + } + + /** + * @param string $configuration + */ + public function setConfiguration($configuration) + { + $this->configuration = $configuration; + } +} diff --git a/src/PartKeepr/ImportBundle/Service/ImporterService.php b/src/PartKeepr/ImportBundle/Service/ImporterService.php @@ -66,14 +66,18 @@ class ImporterService $entities = []; $logs = []; + $configuration = $this->parseConfiguration(); + foreach ($this->importData as $row) { - list($entity, $log) = $this->parseConfiguration()->import($row); + $entity = $configuration->import($row); $entities[] = $entity; $logs[] = implode("<br/>", - [ "data" => implode(",",$row), '<p style="text-indent: 50px;">', "log" => " ".implode("<br/> ", $log), '</p>']); + [ "data" => implode(",",$row), '<p style="text-indent: 50px;">', "log" => " ".implode("<br/> ", $configuration->getLog() ), '</p>']); + + $configuration->clearLog(); } - return [$entities, implode("<br/>", $logs)]; + return [$configuration->getPersistEntities(), implode("<br/>", $logs)]; } public function parseConfiguration() diff --git a/src/PartKeepr/ProjectBundle/Entity/ProjectPart.php b/src/PartKeepr/ProjectBundle/Entity/ProjectPart.php @@ -8,6 +8,7 @@ use PartKeepr\DoctrineReflectionBundle\Annotation\ByReference; use PartKeepr\DoctrineReflectionBundle\Annotation\TargetService; use PartKeepr\PartBundle\Entity\Part; use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints as Assert; /** * Represents a project part. @@ -22,7 +23,9 @@ class ProjectPart extends BaseEntity * * @ORM\ManyToOne(targetEntity="PartKeepr\PartBundle\Entity\Part", inversedBy="projectParts") * @Groups({"default"}) - * @ByReference + * @Assert\NotNull() + * + * @ByReference() * * @var Part */ @@ -42,6 +45,7 @@ class ProjectPart extends BaseEntity * Specifies the project which belongs to this project part. * * @ORM\ManyToOne(targetEntity="PartKeepr\ProjectBundle\Entity\Project", inversedBy="parts") + * @Assert\NotNull() * * @var Project */ @@ -58,16 +62,6 @@ class ProjectPart extends BaseEntity private $remarks; /** - * Sets the part which belongs to this entry. - * - * @param Part $part - */ - public function setPart(Part $part) - { - $this->part = $part; - } - - /** * Returns the part which belongs to this entry. * * @return Part @@ -78,13 +72,13 @@ class ProjectPart extends BaseEntity } /** - * Sets the quantity for this entry. + * Sets the part which belongs to this entry. * - * @param int $quantity + * @param Part $part */ - public function setQuantity($quantity) + public function setPart(Part $part) { - $this->quantity = intval($quantity); + $this->part = $part; } /** @@ -98,13 +92,13 @@ class ProjectPart extends BaseEntity } /** - * Sets the project assigned to this entry. + * Sets the quantity for this entry. * - * @param Project $project + * @param int $quantity */ - public function setProject(Project $project = null) + public function setQuantity($quantity) { - $this->project = $project; + $this->quantity = intval($quantity); } /** @@ -118,13 +112,13 @@ class ProjectPart extends BaseEntity } /** - * Sets the remarks for this entry. + * Sets the project assigned to this entry. * - * @param string $remarks + * @param Project $project */ - public function setRemarks($remarks) + public function setProject(Project $project = null) { - $this->remarks = $remarks; + $this->project = $project; } /** @@ -136,4 +130,14 @@ class ProjectPart extends BaseEntity { return $this->remarks; } + + /** + * Sets the remarks for this entry. + * + * @param string $remarks + */ + public function setRemarks($remarks) + { + $this->remarks = $remarks; + } }