diff --git a/src/Model.php b/src/Model.php index 0613644bb..6085c42d8 100644 --- a/src/Model.php +++ b/src/Model.php @@ -1990,11 +1990,11 @@ public function leftJoin(string $foreign_table, $defaults = []) /** * Private method. * - * @param string $c Class name - * @param string $link Link - * @param array|callable $defaults Properties which we will pass to Reference object constructor + * @param string $className Class name + * @param string $link Link + * @param array|callable $defaults Properties which we will pass to Reference object constructor */ - protected function _hasReference($c, $link, $defaults = []): Reference + protected function _hasReference($className, $link, $defaults = []): Reference { if (!is_array($defaults)) { $defaults = ['model' => $defaults ?: 'Model_' . $link]; @@ -2005,7 +2005,7 @@ protected function _hasReference($c, $link, $defaults = []): Reference $defaults[0] = $link; - $obj = $this->factory($c, $defaults); + $obj = $this->factory($className, $defaults); // if reference with such name already exists, then throw exception if ($this->hasElement($name = $obj->getDesiredName())) { diff --git a/src/Reference.php b/src/Reference.php index 921c919e1..f8b4595fc 100644 --- a/src/Reference.php +++ b/src/Reference.php @@ -11,7 +11,7 @@ * * It's possible to extend the basic reference with more meaningful references. * - * @property Model $owner + * @property Model $owner definition of our model */ class Reference { @@ -41,7 +41,7 @@ class Reference public $link; /** - * Definition of the destination model, that can be either an object, a + * Definition of the destination their model, that can be either an object, a * callback or a string. This can be defined during initialization and * then used inside getModel() to fully populate and associate with * persistence. @@ -90,6 +90,8 @@ public function __construct($link) public function init(): void { $this->_init(); + + $this->initTableAlias(); } /** @@ -100,59 +102,75 @@ public function getDesiredName(): string return '#ref_' . $this->link; } + public function getOurModel(): Model + { + return $this->owner; + } + + /** + * @deprecated use getTheirModel instead - will be removed in dec-2020 + */ + public function getModel($defaults = []): Model + { + 'trigger_error'('Method Reference::getModel is deprecated. Use Model::getTheirModel instead', E_USER_DEPRECATED); + + return $this->getTheirModel($defaults); + } + /** * Returns destination model that is linked through this reference. Will apply * necessary conditions. * * @param array $defaults Properties */ - public function getModel($defaults = []): Model + public function getTheirModel($defaults = []): Model { // set table_alias - if (!isset($defaults['table_alias'])) { - if (!$this->table_alias) { - $this->table_alias = $this->link; - $this->table_alias = preg_replace('/_' . ($this->owner->id_field ?: 'id') . '/', '', $this->table_alias); - $this->table_alias = preg_replace('/([a-zA-Z])[a-zA-Z]*[^a-zA-Z]*/', '\1', $this->table_alias); - if (isset($this->owner->table_alias)) { - $this->table_alias = $this->owner->table_alias . '_' . $this->table_alias; - } - } - $defaults['table_alias'] = $this->table_alias; - } - - // if model is Closure, then call it and return model - if (is_object($this->model) && $this->model instanceof \Closure) { - $c = ($this->model)($this->owner, $this, $defaults); - - return $this->addToPersistence($c, $defaults); - } + $defaults['table_alias'] = $defaults['table_alias'] ?? $this->table_alias; - // if model is set, then return clone of this model if (is_object($this->model)) { - $c = clone $this->model; + if ($this->model instanceof \Closure) { + // if model is Closure, then call the closure and whci should return a model + $theirModel = ($this->model)($this->getOurModel(), $this, $defaults); + } else { + // if model is set, then use clone of this model + $theirModel = clone $this->model; + } - return $this->addToPersistence($c, $defaults); + return $this->addToPersistence($theirModel, $defaults); } - // last effort - try to add model + // add model from seed if (is_array($this->model)) { - $model = [$this->model[0]]; - $md = $this->model; - unset($md[0]); + $modelDefaults = $this->model; + $theirModelSeed = [$modelDefaults[0]]; + + unset($modelDefaults[0]); - $defaults = array_merge($md, $defaults); + $defaults = array_merge($modelDefaults, $defaults); } elseif (is_string($this->model)) { - $model = [$this->model]; + $theirModelSeed = [$this->model]; } else { - $model = $this->model; + $theirModelSeed = $this->model; } - if (!$model instanceof Model) { - $model = $this->factory($model, $defaults); - } + $theirModel = $this->factory($theirModelSeed, $defaults); + + return $this->addToPersistence($theirModel, $defaults); + } - return $this->addToPersistence($model, $defaults); + protected function initTableAlias(): void + { + if (!$this->table_alias) { + $ourModel = $this->getOurModel(); + + $this->table_alias = $this->link; + $this->table_alias = preg_replace('/_' . ($ourModel->id_field ?: 'id') . '/', '', $this->table_alias); + $this->table_alias = preg_replace('/([a-zA-Z])[a-zA-Z]*[^a-zA-Z]*/', '\1', $this->table_alias); + if (isset($ourModel->table_alias)) { + $this->table_alias = $ourModel->table_alias . '_' . $this->table_alias; + } + } } /** @@ -163,8 +181,8 @@ public function getModel($defaults = []): Model */ protected function addToPersistence($model, $defaults = []): Model { - if (!$model->persistence && $p = $this->getDefaultPersistence($model)) { - $p->add($model, $defaults); + if (!$model->persistence && $persistence = $this->getDefaultPersistence($model)) { + $persistence->add($model, $defaults); } // set model caption @@ -184,16 +202,16 @@ protected function addToPersistence($model, $defaults = []): Model */ protected function getDefaultPersistence($model) { - $m = $this->owner; + $ourModel = $this->getOurModel(); // this will be useful for containsOne/Many implementation in case when you have // SQL_Model->containsOne()->hasOne() structure to get back to SQL persistence // from Array persistence used in containsOne model - if ($m->contained_in_root_model && $m->contained_in_root_model->persistence) { - return $m->contained_in_root_model->persistence; + if ($ourModel->contained_in_root_model && $ourModel->contained_in_root_model->persistence) { + return $ourModel->contained_in_root_model->persistence; } - return $m->persistence ?: false; + return $ourModel->persistence ?: false; } /** @@ -204,7 +222,7 @@ protected function getDefaultPersistence($model) */ public function ref($defaults = []): Model { - return $this->getModel($defaults); + return $this->getTheirModel($defaults); } /** @@ -216,7 +234,7 @@ public function ref($defaults = []): Model */ public function refModel($defaults = []): Model { - return $this->getModel($defaults); + return $this->getTheirModel($defaults); } // {{{ Debug Methods diff --git a/src/Reference/ContainsMany.php b/src/Reference/ContainsMany.php index 8619d39e5..57a5c215b 100644 --- a/src/Reference/ContainsMany.php +++ b/src/Reference/ContainsMany.php @@ -4,10 +4,8 @@ namespace atk4\data\Reference; -use atk4\data\Exception; use atk4\data\Model; -use atk4\data\Persistence\ArrayOfStrings; -use atk4\data\Reference; +use atk4\data\Persistence; /** * ContainsMany reference. @@ -25,28 +23,25 @@ class ContainsMany extends ContainsOne */ protected function getDefaultPersistence($model) { - $m = $this->owner; + $ourModel = $this->getOurModel(); // model should be loaded /* Imants: it looks that this is not actually required - disabling - if (!$m->loaded()) { + if (!$ourModel->loaded()) { throw (new Exception('Model should be loaded!')) - ->addMoreInfo('model', get_class($m)); + ->addMoreInfo('model', get_class($ourModel)); } */ // set data source of referenced array persistence - $rows = $m->get($this->our_field) ?: []; + $rows = $ourModel->get($this->our_field) ?: []; /* foreach ($rows as $id=>$row) { - $rows[$id] = $this->owner->persistence->typecastLoadRow($m, $row); // we need this typecasting because we set persistence data directly + $rows[$id] = $ourModel->persistence->typecastLoadRow($ourModel, $row); // we need this typecasting because we set persistence data directly } */ - $data = [$this->table_alias => $rows ?: []]; - $p = new ArrayOfStrings($data); - - return $p; + return new Persistence\ArrayOfStrings([$this->table_alias => $rows ?: []]); } /** @@ -56,22 +51,24 @@ protected function getDefaultPersistence($model) */ public function ref($defaults = []): Model { + $ourModel = $this->getOurModel(); + // get model // will not use ID field (no, sorry, will have to use it) - $m = $this->getModel(array_merge($defaults, [ - 'contained_in_root_model' => $this->owner->contained_in_root_model ?: $this->owner, + $theirModel = $this->getTheirModel(array_merge($defaults, [ + 'contained_in_root_model' => $ourModel->contained_in_root_model ?: $ourModel, //'id_field' => false, 'table' => $this->table_alias, ])); // set some hooks for ref_model foreach ([Model::HOOK_AFTER_SAVE, Model::HOOK_AFTER_DELETE] as $spot) { - $m->onHook($spot, function ($model) { - $rows = $model->persistence->getRawDataByTable($this->table_alias); - $this->owner->save([$this->our_field => $rows ?: null]); + $theirModel->onHook($spot, function ($theirModel) { + $rows = $theirModel->persistence->getRawDataByTable($this->table_alias); + $this->getOurModel()->save([$this->our_field => $rows ?: null]); }); } - return $m; + return $theirModel; } } diff --git a/src/Reference/ContainsOne.php b/src/Reference/ContainsOne.php index 1b81df1a1..1e03903b7 100644 --- a/src/Reference/ContainsOne.php +++ b/src/Reference/ContainsOne.php @@ -4,9 +4,8 @@ namespace atk4\data\Reference; -use atk4\data\Exception; use atk4\data\Model; -use atk4\data\Persistence\ArrayOfStrings; +use atk4\data\Persistence; use atk4\data\Reference; /** @@ -60,8 +59,10 @@ public function init(): void $this->our_field = $this->link; } - if (!$this->owner->hasElement($this->our_field)) { - $this->owner->addField($this->our_field, [ + $ourModel = $this->getOurModel(); + + if (!$ourModel->hasElement($this->our_field)) { + $ourModel->addField($this->our_field, [ 'type' => $this->type, 'reference' => $this, 'system' => $this->system, @@ -82,24 +83,21 @@ public function init(): void */ protected function getDefaultPersistence($model) { - $m = $this->owner; + $ourModel = $this->getOurModel(); // model should be loaded /* Imants: it looks that this is not actually required - disabling - if (!$m->loaded()) { + if (!$ourModel->loaded()) { throw (new Exception('Model should be loaded!')) - ->addMoreInfo('model', get_class($m)); + ->addMoreInfo('model', get_class($ourModel)); } */ // set data source of referenced array persistence - $row = $m->get($this->our_field) ?: []; - //$row = $m->persistence->typecastLoadRow($m, $row); // we need this typecasting because we set persistence data directly - - $data = [$this->table_alias => $row ? [1 => $row] : []]; - $p = new ArrayOfStrings($data); + $row = $ourModel->get($this->our_field) ?: []; + //$row = $ourModel->persistence->typecastLoadRow($ourModel, $row); // we need this typecasting because we set persistence data directly - return $p; + return new Persistence\ArrayOfStrings([$this->table_alias => $row ? [1 => $row] : []]); } /** @@ -109,26 +107,28 @@ protected function getDefaultPersistence($model) */ public function ref($defaults = []): Model { + $ourModel = $this->getOurModel(); + // get model // will not use ID field - $m = $this->getModel(array_merge($defaults, [ - 'contained_in_root_model' => $this->owner->contained_in_root_model ?: $this->owner, + $theirModel = $this->getTheirModel(array_merge($defaults, [ + 'contained_in_root_model' => $ourModel->contained_in_root_model ?: $ourModel, 'id_field' => false, 'table' => $this->table_alias, ])); // set some hooks for ref_model foreach ([Model::HOOK_AFTER_SAVE, Model::HOOK_AFTER_DELETE] as $spot) { - $m->onHook($spot, function ($model) { - $row = $model->persistence->getRawDataByTable($this->table_alias); + $theirModel->onHook($spot, function ($theirModel) { + $row = $theirModel->persistence->getRawDataByTable($this->table_alias); $row = $row ? array_shift($row) : null; // get first and only one record from array persistence - $this->owner->save([$this->our_field => $row]); + $this->getOurModel()->save([$this->our_field => $row]); }); } // try to load any (actually only one possible) record - $m->tryLoadAny(); + $theirModel->tryLoadAny(); - return $m; + return $theirModel; } } diff --git a/src/Reference/HasMany.php b/src/Reference/HasMany.php index 374d3ffd8..1905b3747 100644 --- a/src/Reference/HasMany.php +++ b/src/Reference/HasMany.php @@ -21,19 +21,18 @@ class HasMany extends Reference */ protected function getOurValue() { - if ($this->owner->loaded()) { + $ourModel = $this->getOurModel(); + + if ($ourModel->loaded()) { return $this->our_field - ? $this->owner->get($this->our_field) - : $this->owner->id; + ? $ourModel->get($this->our_field) + : $ourModel->id; } // create expression based on existing conditions - return $this->owner->action( - 'field', - [ - $this->our_field ?: ($this->owner->id_field ?: 'id'), - ] - ); + return $ourModel->action('field', [ + $this->our_field ?: $ourModel->id_field, + ]); } /** @@ -41,9 +40,11 @@ protected function getOurValue() */ protected function referenceOurValue(): Field { - $this->owner->persistence_data['use_table_prefixes'] = true; + $ourModel = $this->getOurModel(); + + $ourModel->persistence_data['use_table_prefixes'] = true; - return $this->owner->getField($this->our_field ?: ($this->owner->id_field ?: 'id')); + return $ourModel->getField($this->our_field ?: $ourModel->id_field); } /** @@ -53,11 +54,12 @@ protected function referenceOurValue(): Field */ public function ref($defaults = []): Model { - return $this->getModel($defaults) - ->addCondition( - $this->their_field ?: ($this->owner->table . '_' . ($this->owner->id_field ?: 'id')), - $this->getOurValue() - ); + $ourModel = $this->getOurModel(); + + return $this->getTheirModel($defaults)->addCondition( + $this->their_field ?: ($ourModel->table . '_' . $ourModel->id_field), + $this->getOurValue() + ); } /** @@ -67,70 +69,71 @@ public function ref($defaults = []): Model */ public function refLink($defaults = []): Model { - return $this->getModel($defaults) - ->addCondition( - $this->their_field ?: ($this->owner->table . '_' . ($this->owner->id_field ?: 'id')), - $this->referenceOurValue() - ); + $ourModel = $this->getOurModel(); + + $theirModelLinked = $this->getTheirModel($defaults)->addCondition( + $this->their_field ?: ($ourModel->table . '_' . $ourModel->id_field), + $this->referenceOurValue() + ); + + return $theirModelLinked; } /** - * Adds field as expression to owner model. + * Adds field as expression to our model. * Used in aggregate strategy. * - * @param string $n Field name - * @param array $defaults Properties + * @param string $fieldName Field name + * @param array $defaults Properties */ - public function addField($n, $defaults = []): Field + public function addField($fieldName, $defaults = []): Field { if (!isset($defaults['aggregate']) && !isset($defaults['concat']) && !isset($defaults['expr'])) { throw (new Exception('Aggregate field requires "aggregate", "concat" or "expr" specified to hasMany()->addField()')) - ->addMoreInfo('field', $n) + ->addMoreInfo('field', $fieldName) ->addMoreInfo('defaults', $defaults); } $defaults['aggregate_relation'] = $this; - $field_n = $defaults['field'] ?? $n; - $field = $defaults['field'] ?? null; + $alias = $defaults['field'] ?? null; + $field = $alias ?? $fieldName; if (isset($defaults['concat'])) { - $defaults['aggregate'] = $this->owner->dsql()->groupConcat($field_n, $defaults['concat']); + $defaults['aggregate'] = $this->getOurModel()->dsql()->groupConcat($field, $defaults['concat']); $defaults['read_only'] = false; $defaults['never_save'] = true; } if (isset($defaults['expr'])) { - $cb = function () use ($defaults, $field) { - $r = $this->refLink(); + $fx = function () use ($defaults, $alias) { + $theirModelLinked = $this->refLink(); - return $r->action('field', [$r->expr( + return $theirModelLinked->action('field', [$theirModelLinked->expr( $defaults['expr'], $defaults['args'] ?? null - ), 'alias' => $field]); + ), 'alias' => $alias]); }; unset($defaults['args']); } elseif (is_object($defaults['aggregate'])) { - $cb = function () use ($defaults, $field) { - return $this->refLink()->action('field', [$defaults['aggregate'], 'alias' => $field]); + $fx = function () use ($defaults, $alias) { + return $this->refLink()->action('field', [$defaults['aggregate'], 'alias' => $alias]); }; } elseif ($defaults['aggregate'] === 'count' && !isset($defaults['field'])) { - $cb = function () use ($defaults, $field) { - return $this->refLink()->action('count', ['alias' => $field]); + $fx = function () use ($defaults, $alias) { + return $this->refLink()->action('count', ['alias' => $alias]); }; } elseif (in_array($defaults['aggregate'], ['sum', 'avg', 'min', 'max', 'count'], true)) { - $cb = function () use ($defaults, $field_n) { - return $this->refLink()->action('fx0', [$defaults['aggregate'], $field_n]); + $fx = function () use ($defaults, $field) { + return $this->refLink()->action('fx0', [$defaults['aggregate'], $field]); }; } else { - $cb = function () use ($defaults, $field_n) { - return $this->refLink()->action('fx', [$defaults['aggregate'], $field_n]); + $fx = function () use ($defaults, $field) { + return $this->refLink()->action('fx', [$defaults['aggregate'], $field]); }; } - $e = $this->owner->addExpression($n, array_merge([$cb], $defaults)); - - return $e; + return $this->getOurModel()->addExpression($fieldName, array_merge([$fx], $defaults)); } /** @@ -144,10 +147,10 @@ public function addField($n, $defaults = []): Field */ public function addFields($fields = []) { - foreach ($fields as $field) { - $name = $field[0]; - unset($field[0]); - $this->addField($name, $field); + foreach ($fields as $defaults) { + $fieldName = $defaults[0]; + unset($defaults[0]); + $this->addField($fieldName, $defaults); } return $this; diff --git a/src/Reference/HasOne.php b/src/Reference/HasOne.php index 2497e2e62..dae9e2257 100644 --- a/src/Reference/HasOne.php +++ b/src/Reference/HasOne.php @@ -164,8 +164,10 @@ public function init(): void $this->our_field = $this->link; } - if (!$this->owner->hasField($this->our_field)) { - $this->owner->addField($this->our_field, [ + $ourModel = $this->getOurModel(); + + if (!$ourModel->hasField($this->our_field)) { + $ourModel->addField($this->our_field, [ 'type' => $this->type, 'reference' => $this, 'system' => $this->system, @@ -192,47 +194,43 @@ public function init(): void */ protected function referenceOurValue(): Field { - $this->owner->persistence_data['use_table_prefixes'] = true; + $this->getOurModel()->persistence_data['use_table_prefixes'] = true; - return $this->owner->getField($this->our_field); + return $this->getOurModel()->getField($this->our_field); } /** - * If owner model is loaded, then return referenced model with respective record loaded. + * If our model is loaded, then return their model with respective record loaded. * - * If owner model is not loaded, then return referenced model with condition set. - * This can happen in case of deep traversal $m->ref('Many')->ref('one_id'), for example. + * If our model is not loaded, then return their model with condition set. + * This can happen in case of deep traversal $model->ref('Many')->ref('one_id'), for example. * * @param array $defaults Properties */ public function ref($defaults = []): Model { - $m = $this->getModel($defaults); + $theirModel = $this->getTheirModel($defaults); // add hook to set our_field = null when record of referenced model is deleted - $m->onHook(Model::HOOK_AFTER_DELETE, function ($m) { - $this->owner->set($this->our_field, null); + $theirModel->onHook(Model::HOOK_AFTER_DELETE, function ($theirModel) { + $this->getOurModel()->set($this->our_field, null); }); - // if owner model is loaded, then try to load referenced model - if ($this->their_field) { - if ($this->owner->get($this->our_field)) { - $m->tryLoadBy($this->their_field, $this->owner->get($this->our_field)); + if ($ourValue = $this->getOurModel()->get($this->our_field)) { + // if our model is loaded, then try to load referenced model + if ($this->their_field) { + $theirModel->tryLoadBy($this->their_field, $ourValue); + } else { + $theirModel->tryLoad($ourValue); } + } - $m->onHook(Model::HOOK_AFTER_SAVE, function ($m) { - $this->owner->set($this->our_field, $m->get($this->their_field)); - }); - } else { - if ($this->owner->get($this->our_field)) { - $m->tryLoad($this->owner->get($this->our_field)); - } + $theirModel->onHook(Model::HOOK_AFTER_SAVE, function ($theirModel) { + $ourValue = $this->their_field ? $theirModel->get($this->their_field) : $theirModel->id; - $m->onHook(Model::HOOK_AFTER_SAVE, function ($m) { - $this->owner->set($this->our_field, $m->id); - }); - } + $this->getOurModel()->set($this->our_field, $ourValue); + }); - return $m; + return $theirModel; } } diff --git a/src/Reference/HasOneSql.php b/src/Reference/HasOneSql.php index 3c160862f..008fc6b84 100644 --- a/src/Reference/HasOneSql.php +++ b/src/Reference/HasOneSql.php @@ -20,57 +20,60 @@ class HasOneSql extends HasOne * * Returns Expression in case you want to do something else with it. * - * @param string|Field|array $field or [$field, ..defaults] + * @param string|Field|array $fieldName or [$field, ..defaults] */ - public function addField($field, string $their_field = null): FieldSqlExpression + public function addField($fieldName, string $theirFieldName = null): FieldSqlExpression { - if (is_array($field)) { - $defaults = $field; + if (is_array($fieldName)) { + $defaults = $fieldName; if (!isset($defaults[0])) { throw (new Exception('Field name must be specified')) - ->addMoreInfo('field', $field); + ->addMoreInfo('field', $fieldName); } - $field = $defaults[0]; + $fieldName = $defaults[0]; unset($defaults[0]); } else { $defaults = []; } - if ($their_field === null) { - $their_field = $field; + if ($theirFieldName === null) { + $theirFieldName = $fieldName; } - // if caption is not defined in $defaults -> get it directly from the linked model field $their_field - $defaults['caption'] = $defaults['caption'] ?? $this->owner->refModel($this->link)->getField($their_field)->getCaption(); + $ourModel = $this->getOurModel(); - /** @var FieldSqlExpression $e */ - $e = $this->owner->addExpression($field, array_merge( + // if caption is not defined in $defaults -> get it directly from the linked model field $theirFieldName + $defaults['caption'] = $defaults['caption'] ?? $ourModel->refModel($this->link)->getField($theirFieldName)->getCaption(); + + /** @var FieldSqlExpression $fieldExpression */ + $fieldExpression = $ourModel->addExpression($fieldName, array_merge( [ - function (Model $m) use ($their_field) { + function (Model $ourModel) use ($theirFieldName) { // remove order if we just select one field from hasOne model // that is mandatory for Oracle - return $m->refLink($this->link)->action('field', [$their_field])->reset('order'); - }, ], + return $ourModel->refLink($this->link)->action('field', [$theirFieldName])->reset('order'); + }, + ], $defaults )); - $e->read_only = false; - $e->never_save = true; + $fieldExpression->read_only = false; + $fieldExpression->never_save = true; // Will try to execute last - $this->owner->onHook(Model::HOOK_BEFORE_SAVE, function (Model $m) use ($field, $their_field) { + $ourModel->onHook(Model::HOOK_BEFORE_SAVE, function (Model $ourModel) use ($fieldName, $theirFieldName) { // if title field is changed, but reference ID field (our_field) // is not changed, then update reference ID field value - if ($m->isDirty($field) && !$m->isDirty($this->our_field)) { - $mm = $this->getModel(); + if ($ourModel->isDirty($fieldName) && !$ourModel->isDirty($this->our_field)) { + $theirModel = $this->getTheirModel(); - $mm->addCondition($their_field, $m->get($field)); - $m->set($this->our_field, $mm->action('field', [$mm->id_field])); - $m->_unset($field); + $theirModel->addCondition($theirFieldName, $ourModel->get($fieldName)); + $ourModel->set($this->our_field, $theirModel->action('field', [$theirModel->id_field])); + $ourModel->_unset($fieldName); } }, [], 21); - return $e; + return $fieldExpression; } /** @@ -92,25 +95,24 @@ function (Model $m) use ($their_field) { */ public function addFields($fields = [], $defaults = []) { - foreach ($fields as $field => $alias) { - if (is_array($alias)) { - $d = array_merge($defaults, $alias); - if (!isset($alias[0])) { - throw (new Exception('Incorrect definition for addFields. Field name must be specified')) - ->addMoreInfo('field', $field) - ->addMoreInfo('alias', $alias); - } - $alias = $alias[0]; - } else { - $d = $defaults; + foreach ($fields as $ourFieldName => $ourFieldDefaults) { + $ourFieldDefaults = array_merge($defaults, (array) $ourFieldDefaults); + + if (!isset($ourFieldDefaults[0])) { + throw (new Exception('Incorrect definition for addFields. Field name must be specified')) + ->addMoreInfo('ourFieldName', $ourFieldName) + ->addMoreInfo('ourFieldDefaults', $ourFieldDefaults); } - if (is_numeric($field)) { - $field = $alias; + $theirFieldName = $ourFieldDefaults[0]; + + if (is_numeric($ourFieldName)) { + $ourFieldName = $theirFieldName; } - $d[0] = $field; - $this->addField($d, $alias); + $ourFieldDefaults[0] = $ourFieldName; + + $this->addField($ourFieldDefaults, $theirFieldName); } return $this; @@ -123,14 +125,14 @@ public function addFields($fields = [], $defaults = []) */ public function refLink($defaults = []): Model { - $m = $this->getModel($defaults); + $theirModel = $this->getTheirModel($defaults); - $m->addCondition( - $this->their_field ?: ($m->id_field), + $theirModel->addCondition( + $this->their_field ?: $theirModel->id_field, $this->referenceOurValue() ); - return $m; + return $theirModel; } /** @@ -140,32 +142,33 @@ public function refLink($defaults = []): Model */ public function ref($defaults = []): Model { - $m = parent::ref($defaults); + $theirModel = parent::ref($defaults); + $ourModel = $this->getOurModel(); - if (!isset($this->owner->persistence) || !($this->owner->persistence instanceof Persistence\Sql)) { - return $m; + if (!isset($ourModel->persistence) || !($ourModel->persistence instanceof Persistence\Sql)) { + return $theirModel; } // If model is not loaded, then we are probably doing deep traversal - if (!$this->owner->loaded()) { - $values = $this->owner->action('field', [$this->our_field]); + if (!$ourModel->loaded()) { + $values = $ourModel->action('field', [$this->our_field]); - return $m->addCondition($this->their_field ?: $m->id_field, $values); + return $theirModel->addCondition($this->their_field ?: $theirModel->id_field, $values); } // At this point the reference // if our_field is the id_field and is being used in the reference // we should persist the relation in condtition - // example - $m->load(1)->ref('refLink')->import($rows); - if ($this->owner->loaded() && !$m->loaded()) { - if ($this->owner->id_field === $this->our_field) { - $condition_field = $this->their_field ?: $m->id_field; - $condition_value = $this->owner->get($this->our_field ?: $this->owner->id_field); - $m->addCondition($condition_field, $condition_value); + // example - $model->load(1)->ref('refLink')->import($rows); + if ($ourModel->loaded() && !$theirModel->loaded()) { + if ($ourModel->id_field === $this->our_field) { + $field = $this->their_field ?: $theirModel->id_field; + $value = $ourModel->get($this->our_field ?: $ourModel->id_field); + $theirModel->addCondition($field, $value); } } - return $m; + return $theirModel; } /** @@ -186,21 +189,22 @@ public function addTitle($defaults = []): FieldSqlExpression ->addMoreInfo('arg', $defaults); } - $field = $defaults['field'] - ?? preg_replace('/_' . ($this->owner->id_field ?: 'id') . '$/i', '', $this->link); + $ourModel = $this->getOurModel(); + + $field = $defaults['field'] ?? preg_replace('/_' . $ourModel->id_field . '$/i', '', $this->link); - if ($this->owner->hasField($field)) { + if ($ourModel->hasField($field)) { throw (new Exception('Field with this name already exists. Please set title field name manually addTitle([\'field\'=>\'field_name\'])')) ->addMoreInfo('field', $field); } - /** @var FieldSqlExpression $ex */ - $ex = $this->owner->addExpression($field, array_replace_recursive( + /** @var FieldSqlExpression $fieldExpression */ + $fieldExpression = $ourModel->addExpression($field, array_replace_recursive( [ - function (Model $m) { - $mm = $m->refLink($this->link); + function (Model $ourModel) { + $theirModel = $ourModel->refLink($this->link); - return $mm->action('field', [$mm->title_field])->reset('order'); + return $theirModel->action('field', [$theirModel->title_field])->reset('order'); }, 'type' => null, 'ui' => ['editable' => false, 'visible' => true], @@ -215,23 +219,23 @@ function (Model $m) { )); // Will try to execute last - $this->owner->onHook(Model::HOOK_BEFORE_SAVE, function (Model $m) use ($field) { + $ourModel->onHook(Model::HOOK_BEFORE_SAVE, function (Model $ourModel) use ($field) { // if title field is changed, but reference ID field (our_field) // is not changed, then update reference ID field value - if ($m->isDirty($field) && !$m->isDirty($this->our_field)) { - $mm = $this->getModel(); + if ($ourModel->isDirty($field) && !$ourModel->isDirty($this->our_field)) { + $theirModel = $this->getTheirModel(); - $mm->addCondition($mm->title_field, $m->get($field)); - $m->set($this->our_field, $mm->action('field', [$mm->id_field])); + $theirModel->addCondition($theirModel->title_field, $ourModel->get($field)); + $ourModel->set($this->our_field, $theirModel->action('field', [$theirModel->id_field])); } }, [], 20); // Set ID field as not visible in grid by default - if (!array_key_exists('visible', $this->owner->getField($this->our_field)->ui)) { - $this->owner->getField($this->our_field)->ui['visible'] = false; + if (!array_key_exists('visible', $ourModel->getField($this->our_field)->ui)) { + $ourModel->getField($this->our_field)->ui['visible'] = false; } - return $ex; + return $fieldExpression; } /**