diff --git a/src/Libs/Data.php b/src/Libs/Data.php index dd43ae2a..b3181e9f 100644 --- a/src/Libs/Data.php +++ b/src/Libs/Data.php @@ -52,4 +52,9 @@ final class Data { return ag(self::$data, $filter, $default); } + + public static function reset(): void + { + self::$data = []; + } } diff --git a/src/Libs/Entity/StateEntity.php b/src/Libs/Entity/StateEntity.php index 78b32fc0..c5bb6ce9 100644 --- a/src/Libs/Entity/StateEntity.php +++ b/src/Libs/Entity/StateEntity.php @@ -155,6 +155,11 @@ final class StateEntity implements StateInterface return $this->guids; } + public function getPointers(array|null $guids = null): array + { + return Guid::fromArray(array_intersect_key($this->guids, Guid::SUPPORTED))->getPointers(); + } + public function hasParentGuid(): bool { return count($this->parent) >= 1; @@ -220,18 +225,12 @@ final class StateEntity implements StateInterface public function apply(StateInterface $entity, bool $guidOnly = false): self { if (true === $guidOnly) { - if ($this->guids !== $entity->guids) { - $this->updateValue('guids', $entity); + foreach (StateInterface::ENTITY_FORCE_UPDATE_FIELDS as $key) { + if (true === $this->isEqualValue($key, $entity)) { + continue; + } + $this->updateValue($key, $entity); } - - if ($this->parent !== $entity->parent) { - $this->updateValue('parent', $entity); - } - - if ($this->suids !== $entity->suids) { - $this->updateValue('suids', $entity); - } - return $this; } @@ -257,19 +256,6 @@ final class StateEntity implements StateInterface return $this->data; } - public function getPointers(array|null $guids = null): array - { - $list = array_intersect_key($this->guids, Guid::SUPPORTED); - - if ($this->isEpisode()) { - foreach ($list as $key => $val) { - $list[$key] = $val . '/' . $this->season . '/' . $this->episode; - } - } - - return Guid::fromArray($list)->getPointers(); - } - public function setIsTainted(bool $isTainted): StateInterface { $this->tainted = $isTainted; @@ -283,9 +269,8 @@ final class StateEntity implements StateInterface private function isEqual(StateInterface $entity): bool { - foreach ($this->getAll() as $key => $val) { - $checkedValue = $this->isEqualValue($key, $entity); - if (false === $checkedValue) { + foreach (StateInterface::ENTITY_KEYS as $key) { + if (false === $this->isEqualValue($key, $entity)) { return false; } } diff --git a/src/Libs/Entity/StateInterface.php b/src/Libs/Entity/StateInterface.php index eac97d04..628f714d 100644 --- a/src/Libs/Entity/StateInterface.php +++ b/src/Libs/Entity/StateInterface.php @@ -9,34 +9,69 @@ interface StateInterface public const TYPE_MOVIE = 'movie'; public const TYPE_EPISODE = 'episode'; - public const ENTITY_IGNORE_DIFF_CHANGES = [ - 'via', - 'extra', - 'title', - 'year', - ]; - - public const ENTITY_ARRAY_KEYS = [ - 'parent', - 'guids', - 'extra', - 'suids' - ]; + /** + * If you must reference field directly, use those constants. + */ + public const COLUMN_ID = 'id'; + public const COLUMN_TYPE = 'type'; + public const COLUMN_UPDATED = 'updated'; + public const COLUMN_WATCHED = 'watched'; + public const COLUMN_VIA = 'via'; + public const COLUMN_TITLE = 'title'; + public const COLUMN_YEAR = 'year'; + public const COLUMN_SEASON = 'season'; + public const COLUMN_EPISODE = 'episode'; + public const COLUMN_PARENT = 'parent'; + public const COLUMN_GUIDS = 'guids'; + public const COLUMN_EXTRA = 'extra'; + public const COLUMN_SUIDS = 'suids'; + /** + * List of table keys. + */ public const ENTITY_KEYS = [ - 'id', - 'type', - 'updated', - 'watched', - 'via', - 'title', - 'year', - 'season', - 'episode', - 'parent', - 'guids', - 'extra', - 'suids', + self::COLUMN_ID, + self::COLUMN_TYPE, + self::COLUMN_UPDATED, + self::COLUMN_WATCHED, + self::COLUMN_VIA, + self::COLUMN_TITLE, + self::COLUMN_YEAR, + self::COLUMN_SEASON, + self::COLUMN_EPISODE, + self::COLUMN_PARENT, + self::COLUMN_GUIDS, + self::COLUMN_EXTRA, + self::COLUMN_SUIDS, + ]; + + /** + * Ignore listed fields if the played status did not change. + */ + public const ENTITY_IGNORE_DIFF_CHANGES = [ + self::COLUMN_VIA, + self::COLUMN_EXTRA, + self::COLUMN_TITLE, + self::COLUMN_YEAR, + ]; + + /** + * Fields that if changed will trigger an update regardless of the watchstate or event. + */ + public const ENTITY_FORCE_UPDATE_FIELDS = [ + self::COLUMN_PARENT, + self::COLUMN_GUIDS, + self::COLUMN_SUIDS, + ]; + + /** + * List of JSON/array fields. + */ + public const ENTITY_ARRAY_KEYS = [ + self::COLUMN_PARENT, + self::COLUMN_GUIDS, + self::COLUMN_EXTRA, + self::COLUMN_SUIDS, ]; /** diff --git a/src/Libs/Initializer.php b/src/Libs/Initializer.php index 31ce39b8..4d6109ff 100644 --- a/src/Libs/Initializer.php +++ b/src/Libs/Initializer.php @@ -292,11 +292,10 @@ final class Initializer } if (true === $entity->isTainted()) { - if ($backend->apply($entity, guidOnly: true)->isChanged()) { - if (!empty($entity->meta)) { - $backend->meta = $entity->meta; - } - $backend = $storage->update($backend); + $cloned = clone $backend; + + if ($cloned->apply($entity, guidOnly: true)->isChanged()) { + $backend = $storage->update($backend->apply($entity)); return jsonResponse(status: 200, body: $backend->getAll(), headers: [ 'X-Status' => '[T] Updated External/Relative ids.', 'X-WH-Type' => $request->getAttribute('WH_TYPE', 'not_set'), @@ -312,11 +311,10 @@ final class Initializer } if ($backend->updated > $entity->updated) { - if ($backend->apply($entity, guidOnly: true)->isChanged()) { - if (!empty($entity->meta)) { - $backend->meta = $entity->meta; - } - $backend = $storage->update($backend); + $cloned = clone $backend; + + if ($cloned->apply($entity, guidOnly: true)->isChanged()) { + $backend = $storage->update($backend->apply($entity)); return jsonResponse(status: 200, body: $backend->getAll(), headers: [ 'X-Status' => 'Updated External/Relative ids.', 'X-WH-Type' => $request->getAttribute('WH_TYPE', 'not_set'), diff --git a/tests/Mappers/Import/MemoryMapperTest.php b/tests/Mappers/Import/MemoryMapperTest.php index 524ffa0c..bae19730 100644 --- a/tests/Mappers/Import/MemoryMapperTest.php +++ b/tests/Mappers/Import/MemoryMapperTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Tests\Mappers\Import; +use App\Libs\Data; use App\Libs\Entity\StateEntity; use App\Libs\Entity\StateInterface; use App\Libs\Extends\CliLogger; @@ -38,6 +39,8 @@ class MemoryMapperTest extends TestCase $this->mapper = new MemoryMapper($logger, $this->storage); $this->mapper->setUp(['class' => new StateEntity([])]); + + Data::reset(); } public function test_loadData_null_date_conditions(): void @@ -169,26 +172,33 @@ class MemoryMapperTest extends TestCase $testMovie = new StateEntity($this->testMovie); $testEpisode = new StateEntity($this->testEpisode); - $this->mapper->add('test', 'movie', $testMovie) - ->add('test', 'episode', $testEpisode); + $insert = $this->mapper + ->add('test', 'movie', $testMovie) + ->add('test', 'episode', $testEpisode) + ->commit(); $this->assertSame( [ StateInterface::TYPE_MOVIE => ['added' => 1, 'updated' => 0, 'failed' => 0], StateInterface::TYPE_EPISODE => ['added' => 1, 'updated' => 0, 'failed' => 0], ], - $this->mapper->commit() + $insert ); - $testMovie->guids['guid_anidb'] = '1'; - $testEpisode->guids['guid_anidb'] = '1'; + $testMovie->guids['guid_anidb'] = 'movie/211'; + $testEpisode->guids['guid_anidb'] = 'series/223'; + + $updated = $this->mapper + ->add('test', 'movie', $testMovie) + ->add('test', 'episode', $testEpisode) + ->commit(); $this->assertSame( [ StateInterface::TYPE_MOVIE => ['added' => 0, 'updated' => 1, 'failed' => 0], StateInterface::TYPE_EPISODE => ['added' => 0, 'updated' => 1, 'failed' => 0], ], - $this->mapper->add('test', 'movie', $testMovie)->add('test', 'episode', $testEpisode)->commit() + $updated ); }