chg: [metaTemplate] Continuation of refactoring - WiP

Editing meta field from entities working
pull/93/head
Sami Mokaddem 2021-11-04 08:10:32 +01:00
parent 16581a13fb
commit c55088aa85
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
9 changed files with 226 additions and 76 deletions

View File

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
use Migrations\AbstractMigration;
use Phinx\Db\Adapter\MysqlAdapter;
class MoreMetaFieldColumns extends AbstractMigration
{
public function change()
{
$metaFieldsTable = $this->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
}
}

View File

@ -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);

View File

@ -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');

View File

@ -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

View File

@ -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');
}

View File

@ -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.'),

View File

@ -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,

View File

@ -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;

View File

@ -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('<div class="d-none template-container">%s</div>', $emptyInputForSecurityComponent);
$fieldsHtml .= $multiFieldButtonHtml;
}