chg: [component:CRUD] Support of validation and re-edition (WiP)

pull/93/head
Sami Mokaddem 2021-11-08 14:08:47 +01:00
parent b11db037d7
commit 94fbd74918
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
8 changed files with 206 additions and 39 deletions

View File

@ -134,7 +134,7 @@ class CRUDComponent extends Component
});
$metaTemplates = $metaQuery->all();
}
$this->Controller->set('metaTemplates', $metaTemplates);
// $this->Controller->set('metaTemplates', $metaTemplates->toArray());
return $metaTemplates;
}
@ -232,7 +232,11 @@ class CRUDComponent extends Component
foreach ($data->getErrors() as $field => $errorData) {
$errorMessages = [];
foreach ($errorData as $key => $value) {
$errorMessages[] = $value;
if (is_array($value)) {
$errorMessages[] = implode('& ', Hash::extract($value, "{s}.{s}"));
} else {
$errorMessages[] = $value;
}
}
$validationMessage .= __('{0}: {1}', $field, implode(',', $errorMessages));
}
@ -281,28 +285,50 @@ class CRUDComponent extends Component
'uuid' => Text::uuid(),
]);
$entity->meta_fields[] = $metaField;
$entity->MetaTemplates[$template_id]->meta_template_fields[$meta_template_field_id]->metaFields[] = $metaField;
// $entity->MetaTemplates[$template_id]->meta_template_fields[$meta_template_field_id]->metaFields['new'][] = $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,
]);
// update meta_field and attach validation errors
if (!empty($metaFieldsIndex[$meta_field_id])) {
$index = $metaFieldsIndex[$meta_field_id];
$metaFieldsTable->patchEntity($entity->meta_fields[$index], [
'value' => $new_value, 'meta_template_field_id' => $rawMetaTemplateField->id
], ['value']);
$metaFieldsTable->patchEntity(
$entity->MetaTemplates[$template_id]->meta_template_fields[$meta_template_field_id]->metaFields[$meta_field_id],
['value' => $new_value, 'meta_template_field_id' => $rawMetaTemplateField->id],
['value']
);
} else { // metafield comes from a second post where the temporary entity has already been created
$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;
$entity->MetaTemplates[$template_id]->meta_template_fields[$meta_template_field_id]->metaFields[] = $metaField;
}
} else {
// remove meta field - Just checking if not having it will have it deleted or if it should actually be deleted
// Maybe Table->unlink ?
// Metafield value is empty, indicating the field should be removed
$index = $metaFieldsIndex[$meta_field_id];
$metaFieldsToDelete[] = $entity->meta_fields[$index];
unset($entity->meta_fields[$index]);
unset($entity->MetaTemplates[$template_id]->meta_template_fields[$meta_template_field_id]->metaFields[$meta_field_id]);
}
}
}
}
}
// $input['metaFields'] = $metaFields;
$entity->setDirty('meta_fields', true);
return ['entity' => $entity, 'metafields_to_delete' => $metaFieldsToDelete];
}
@ -348,7 +374,6 @@ class CRUDComponent extends Component
}
$data = $this->Table->get($id, $getParam);
$data = $this->attachMetaTemplates($data, $metaTemplates->toArray());
// $data = $this->getMetaFields($id, $data);
if (!empty($params['fields'])) {
$this->Controller->set('fields', $params['fields']);
}
@ -360,10 +385,11 @@ class CRUDComponent extends Component
if (!empty($params['fields'])) {
$patchEntityParams['fields'] = $params['fields'];
}
$massagedData = $this->massageMetaFields($data, $input, $metaTemplates);
unset($input['MetaTemplates']); // Avoid MetaTemplates to be overriden when patching entity
$data = $massagedData['entity'];
$metaFieldsToDelete = $massagedData['metafields_to_delete'];
$data = $this->Table->patchEntity($data, $input, $patchEntityParams);
$massaged = $this->massageMetaFields($data, $input, $metaTemplates);
$data = $massaged['entity'];
$metaFieldsToDelete = $massaged['metafields_to_delete'];
if (isset($params['beforeSave'])) {
$data = $params['beforeSave']($data);
}
@ -389,8 +415,10 @@ class CRUDComponent extends Component
}
}
} else {
// debug($data['MetaTemplates']);
$validationErrors = $data->getErrors();
$validationMessage = $this->prepareValidationMessage($validationErrors);
// $validationMessage = $this->prepareValidationMessage($validationErrors);
$validationMessage = $this->prepareValidationError($data);
$message = __(
'{0} could not be modified.{1}',
$this->ObjectAlias,

View File

@ -5,6 +5,7 @@ namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\ORM\RulesChecker;
class MetaFieldsTable extends AppTable
{
@ -34,7 +35,69 @@ class MetaFieldsTable extends AppTable
->notEmptyString('meta_template_field_id')
->requirePresence(['scope', 'field', 'value', 'uuid', 'meta_template_id', 'meta_template_field_id'], 'create');
// add validation regex
$validator->add('value', 'validMetaField', [
'rule' => 'isValidMetaField',
'message' => __('The provided value doesn\'t satisfy the validation defined by the meta-fields\'s meta-template'),
'provider' => 'table',
]);
return $validator;
}
// public function buildRules(RulesChecker $rules): RulesChecker
// {
// $rules->add([$this, 'isValidMetaField'], 'validMetaField', [
// // 'errorField' => 'meta_field.value',
// 'errorField' => 'value',
// 'message' => __('The provided value doesn\'t satisfy the validation defined by the meta-fields\'s meta-template'),
// ]);
// return $rules;
// }
public function isValidMetaField($value, array $context)
{
$metaFieldsTable = $context['providers']['table'];
$entityData = $context['data'];
$metaTemplateField = $metaFieldsTable->MetaTemplateFields->get($entityData['meta_template_field_id']);
$typeValid = $this->isValidType($value, $metaTemplateField['type']);
if ($typeValid !== true) {
return $typeValid;
}
if (!empty($metaTemplateField['regex'])) {
return $this->isValidRegex($value, $metaTemplateField);
}
return true;
}
public function isValidMetaFieldOLD($entity, $options)
{
debug($entity['value']);
$metaFieldsTable = $options['repository'];
$metaTemplateField = $metaFieldsTable->MetaTemplateFields->get($entity['meta_template_field_id']);
$typeValid = $this->isValidType($entity['value'], $metaTemplateField['type']);
if ($typeValid !== true) {
return $typeValid;
}
$metaTemplateField['regex'] = '/123/';
if (!empty($metaTemplateField['regex'])) {
return $this->isValidRegex($entity['value'], $metaTemplateField);
}
return true;
}
public function isValidType($value, string $type)
{
if (empty($value)) {
return __('Metafield value cannot be empty.');
}
return true;
}
public function isValidRegex($value, $metaTemplateField)
{
if (!preg_match($metaTemplateField['regex'], $value)) {
return __('Metafield value `{0}` for `{1}` doesn\'t pass regex validation', $value, $metaTemplateField->field);
}
return true;
}
}

View File

@ -26,7 +26,7 @@
'type' => 'tags'
),
),
'metaTemplates' => empty($metaTemplates) ? [] : $metaTemplates,
// 'metaTemplates' => empty($metaTemplates) ? [] : $metaTemplates,
'submit' => array(
'action' => $this->request->getParam('action')
)

