From c55088aa8506e2046721203ca6b9b7e3fbbb7dba Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 4 Nov 2021 08:10:32 +0100 Subject: [PATCH] chg: [metaTemplate] Continuation of refactoring - WiP Editing meta field from entities working --- .../20211104072514_MoreMetaFieldColumns.php | 40 ++++++ src/Controller/Component/CRUDComponent.php | 135 ++++++++++++++---- src/Model/Behavior/MetaFieldsBehavior.php | 14 -- src/Model/Table/MetaFieldsTable.php | 14 +- src/Model/Table/MetaTemplateFieldsTable.php | 2 + templates/MetaTemplateFields/index.php | 7 +- .../genericElements/Form/genericForm.php | 4 + .../Form/metaTemplateScaffold.php | 37 +++-- .../Form/multiFieldScaffold.php | 49 ++++--- 9 files changed, 226 insertions(+), 76 deletions(-) create mode 100644 config/Migrations/20211104072514_MoreMetaFieldColumns.php diff --git a/config/Migrations/20211104072514_MoreMetaFieldColumns.php b/config/Migrations/20211104072514_MoreMetaFieldColumns.php new file mode 100644 index 0000000..400da1a --- /dev/null +++ b/config/Migrations/20211104072514_MoreMetaFieldColumns.php @@ -0,0 +1,40 @@ +table('meta_fields'); + + $metaFieldsTable + ->addColumn('created', 'datetime', [ + 'default' => null, + 'null' => false, + ]) + ->addColumn('modified', 'datetime', [ + 'default' => null, + 'null' => false, + ]) + ->update(); + + $metaFieldsTable + ->addIndex('created') + ->addIndex('modified'); + + $metaTemplateFieldsTable = $this->table('meta_template_fields') + ->addColumn('counter', 'integer', [ + 'default' => 0, + 'length' => 11, + 'null' => false, + 'signed' => false, + 'comment' => 'Field used by the CounterCache behaviour to count the occurence of meta_template_fields' + ]) + ->update(); + + // TODO: Make sure FK constraints are set between meta_field, meta_template and meta_template_fields + } +} \ No newline at end of file diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index 99c7d9d..d5024ee 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -6,6 +6,7 @@ use Cake\Controller\Component; use Cake\Error\Debugger; use Cake\Utility\Hash; use Cake\Utility\Inflector; +use Cake\Utility\Text; use Cake\View\ViewBuilder; use Cake\ORM\TableRegistry; use Cake\Http\Exception\MethodNotAllowedException; @@ -125,7 +126,7 @@ class CRUDComponent extends Component ->contain('MetaTemplateFields') ->formatResults(function (\Cake\Collection\CollectionInterface $metaTemplates) { // Set meta-template && meta-template-fields indexed by their ID return $metaTemplates - ->map(function($metaTemplate) { + ->map(function ($metaTemplate) { $metaTemplate->meta_template_fields = Hash::combine($metaTemplate->meta_template_fields, '{n}.id', '{n}'); return $metaTemplate; }) @@ -139,8 +140,9 @@ class CRUDComponent extends Component public function add(array $params = []): void { - $this->getMetaTemplates(); + $metaTemplates = $this->getMetaTemplates(); $data = $this->Table->newEmptyEntity(); + // $data = $this->attachMetaTemplates($data, $metaTemplates->toArray()); if (!empty($params['fields'])) { $this->Controller->set('fields', $params['fields']); } @@ -243,6 +245,66 @@ class CRUDComponent extends Component $this->Table->saveMetaFields($id, $input, $this->Table); } + // prune empty values and marshall fields + private function massageMetaFields($entity, $input, $allMetaTemplates=[]) + { + if (empty($input['MetaTemplates'])) { + return $entity; + } + + $metaFieldsTable = TableRegistry::getTableLocator()->get('MetaFields'); + $metaFieldsIndex = []; + if (empty($metaTemplates)) { + $allMetaTemplates = $this->getMetaTemplates()->toArray(); + } + foreach ($entity->meta_fields as $i => $metaField) { + $metaFieldsIndex[$metaField->id] = $i; + } + + foreach ($input['MetaTemplates'] as $template_id => $template) { + foreach ($template['meta_template_fields'] as $meta_template_field_id => $meta_template_field) { + $rawMetaTemplateField = $allMetaTemplates[$template_id]['meta_template_fields'][$meta_template_field_id]; + foreach ($meta_template_field['metaFields'] as $meta_field_id => $meta_field) { + if ($meta_field_id == 'new') { // create new meta_field + $new_meta_fields = $meta_field; + foreach ($new_meta_fields as $new_value) { + if (!empty($new_value)) { + $metaField = $metaFieldsTable->newEmptyEntity(); + $metaFieldsTable->patchEntity($metaField, [ + 'value' => $new_value, + 'scope' => $this->Table->metaFields, // get scope from behavior + 'field' => $rawMetaTemplateField->field, + 'meta_template_id' => $rawMetaTemplateField->meta_template_id, + 'meta_template_field_id' => $rawMetaTemplateField->id, + 'parent_id' => $entity->id, + 'uuid' => Text::uuid(), + ]); + $entity->meta_fields[] = $metaField; + } + } + } else { + $new_value = $meta_field['value']; + $index = $metaFieldsIndex[$meta_field_id]; + if (!empty($new_value)) { + // update meta_field + $metaFieldsTable->patchEntity($entity->meta_fields[$index], [ + 'value' => $new_value, + ]); + } else { + // remove meta field - Just checking if not having it will have it deleted or if it should actually be deleted + // Maybe Table->unlink ? + unset($entity->meta_fields[$index]); + } + } + } + } + } + + // $input['metaFields'] = $metaFields; + $entity->setDirty('meta_fields', true); + return $entity; + } + private function __massageInput($params) { $input = $this->request->getData(); @@ -271,7 +333,18 @@ class CRUDComponent extends Component $params['contain'][] = 'Tags'; $this->setAllTags(); } - $data = $this->Table->get($id, isset($params['get']) ? $params['get'] : $params); + $getParam = isset($params['get']) ? $params['get'] : $params; + if (true) { // TODO: check if has meta field behavior + if (empty($getParam['contain'])) { + $getParam['contain'] = []; + } + if (is_array($getParam['contain'])) { + $getParam['contain'][] = 'MetaFields'; + } else { + $getParam['contain'] = [$getParam['contain'], 'MetaFields']; + } + } + $data = $this->Table->get($id, $getParam); $data = $this->attachMetaTemplates($data, $metaTemplates->toArray()); // $data = $this->getMetaFields($id, $data); if (!empty($params['fields'])) { @@ -282,24 +355,30 @@ class CRUDComponent extends Component 'associated' => [] ]; $input = $this->__massageInput($params); - dd($input); if (!empty($params['fields'])) { $patchEntityParams['fields'] = $params['fields']; } $data = $this->Table->patchEntity($data, $input, $patchEntityParams); + $data = $this->massageMetaFields($data, $input, $metaTemplates); if (isset($params['beforeSave'])) { $data = $params['beforeSave']($data); } + /* + - Update meta_field table: + - Add created and modified + - Update meta_template_field_table: + - Add counter column + */ $savedData = $this->Table->save($data); if ($savedData !== false) { if (isset($params['afterSave'])) { $params['afterSave']($data); } $message = __('{0} `{1}` updated.', $this->ObjectAlias, $savedData->{$this->Table->getDisplayField()}); - if (!empty($input['metaFields'])) { - $this->MetaFields->deleteAll(['scope' => $this->Table->metaFields, 'parent_id' => $savedData->id]); - $this->saveMetaFields($savedData->id, $input); - } + // if (!empty($input['metaFields'])) { + // $this->MetaFields->deleteAll(['scope' => $this->Table->metaFields, 'parent_id' => $savedData->id]); + // $this->saveMetaFields($savedData->id, $input); + // } if ($this->Controller->ParamHandler->isRest()) { $this->Controller->restResponsePayload = $this->RestResponse->viewData($savedData, 'json'); } else if ($this->Controller->ParamHandler->isAjax()) { @@ -390,7 +469,7 @@ class CRUDComponent extends Component $query->where(['MetaFields.scope' => $this->Table->metaFields, 'MetaFields.parent_id' => $id]); $metaFields = $query->all(); $data = []; - foreach($metaFields as $metaField) { + foreach ($metaFields as $metaField) { if (empty($data[$metaField->meta_template_id][$metaField->meta_template_field_id])) { $data[$metaField->meta_template_id][$metaField->meta_template_field_id] = []; } @@ -439,10 +518,10 @@ class CRUDComponent extends Component $this->Controller->set('entity', $data); } - public function delete($id=false): void + public function delete($id = false): void { if ($this->request->is('get')) { - if(!empty($id)) { + if (!empty($id)) { $data = $this->Table->get($id); $this->Controller->set('id', $data['id']); $this->Controller->set('data', $data); @@ -468,7 +547,8 @@ class CRUDComponent extends Component __('{0} deleted.', $this->ObjectAlias), __('All {0} have been deleted.', Inflector::pluralize($this->ObjectAlias)), __('Could not delete {0}.', $this->ObjectAlias), - __('{0} / {1} {2} have been deleted.', + __( + '{0} / {1} {2} have been deleted.', $bulkSuccesses, count($ids), Inflector::pluralize($this->ObjectAlias) @@ -482,14 +562,14 @@ class CRUDComponent extends Component $this->Controller->render('/genericTemplates/delete'); } - public function tag($id=false): void + public function tag($id = false): void { if (!$this->taggingSupported()) { throw new Exception("Table {$this->TableAlias} does not support tagging"); } if ($this->request->is('get')) { $this->setAllTags(); - if(!empty($id)) { + if (!empty($id)) { $params = [ 'contain' => 'Tags', ]; @@ -529,7 +609,8 @@ class CRUDComponent extends Component __('{0} tagged with `{1}`.', $this->ObjectAlias, $input['tag_list']), __('All {0} have been tagged.', Inflector::pluralize($this->ObjectAlias)), __('Could not tag {0} with `{1}`.', $this->ObjectAlias, $input['tag_list']), - __('{0} / {1} {2} have been tagged.', + __( + '{0} / {1} {2} have been tagged.', $bulkSuccesses, count($ids), Inflector::pluralize($this->ObjectAlias) @@ -541,14 +622,14 @@ class CRUDComponent extends Component $this->Controller->render('/genericTemplates/tagForm'); } - public function untag($id=false): void + public function untag($id = false): void { if (!$this->taggingSupported()) { throw new Exception("Table {$this->TableAlias} does not support tagging"); } if ($this->request->is('get')) { $this->setAllTags(); - if(!empty($id)) { + if (!empty($id)) { $params = [ 'contain' => 'Tags', ]; @@ -590,7 +671,8 @@ class CRUDComponent extends Component __('{0} untagged with `{1}`.', $this->ObjectAlias, implode(', ', $tagsToRemove)), __('All {0} have been untagged.', Inflector::pluralize($this->ObjectAlias)), __('Could not untag {0} with `{1}`.', $this->ObjectAlias, $input['tag_list']), - __('{0} / {1} {2} have been untagged.', + __( + '{0} / {1} {2} have been untagged.', $bulkSuccesses, count($ids), Inflector::pluralize($this->ObjectAlias) @@ -625,7 +707,7 @@ class CRUDComponent extends Component $this->Controller->render('/genericTemplates/tag'); } - public function setResponseForController($action, $success, $message, $data=[], $errors=null) + public function setResponseForController($action, $success, $message, $data = [], $errors = null) { if ($success) { if ($this->Controller->ParamHandler->isRest()) { @@ -665,7 +747,7 @@ class CRUDComponent extends Component * @return Array The ID converted to a list or the list of provided IDs from the request * @throws NotFoundException when no ID could be found */ - public function getIdsOrFail($id=false): Array + public function getIdsOrFail($id = false): array { $params = $this->Controller->ParamHandler->harvestParams(['ids']); if (!empty($params['ids'])) { @@ -826,7 +908,7 @@ class CRUDComponent extends Component $query = $this->setRelatedCondition($query, $modelName, $fieldName, $filterValue); } else { $filterParts = array_slice($filterParts, 1); - $query = $query->matching($modelName, function(\Cake\ORM\Query $q) use ($filterParts, $filterValue) { + $query = $query->matching($modelName, function (\Cake\ORM\Query $q) use ($filterParts, $filterValue) { return $this->setNestedRelatedCondition($q, $filterParts, $filterValue); }); } @@ -835,7 +917,7 @@ class CRUDComponent extends Component protected function setRelatedCondition($query, $modelName, $fieldName, $filterValue) { - return $query->matching($modelName, function(\Cake\ORM\Query $q) use ($fieldName, $filterValue) { + return $query->matching($modelName, function (\Cake\ORM\Query $q) use ($fieldName, $filterValue) { return $this->setValueCondition($q, $fieldName, $filterValue); }); } @@ -959,7 +1041,8 @@ class CRUDComponent extends Component } $savedData = $this->Table->save($data); if ($savedData !== false) { - $message = __('{0} field {1}. (ID: {2} {3})', + $message = __( + '{0} field {1}. (ID: {2} {3})', $fieldName, $data->{$fieldName} ? __('enabled') : __('disabled'), Inflector::humanize($this->ObjectAlias), @@ -1041,9 +1124,9 @@ class CRUDComponent extends Component [$this->Table->getAlias() => $this->Table->getTable()], [sprintf('%s.id = %s.%s', $this->Table->getAlias(), $associatedTable->getAlias(), $association->getForeignKey())] ) - ->where([ - ["${field} IS NOT" => NULL] - ]); + ->where([ + ["${field} IS NOT" => NULL] + ]); } else if ($associationType == 'manyToOne') { $fieldToExtract = sprintf('%s.%s', Inflector::singularize(strtolower($model)), $subField); $query = $this->Table->find()->contain($model); diff --git a/src/Model/Behavior/MetaFieldsBehavior.php b/src/Model/Behavior/MetaFieldsBehavior.php index c23247a..9e5ec1e 100644 --- a/src/Model/Behavior/MetaFieldsBehavior.php +++ b/src/Model/Behavior/MetaFieldsBehavior.php @@ -45,7 +45,6 @@ class MetaFieldsBehavior extends Behavior public function initialize(array $config): void { $this->bindAssociations(); - $this->attachCounters(); $this->_metaTemplateFieldTable = $this->_table; $this->_metaTemplateTable = $this->_table; } @@ -81,19 +80,6 @@ class MetaFieldsBehavior extends Behavior } } - public function attachCounters() - { - $config = $this->getConfig(); - $metaFieldsTable = $this->_table->MetaFields; - $tableAlias = $this->_table->getAlias(); - - if (!$metaFieldsTable->hasBehavior('CounterCache')) { - $metaFieldsTable->addBehavior('CounterCache', [ - $tableAlias => $config['metaTemplateFieldCounter'] - ]); - } - } - public function beforeMarshal($event, $data, $options) { $property = $this->getConfig('metaFieldsAssoc.propertyName'); diff --git a/src/Model/Table/MetaFieldsTable.php b/src/Model/Table/MetaFieldsTable.php index 40c6509..e26e5f9 100644 --- a/src/Model/Table/MetaFieldsTable.php +++ b/src/Model/Table/MetaFieldsTable.php @@ -12,15 +12,15 @@ class MetaFieldsTable extends AppTable { parent::initialize($config); $this->addBehavior('UUID'); - $this->setDisplayField('field'); + $this->addBehavior('Timestamp'); + $this->addBehavior('CounterCache', [ + 'MetaTemplateFields' => ['counter'] + ]); + $this->belongsTo('MetaTemplates'); $this->belongsTo('MetaTemplateFields'); - // $this->belongsTo('Individuals') - // ->setForeignKey('parent_id') - // ->setBindingKey('id') - // ->setConditions([ - // 'scope' => 'individual' - // ]); + + $this->setDisplayField('field'); } public function validationDefault(Validator $validator): Validator diff --git a/src/Model/Table/MetaTemplateFieldsTable.php b/src/Model/Table/MetaTemplateFieldsTable.php index 453690a..9357b2d 100644 --- a/src/Model/Table/MetaTemplateFieldsTable.php +++ b/src/Model/Table/MetaTemplateFieldsTable.php @@ -11,10 +11,12 @@ class MetaTemplateFieldsTable extends AppTable public function initialize(array $config): void { parent::initialize($config); + $this->BelongsTo( 'MetaTemplates' ); $this->hasMany('MetaFields'); + $this->setDisplayField('field'); } diff --git a/templates/MetaTemplateFields/index.php b/templates/MetaTemplateFields/index.php index 6cadf15..5620603 100644 --- a/templates/MetaTemplateFields/index.php +++ b/templates/MetaTemplateFields/index.php @@ -34,7 +34,12 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'name' => __('Validation regex'), 'sort' => 'regex', 'data_path' => 'regex' - ] + ], + [ + 'name' => __('Field Usage'), + 'sort' => 'counter', + 'data_path' => 'counter', + ], ], 'title' => __('Meta Template Fields'), 'description' => __('The various fields that the given template contans. When a meta template is enabled, the fields are automatically appended to the appropriate object.'), diff --git a/templates/element/genericElements/Form/genericForm.php b/templates/element/genericElements/Form/genericForm.php index 2c12909..5614982 100644 --- a/templates/element/genericElements/Form/genericForm.php +++ b/templates/element/genericElements/Form/genericForm.php @@ -77,6 +77,7 @@ ); } } + $metaTemplateString = ''; if (!empty($data['metaTemplates']) && $data['metaTemplates']->count() > 0) { $metaTemplateString = $this->element( 'genericElements/Form/metaTemplateScaffold', @@ -119,6 +120,9 @@ ]); } else if (!empty($raw)) { echo $this->element('genericElements/Form/formLayouts/formDefault', [ + 'actionName' => $actionName, + 'modelName' => $modelName, + 'submitButtonData' => $submitButtonData, 'formCreate' => $formCreate, 'ajaxFlashMessage' => $ajaxFlashMessage, 'fieldsString' => $fieldsString, diff --git a/templates/element/genericElements/Form/metaTemplateScaffold.php b/templates/element/genericElements/Form/metaTemplateScaffold.php index 2dd6629..8b27bdf 100644 --- a/templates/element/genericElements/Form/metaTemplateScaffold.php +++ b/templates/element/genericElements/Form/metaTemplateScaffold.php @@ -50,18 +50,31 @@ foreach ($metaTemplatesData as $i => $metaTemplate) { ); } } else { - $this->Form->setTemplates($backupTemplates); - $fieldData = [ - 'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new.0', $metaTemplateField->meta_template_id, $metaTemplateField->id), - 'label' => $metaTemplateField->field, - ]; - $fieldsHtml .= $this->element( - 'genericElements/Form/fieldScaffold', - [ - 'fieldData' => $fieldData, - 'form' => $this->Form - ] - ); + if (!empty($metaTemplateField->multiple)) { + $fieldsHtml .= $this->element( + 'genericElements/Form/multiFieldScaffold', + [ + 'metaFieldsEntities' => [], + 'metaTemplateField' => $metaTemplateField, + 'multiple' => !empty($metaTemplateField->multiple), + 'form' => $this->Form, + ] + ); + $this->Form->setTemplates($backupTemplates); + } else { + $this->Form->setTemplates($backupTemplates); + $fieldData = [ + 'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new.0', $metaTemplateField->meta_template_id, $metaTemplateField->id), + 'label' => $metaTemplateField->field, + ]; + $fieldsHtml .= $this->element( + 'genericElements/Form/fieldScaffold', + [ + 'fieldData' => $fieldData, + 'form' => $this->Form + ] + ); + } } } $tabData['content'][$i] = $fieldsHtml; diff --git a/templates/element/genericElements/Form/multiFieldScaffold.php b/templates/element/genericElements/Form/multiFieldScaffold.php index 123fea1..6af9bcf 100644 --- a/templates/element/genericElements/Form/multiFieldScaffold.php +++ b/templates/element/genericElements/Form/multiFieldScaffold.php @@ -8,30 +8,46 @@ $form->setTemplates($default_template); $fieldsHtml = ''; $labelPrintedOnce = false; -foreach ($metaFieldsEntities as $i => $metaFieldsEntity) { - $fieldData = [ - 'label' => $metaFieldsEntity->field, - 'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.%s.value', $metaFieldsEntity->meta_template_id, $metaFieldsEntity->meta_template_field_id, $metaFieldsEntity->id), - ]; - if ($labelPrintedOnce) { // Only the first input can have a label - $fieldData['label'] = false; +if (!empty($metaFieldsEntities)) { + foreach ($metaFieldsEntities as $i => $metaFieldsEntity) { + $fieldData = [ + 'label' => $metaFieldsEntity->field, + 'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.%s.value', $metaFieldsEntity->meta_template_id, $metaFieldsEntity->meta_template_field_id, $metaFieldsEntity->id), + ]; + if ($labelPrintedOnce) { // Only the first input can have a label + $fieldData['label'] = false; + } + $labelPrintedOnce = true; + $fieldsHtml .= $this->element( + 'genericElements/Form/fieldScaffold', + [ + 'fieldData' => $fieldData, + 'form' => $form + ] + ); } - $labelPrintedOnce = true; - $fieldsHtml .= $this->element( - 'genericElements/Form/fieldScaffold', - [ - 'fieldData' => $fieldData, - 'form' => $form - ] - ); } if (!empty($metaTemplateField) && !empty($multiple)) { // Add multiple field button + $emptyMetaFieldInput = ''; + if (empty($metaFieldsEntities)) { + $emptyMetaFieldInput = $this->element( + 'genericElements/Form/fieldScaffold', + [ + 'fieldData' => [ + 'label' => $metaTemplateField->field, + 'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new.0', $metaTemplateField->meta_template_id, $metaTemplateField->id), + 'class' => 'new-metafield', + ], + 'form' => $form, + ] + ); + } $emptyInputForSecurityComponent = $this->element( 'genericElements/Form/fieldScaffold', [ 'fieldData' => [ 'label' => false, - 'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new[]', $metaFieldsEntity->meta_template_id, $metaFieldsEntity->meta_template_field_id), + 'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new[]', $metaTemplateField->meta_template_id, $metaTemplateField->id), ], 'form' => $form, ] @@ -45,6 +61,7 @@ if (!empty($metaTemplateField) && !empty($multiple)) { // Add multiple field but ] ) ); + $fieldsHtml .= $emptyMetaFieldInput; $fieldsHtml .= sprintf('
%s
', $emptyInputForSecurityComponent); $fieldsHtml .= $multiFieldButtonHtml; }