Skip to content

Commit 0a49303

Browse files
authored
Unify Multiline expr calculation (#2046)
1 parent 62020d2 commit 0a49303

File tree

2 files changed

+42
-37
lines changed

2 files changed

+42
-37
lines changed

docs/multiline.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ Lets say a User can have many email addresses, but you want to store them in a s
103103

104104
Using a form with User model won't automatically add a Multiline to edit the related email addresses.
105105

106-
.. php:method:: setReferenceModel(string $refModelName, Model $modelEntity = null, array $fieldNames = []): Model
106+
.. php:method:: setReferenceModel(string $refModelName, Model $entity = null, array $fieldNames = []): Model
107107
108108
If you want to edit them along with the user, Multiline need to be set up accordingly using the setReferenceModel method::
109109

src/Form/Control/Multiline.php

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -426,17 +426,17 @@ public function setModel(Model $model, array $fieldNames = null): void
426426
* Otherwise, form will try to save 'multiline' field value as an array when form is save.
427427
* $multiline = $form->addControl('multiline', [Multiline::class], ['neverPersist' => true])
428428
*/
429-
public function setReferenceModel(string $refModelName, Model $modelEntity = null, array $fieldNames = []): void
429+
public function setReferenceModel(string $refModelName, Model $entity = null, array $fieldNames = []): void
430430
{
431-
if ($modelEntity === null) {
431+
if ($entity === null) {
432432
if (!$this->form->model->isEntity()) {
433433
throw new Exception('Model entity is not set');
434434
}
435435

436-
$modelEntity = $this->form->model;
436+
$entity = $this->form->model;
437437
}
438438

439-
$this->setModel($modelEntity->ref($refModelName), $fieldNames);
439+
$this->setModel($entity->ref($refModelName), $fieldNames);
440440
}
441441

442442
/**
@@ -683,8 +683,8 @@ private function outputJson(): void
683683
{
684684
switch ($_POST['__atkml_action'] ?? null) {
685685
case 'update-row':
686-
$model = $this->setDummyModelValue($this->model->createEntity());
687-
$expressionValues = array_merge($this->getExpressionValues($model), $this->getCallbackValues($model));
686+
$entity = $this->createDummyEntityFromPost($this->model);
687+
$expressionValues = array_merge($this->getExpressionValues($entity), $this->getCallbackValues($entity));
688688
$this->getApp()->terminateJson(['success' => true, 'expressions' => $expressionValues]);
689689
// no break - expression above always terminate
690690
case 'on-change':
@@ -697,17 +697,17 @@ private function outputJson(): void
697697
/**
698698
* Return values associated with callback field.
699699
*/
700-
private function getCallbackValues(Model $model): array
700+
private function getCallbackValues(Model $entity): array
701701
{
702702
$values = [];
703703
foreach ($this->fieldDefs as $def) {
704704
$fieldName = $def['name'];
705-
if ($fieldName === $model->idField) {
705+
if ($fieldName === $entity->idField) {
706706
continue;
707707
}
708-
$field = $model->getField($fieldName);
708+
$field = $entity->getField($fieldName);
709709
if ($field instanceof CallbackField) {
710-
$value = ($field->expr)($model);
710+
$value = ($field->expr)($entity);
711711
$values[$fieldName] = $this->getApp()->uiPersistence->typecastSaveField($field, $value);
712712
}
713713
}
@@ -719,30 +719,30 @@ private function getCallbackValues(Model $model): array
719719
* Looks inside the POST of the request and loads data into model.
720720
* Allow to Run expression base on post row value.
721721
*/
722-
private function setDummyModelValue(Model $model): Model
722+
private function createDummyEntityFromPost(Model $model): Model
723723
{
724-
$model = clone $model; // for clearing "required"
724+
$entity = (clone $model)->createEntity(); // clone for clearing "required"
725725

726726
foreach ($this->fieldDefs as $def) {
727727
$fieldName = $def['name'];
728-
if ($fieldName === $model->idField) {
728+
if ($fieldName === $entity->idField) {
729729
continue;
730730
}
731731

732-
$field = $model->getField($fieldName);
732+
$field = $entity->getField($fieldName);
733733

734734
$value = $this->getApp()->uiPersistence->typecastLoadField($field, $_POST[$fieldName] ?? null);
735735
if ($field->isEditable()) {
736736
try {
737737
$field->required = false;
738-
$model->set($fieldName, $value);
738+
$entity->set($fieldName, $value);
739739
} catch (ValidationException $e) {
740740
// bypass validation at this point
741741
}
742742
}
743743
}
744744

745-
return $model;
745+
return $entity;
746746
}
747747

748748
/**
@@ -767,22 +767,21 @@ private function getExpressionFields(Model $model): array
767767
/**
768768
* Return values associated to field expression.
769769
*/
770-
private function getExpressionValues(Model $model): array
770+
private function getExpressionValues(Model $entity): array
771771
{
772-
$dummyFields = $this->getExpressionFields($model);
772+
$dummyFields = $this->getExpressionFields($entity);
773773
foreach ($dummyFields as $k => $field) {
774-
if (!$field->expr instanceof \Closure) {
775-
$dummyFields[$k]->expr = $this->getDummyExpression($field, $model);
776-
}
774+
$dummyFields[$k] = clone $field;
775+
$dummyFields[$k]->expr = $this->getDummyExpression($field, $entity);
777776
}
778777

779778
if ($dummyFields === []) {
780779
return [];
781780
}
782781

783-
$dummyModel = new Model($model->getModel()->getPersistence(), ['table' => $model->table]);
782+
$dummyModel = new Model($entity->getModel()->getPersistence(), ['table' => $entity->table]);
784783
$dummyModel->removeField('id');
785-
$dummyModel->idField = $model->idField;
784+
$dummyModel->idField = $entity->idField;
786785

787786
$createExprFromValueFx = function ($v) use ($dummyModel): Persistence\Sql\Expression {
788787
if (is_int($v)) {
@@ -794,25 +793,25 @@ private function getExpressionValues(Model $model): array
794793
return $dummyModel->expr('[]', [$v]);
795794
};
796795

797-
foreach ($model->getFields() as $field) {
796+
foreach ($entity->getFields() as $field) {
798797
$dummyModel->addExpression($field->shortName, [
799798
'expr' => isset($dummyFields[$field->shortName])
800799
? $dummyFields[$field->shortName]->expr
801800
: ($field->shortName === $dummyModel->idField
802801
? '-1'
803-
: $createExprFromValueFx($model->getModel()->getPersistence()->typecastSaveField($field, $field->get($model)))),
802+
: $createExprFromValueFx($entity->getModel()->getPersistence()->typecastSaveField($field, $field->get($entity)))),
804803
'type' => $field->type,
805804
'actual' => $field->actual,
806805
]);
807806
}
808807
$dummyModel->setLimit(1); // TODO must work with empty table, no table should be used
809808
$values = $dummyModel->loadOne()->get();
810-
unset($values[$model->idField]);
809+
unset($values[$entity->idField]);
811810

812811
$formatValues = [];
813812
foreach ($values as $f => $value) {
814813
if (isset($dummyFields[$f])) {
815-
$field = $model->getField($f);
814+
$field = $entity->getField($f);
816815
$formatValues[$f] = $this->getApp()->uiPersistence->typecastSaveField($field, $value);
817816
}
818817
}
@@ -825,22 +824,28 @@ private function getExpressionValues(Model $model): array
825824
* Ex: total field expression = [qty] * [price] will return 4 * 100
826825
* where qty and price current value are 4 and 100 respectively.
827826
*
828-
* @return mixed
827+
* @return string
829828
*/
830-
private function getDummyExpression(SqlExpressionField $exprField, Model $model)
829+
private function getDummyExpression(SqlExpressionField $exprField, Model $entity)
831830
{
832831
$expr = $exprField->expr;
833-
$matches = [];
832+
if ($expr instanceof \Closure) {
833+
$expr = $exprField->getDsqlExpression($entity->getModel()->expr(''));
834+
}
835+
if ($expr instanceof Persistence\Sql\Expression) {
836+
$expr = \Closure::bind(fn () => $expr->template, null, Persistence\Sql\Expression::class)();
837+
}
834838

839+
$matches = [];
835840
preg_match_all('~\[[a-z0-9_]*\]|{[a-z0-9_]*}~i', $expr, $matches);
836841

837842
foreach ($matches[0] as $match) {
838843
$fieldName = substr($match, 1, -1);
839-
$field = $model->getField($fieldName);
844+
$field = $entity->getField($fieldName);
840845
if ($field instanceof SqlExpressionField) {
841-
$expr = str_replace($match, $this->getDummyExpression($field, $model), $expr);
846+
$expr = str_replace($match, $this->getDummyExpression($field, $entity), $expr);
842847
} else {
843-
$expr = str_replace($match, $this->getValueForExpression($exprField, $fieldName, $model), $expr);
848+
$expr = str_replace($match, $this->getValueForExpression($exprField, $fieldName, $entity), $expr);
844849
}
845850
}
846851

@@ -853,17 +858,17 @@ private function getDummyExpression(SqlExpressionField $exprField, Model $model)
853858
*
854859
* @return string
855860
*/
856-
private function getValueForExpression(Field $exprField, string $fieldName, Model $model)
861+
private function getValueForExpression(Field $exprField, string $fieldName, Model $entity)
857862
{
858863
switch ($exprField->type) {
859864
case 'integer':
860865
case 'float':
861866
case 'atk4_money':
862-
$value = (string) ($model->get($fieldName) ?? 0);
867+
$value = (string) ($entity->get($fieldName) ?? 0);
863868

864869
break;
865870
default:
866-
$value = '"' . $model->get($fieldName) . '"';
871+
$value = '"' . $entity->get($fieldName) . '"';
867872
}
868873

869874
return $value;

0 commit comments

Comments
 (0)