View File

@ -41,6 +41,8 @@
$params[$k] = $fd;
}
}
// debug($fieldData);
// debug($params);
$temp = $this->element('genericElements/Form/Fields/' . $fieldTemplate, array(
'fieldData' => $fieldData,
'params' => $params

View File

@ -12,6 +12,7 @@
- use these to define dynamic form fields, or anything that will feed into the regular fields via JS population
* - submit: The submit button itself. By default it will simply submit to the form as defined via the 'model' field
*/
// debug(\Cake\Utility\Hash::extract($entity, 'MetaTemplates.{n}.meta_template_fields.{n}.metaFields.{n}.value'));
$this->Form->setConfig('errorClass', 'is-invalid');
$modelForForm = empty($data['model']) ?
h(\Cake\Utility\Inflector::singularize(\Cake\Utility\Inflector::classify($this->request->getParam('controller')))) :
@ -21,7 +22,6 @@
$simpleFieldWhitelist = [
'default', 'type', 'placeholder', 'label', 'empty', 'rows', 'div', 'required', 'templates'
];
//$fieldsArrayForPersistence = array();
if (empty($data['url'])) {
$data['url'] = ["controller" => $this->request->getParam('controller'), "action" => $this->request->getParam('url')];
}
@ -78,11 +78,10 @@
}
}
$metaTemplateString = '';
if (!empty($data['metaTemplates']) && $data['metaTemplates']->count() > 0) {
if (!empty($entity['MetaTemplates']) && count($entity['MetaTemplates']) > 0) {
$metaTemplateString = $this->element(
'genericElements/Form/metaTemplateScaffold',
[
'metaTemplatesData' => $data['metaTemplates'],
'form' => $this->Form,
]
);

View File

@ -10,7 +10,7 @@ $default_template = [
$this->Form->setTemplates($default_template);
$backupTemplates = $this->Form->getTemplates();
$tabData = [];
foreach ($metaTemplatesData as $i => $metaTemplate) {
foreach ($entity->MetaTemplates as $i => $metaTemplate) {
if ($metaTemplate->is_default) {
$tabData['navs'][$i] = [
'html' => $this->element('/genericElements/MetaTemplates/metaTemplateNav', ['metaTemplate' => $metaTemplate])
@ -21,6 +21,7 @@ foreach ($metaTemplatesData as $i => $metaTemplate) {
];
}
$fieldsHtml = '';
// debug($metaTemplate['meta_template_fields']);
foreach ($metaTemplate->meta_template_fields as $metaTemplateField) {
$metaTemplateField->label = Inflector::humanize($metaTemplateField->field);
if (!empty($metaTemplateField->metaFields)) {

View File

@ -39,10 +39,14 @@ $seed = 'mfb-' . mt_rand();
function addNewField() {
const $clicked = $(this);
const $lastInputContainer = $clicked.closest('.multi-metafields-container').children().not('.template-container').find('input').last().closest('.multi-metafield-container')
const $templateContainer = $clicked.closest('.multi-metafields-container').find('.template-container').children()
const $clonedContainer = $templateContainer.clone()
$clonedContainer.removeClass('d-none', 'template-container')
let $lastInputContainer = $clicked.closest('.multi-metafields-container').children().not('.template-container').find('input').last().closest('.multi-metafield-container')
if ($lastInputContainer.length == 0) {
$lastInputContainer = $clicked.closest('.multi-metafields-container').find('input').last().closest('.multi-metafield-container')
}
const $clonedContainer = $lastInputContainer.clone()
$clonedContainer
.removeClass('has-error')
.find('.error-message ').remove()
const $clonedInput = $clonedContainer.find('input, select')
if ($clonedInput.length > 0) {
const injectedTemplateId = $clicked.closest('.multi-metafields-container').find('.new-metafield').length
@ -63,6 +67,34 @@ $seed = 'mfb-' . mt_rand();
.attr('field', dottedPathStr)
.attr('name', brackettedPathStr)
.val('')
.removeClass('is-invalid')
}
// function addNewField() {
// const $clicked = $(this);
// const $lastInputContainer = $clicked.closest('.multi-metafields-container').children().not('.template-container').find('input').last().closest('.multi-metafield-container')
// const $templateContainer = $clicked.closest('.multi-metafields-container').find('.template-container').children()
// const $clonedContainer = $templateContainer.clone()
// $clonedContainer.removeClass('d-none', 'template-container')
// const $clonedInput = $clonedContainer.find('input, select')
// if ($clonedInput.length > 0) {
// const injectedTemplateId = $clicked.closest('.multi-metafields-container').find('.new-metafield').length
// $clonedInput.addClass('new-metafield')
// adjustClonedInputAttr($clonedInput, injectedTemplateId)
// $clonedContainer.insertAfter($lastInputContainer)
// }
// }
// function adjustClonedInputAttr($input, injectedTemplateId) {
// let explodedPath = $input.attr('field').split('.').splice(0, 5)
// explodedPath.push('new', injectedTemplateId)
// dottedPathStr = explodedPath.join('.')
// brackettedPathStr = explodedPath.map((elem, i) => {
// return i == 0 ? elem : `[${elem}]`
// }).join('')
// $input.attr('id', dottedPathStr)
// .attr('field', dottedPathStr)
// .attr('name', brackettedPathStr)
// .val('')
// }
})()
</script>

View File

@ -4,20 +4,48 @@ use Cake\Utility\Inflector;
$default_template = [
'inputContainer' => '<div class="row mb-1 multi-metafield-container">{{content}}</div>',
'inputContainerError' => '<div class="row mb-1 metafield-container has-error">{{content}}</div>',
'inputContainerError' => '<div class="row mb-1 multi-metafield-container has-error">{{content}}</div>',
'formGroup' => '<label class="col-sm-2 col-form-label form-label" {{attrs}}>{{label}}</label><div class="col-sm-10 multi-metafield-input-container">{{input}}{{error}}</div>',
];
$form->setTemplates($default_template);
$fieldsHtml = '';
$labelPrintedOnce = false;
$newMetaFields = [];
$newFieldIndex = 0;
// $newMetaFieldPrinted = false;
// debug($entity);
// debug($entity->getErrors());
if (!empty($metaFieldsEntities)) {
foreach ($metaFieldsEntities as $i => $metaFieldsEntity) {
$metaFieldsEntity->label = Inflector::humanize($metaFieldsEntity->field);
$fieldData = [
'label' => $metaFieldsEntity->label,
'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.%s.value', $metaFieldsEntity->meta_template_id, $metaFieldsEntity->meta_template_field_id, $metaFieldsEntity->id),
'field' => sprintf(
'MetaTemplates.%s.meta_template_fields.%s.metaFields.%s.value',
$metaFieldsEntity->meta_template_id,
$metaFieldsEntity->meta_template_field_id,
$metaFieldsEntity->id
),
];
if($metaFieldsEntity->isNew()) {
$fieldData['field'] = sprintf(
'MetaTemplates.%s.meta_template_fields.%s.metaFields.%s.value',
$metaFieldsEntity->meta_template_id,
$metaFieldsEntity->meta_template_field_id,
$i
);
$fieldData['class'] = 'new-metafield';
// $fieldData['field'] = sprintf(
// 'MetaTemplates.%s.meta_template_fields.%s.metaFields.new.%s',
// $metaFieldsEntity->meta_template_id,
// $metaFieldsEntity->meta_template_field_id,
// $newFieldIndex
// );
// $fieldData['class'] = 'new-metafield';
// $newMetaFieldPrinted = true;
// $newFieldIndex += 1;
}
if ($labelPrintedOnce) { // Only the first input can have a label
$fieldData['label'] = false;
}
@ -35,17 +63,17 @@ if (!empty($metaTemplateField) && !empty($multiple)) { // Add multiple field but
$metaTemplateField->label = Inflector::humanize($metaTemplateField->field);
$emptyMetaFieldInput = '';
if (empty($metaFieldsEntities)) {
$emptyMetaFieldInput = $this->element(
'genericElements/Form/fieldScaffold',
[
'fieldData' => [
'label' => $metaTemplateField->label,
'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new.0', $metaTemplateField->meta_template_id, $metaTemplateField->id),
'class' => 'new-metafield',
],
'form' => $form,
]
);
// $emptyMetaFieldInput = $this->element(
// 'genericElements/Form/fieldScaffold',
// [
// 'fieldData' => [
// 'label' => $metaTemplateField->label,
// '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',
@ -53,10 +81,24 @@ if (!empty($metaTemplateField) && !empty($multiple)) { // Add multiple field but
'fieldData' => [
'label' => false,
'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new[]', $metaTemplateField->meta_template_id, $metaTemplateField->id),
'value' => '',
],
'form' => $form,
]
);
// $emptyInputForSecurityComponent = '';
// if (!$newMetaFieldPrinted) {
// $emptyInputForSecurityComponent = $this->element(
// 'genericElements/Form/fieldScaffold',
// [
// 'fieldData' => [
// 'label' => false,
// 'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new[]', $metaTemplateField->meta_template_id, $metaTemplateField->id),
// ],
// 'form' => $form,
// ]
// );
// }
$multiFieldButtonHtml = sprintf(
'<div class="row mb-1 multi-metafield-container add-input-container"><div class="col-sm-2 form-label"></div><div class="col-sm-10 multi-metafield-input-container">%s</div></div>',
$this->element(
@ -66,7 +108,7 @@ if (!empty($metaTemplateField) && !empty($multiple)) { // Add multiple field but
]
)
);
$fieldsHtml .= $emptyMetaFieldInput;
// $fieldsHtml .= $emptyMetaFieldInput;
$fieldsHtml .= sprintf('<div class="d-none template-container">%s</div>', $emptyInputForSecurityComponent);
$fieldsHtml .= $multiFieldButtonHtml;
}