From 3781e5ff51e8ad2f6e0db4b75555065a4dc78b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Thu, 22 Apr 2021 16:47:37 +0200 Subject: [PATCH] Fix "field" action - all records, allow Action as scope condition --- phpstan.neon.dist | 2 -- src/Action/RenameColumnIterator.php | 37 +++++++++++++++++++++++++++++ src/Persistence/Array_.php | 21 ++++++++-------- src/Persistence/Array_/Action.php | 17 ++++++++++++- tests/PersistentArrayTest.php | 10 ++++++-- 5 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 src/Action/RenameColumnIterator.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 5a864f0186..8723e10a59 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -46,8 +46,6 @@ parameters: - '~^Call to an undefined method Atk4\\Data\\Reference\\HasOne\:\:addField\(\)\.$~' # for tests/LookupSqlTest.php - '~^Call to an undefined method Atk4\\Data\\Reference\\HasOne\:\:withTitle\(\)\.$~' - # for tests/PersistentArrayTest.php - - '~^Call to an undefined method Atk4\\Data\\Persistence\:\:applyScope\(\)\.$~' # for tests/ReferenceSqlTest.php - '~^Call to an undefined method Atk4\\Data\\Reference\\HasOne\:\:addFields\(\)\.$~' - '~^Call to an undefined method Atk4\\Data\\Reference\:\:addTitle\(\)\.$~' diff --git a/src/Action/RenameColumnIterator.php b/src/Action/RenameColumnIterator.php new file mode 100644 index 0000000000..20c78c8dea --- /dev/null +++ b/src/Action/RenameColumnIterator.php @@ -0,0 +1,37 @@ + $iterator + */ + public function __construct(\Traversable $iterator, string $origName, string $newName) + { + parent::__construct($iterator); + + $this->origName = $origName; + $this->newName = $newName; + } + + public function current(): array + { + $row = parent::current(); + + $keys = array_keys($row); + $keys[array_search($this->origName, $keys, true)] = $this->newName; + + return array_combine($keys, $row); + } +} diff --git a/src/Persistence/Array_.php b/src/Persistence/Array_.php index 91e8f24bb2..889e006c3f 100644 --- a/src/Persistence/Array_.php +++ b/src/Persistence/Array_.php @@ -4,6 +4,7 @@ namespace Atk4\Data\Persistence; +use Atk4\Data\Action\RenameColumnIterator; use Atk4\Data\Exception; use Atk4\Data\Model; use Atk4\Data\Persistence; @@ -199,15 +200,19 @@ public function tryLoad(Model $model, $id): ?array $table = $this->seedDataAndGetTable($model); if ($id === self::ID_LOAD_ONE || $id === self::ID_LOAD_ANY) { - if (iterator_count($table->getRows()) === 0) { + $action = $this->action($model, 'select'); + $action->generator->rewind(); // needed for some reasons! + + $selectRow = $action->getRow(); + if ($selectRow === null) { return null; - } elseif ($id === self::ID_LOAD_ONE && iterator_count($table->getRows()) !== 1) { + } elseif ($id === self::ID_LOAD_ONE && $action->getRow() !== null) { throw (new Exception('Ambiguous conditions, more than one record can be loaded.')) ->addMoreInfo('model', $model) ->addMoreInfo('id', null); } - $id = $table->getRows()->current()->getValue($model->id_field); // @phpstan-ignore-line + $id = $selectRow[$model->id_field]; $row = $this->tryLoad($model, $id); $model->setId($id); // @TODO is it needed? @@ -433,15 +438,11 @@ public function action(Model $model, $type, $args = []) $this->applyScope($model, $action); $this->setLimitOrder($model, $action); - // get first record - if ($row = $action->getRow()) { - if (isset($args['alias']) && array_key_exists($field, $row)) { - $row[$args['alias']] = $row[$field]; - unset($row[$field]); - } + if (isset($args['alias'])) { + $action->generator = new RenameColumnIterator($action->generator, $field, $args['alias']); } - return $row; + return $action; case 'fx': case 'fx0': if (!isset($args[0], $args[1])) { diff --git a/src/Persistence/Array_/Action.php b/src/Persistence/Array_/Action.php index d38f698763..b2ac241882 100644 --- a/src/Persistence/Array_/Action.php +++ b/src/Persistence/Array_/Action.php @@ -155,6 +155,14 @@ protected function match(array $row, Model\Scope\AbstractScope $condition) protected function evaluateIf($v1, $operator, $v2): bool { + if ($v2 instanceof self) { + $v2 = $v2->getRows(); + } + + if ($v2 instanceof \Traversable) { + throw new \Exception('Unexpected v2 type'); + } + switch (strtoupper((string) $operator)) { case '=': $result = is_array($v2) ? $this->evaluateIf($v1, 'IN', $v2) : $v1 === $v2; @@ -192,7 +200,14 @@ protected function evaluateIf($v1, $operator, $v2): bool break; case 'IN': - $result = is_array($v2) ? in_array($v1, $v2, true) : $this->evaluateIf($v1, '=', $v2); + $result = false; + foreach ($v2 as $v2Item) { // flatten rows, this looses column names! + if ($this->evaluateIf($v1, '=', $v2Item)) { + $result = true; + + break; + } + } break; case 'NOT IN': diff --git a/tests/PersistentArrayTest.php b/tests/PersistentArrayTest.php index 0e64c9138d..6c207a7950 100644 --- a/tests/PersistentArrayTest.php +++ b/tests/PersistentArrayTest.php @@ -266,11 +266,17 @@ public function testActionField() // use alias as array key if it is set $q = $m->action('field', ['name', 'alias' => 'first_name']); - $this->assertSame(['first_name' => 'John'], $q); + $this->assertSame([ + 1 => ['first_name' => 'John'], + 2 => ['first_name' => 'Sarah'], + ], $q->getRows()); // if alias is not set, then use field name as key $q = $m->action('field', ['name']); - $this->assertSame(['name' => 'John'], $q); + $this->assertSame([ + 1 => ['name' => 'John'], + 2 => ['name' => 'Sarah'], + ], $q->getRows()); } /**