From 1e8b5dbd73fcebbf34885d4e9d7e6431bb026612 Mon Sep 17 00:00:00 2001 From: John Koster Date: Wed, 27 Mar 2024 19:24:49 -0500 Subject: [PATCH] Refactors --- src/EntryRelationship.php | 21 ++++++++ src/Listeners/EntrySavedListener.php | 2 +- src/Listeners/EntrySavingListener.php | 2 +- .../Concerns/ProcessesManyToMany.php | 4 +- .../Concerns/ProcessesOneToMany.php | 18 +++++++ src/Processors/RelationshipProcessor.php | 30 +++++++++-- src/RelationshipManager.php | 20 ++++++-- tests/OneToManyTest.php | 50 +++++++++++++++++++ tests/RelationshipTestCase.php | 9 ++++ 9 files changed, 145 insertions(+), 11 deletions(-) diff --git a/src/EntryRelationship.php b/src/EntryRelationship.php index 5176da0..af4ccc1 100644 --- a/src/EntryRelationship.php +++ b/src/EntryRelationship.php @@ -14,6 +14,10 @@ class EntryRelationship const TYPE_MANY_TO_ONE = 4; + public ?EntryRelationship $origin = null; + + public ?EntryRelationship $inverted = null; + public $leftType = ''; public $rightType = ''; @@ -115,6 +119,14 @@ public function withEvents($withEvents = true) return $this; } + public function withOriginRelationship(EntryRelationship $relationship) + { + $this->origin = $relationship; + $relationship->inverted = $this; + + return $this; + } + public function isAutomaticInverse($isInverse = true, $inverseIndex = null) { $this->isAutomaticInverse = $isInverse; @@ -128,6 +140,15 @@ public function isAutomaticInverse($isInverse = true, $inverseIndex = null) return $this; } + public function getInverse() + { + if ($this->isAutomaticInverse) { + return $this->origin; + } + + return $this->inverted; + } + /** * Sets whether affected entries will be updated when deleting related entries. * diff --git a/src/Listeners/EntrySavedListener.php b/src/Listeners/EntrySavedListener.php index 2b5d32e..7115f65 100644 --- a/src/Listeners/EntrySavedListener.php +++ b/src/Listeners/EntrySavedListener.php @@ -31,7 +31,7 @@ public function handle(EntrySaved $event) return; } - if (EventStack::count() > 0 || $this->manager->processor()->isProcessingManyToMany()) { + if (EventStack::count() > 0 || $this->manager->processor()->isProcessingRelationships()) { return; } diff --git a/src/Listeners/EntrySavingListener.php b/src/Listeners/EntrySavingListener.php index 2aee6af..59c4166 100644 --- a/src/Listeners/EntrySavingListener.php +++ b/src/Listeners/EntrySavingListener.php @@ -38,7 +38,7 @@ public function handle(EntrySaving $event) return; } - if (EventStack::count() > 1 || $this->manager->processor()->isProcessingManyToMany()) { + if (EventStack::count() > 1 || $this->manager->processor()->isProcessingRelationships()) { return; } diff --git a/src/Processors/Concerns/ProcessesManyToMany.php b/src/Processors/Concerns/ProcessesManyToMany.php index 5617fde..f3931dd 100644 --- a/src/Processors/Concerns/ProcessesManyToMany.php +++ b/src/Processors/Concerns/ProcessesManyToMany.php @@ -9,7 +9,7 @@ trait ProcessesManyToMany { protected function processManyToMany(ComparisonResult $results, EntryRelationship $relationship) { - $this->processingManyToMany = true; + $this->processingRelationships = true; foreach ($results->added as $addedId) { if (! $this->shouldProcessRelationship($relationship, $addedId)) { @@ -27,6 +27,6 @@ protected function processManyToMany(ComparisonResult $results, EntryRelationshi $this->removeItemFromEntry($relationship, $this->getEffectedEntity($relationship, $removedId)); } - $this->processingManyToMany = false; + $this->processingRelationships = false; } } diff --git a/src/Processors/Concerns/ProcessesOneToMany.php b/src/Processors/Concerns/ProcessesOneToMany.php index daf6e07..babf56b 100644 --- a/src/Processors/Concerns/ProcessesOneToMany.php +++ b/src/Processors/Concerns/ProcessesOneToMany.php @@ -2,6 +2,7 @@ namespace Stillat\Relationships\Processors\Concerns; +use Statamic\Facades\Data; use Stillat\Relationships\Comparisons\ComparisonResult; use Stillat\Relationships\EntryRelationship; @@ -11,12 +12,29 @@ protected function processOneToMany(ComparisonResult $results, EntryRelationship { foreach ($results->removed as $removedId) { if ($this->shouldProcessRelationship($relationship, $removedId)) { + $this->dependencies[] = $removedId; + $this->dependencies[] = $this->getDependency($relationship, $removedId); $this->removeFieldValue($relationship, $this->getEffectedEntity($relationship, $removedId)); } } foreach ($results->added as $addedId) { if ($this->shouldProcessRelationship($relationship, $addedId)) { + $this->dependencies[] = $addedId; + $dependent = Data::find($this->getDependency($relationship, $addedId)); + + if ($dependent !== null && $inverse = $relationship->getInverse()) { + $leftReference = $dependent->get($relationship->leftField); + + if (($key = array_search($addedId, $leftReference)) !== false) { + unset($leftReference[$key]); + + $dependent->set($relationship->leftField, array_values($leftReference)); + + $dependent->saveQuietly(); + } + } + $this->setFieldValue($relationship, $this->getEffectedEntity($relationship, $addedId)); } } diff --git a/src/Processors/RelationshipProcessor.php b/src/Processors/RelationshipProcessor.php index f99fa96..ef11b4b 100644 --- a/src/Processors/RelationshipProcessor.php +++ b/src/Processors/RelationshipProcessor.php @@ -87,11 +87,13 @@ class RelationshipProcessor protected $observed = []; - protected $processingManyToMany = false; + protected $processingRelationships = false; - public function isProcessingManyToMany() + protected $dependencies = []; + + public function isProcessingRelationships() { - return $this->processingManyToMany; + return $this->processingRelationships; } public function setIsDeleting($isDeleting = true) @@ -307,9 +309,11 @@ public function process($relationships) public function processRelationship($relationship, $results) { + $this->processingRelationships = true; UpdatingRelationshipsEvent::dispatch($relationship, $results); if (! $results->hasChanges()) { + $this->processingRelationships = false; UpdatedRelationshipsEvent::dispatch($relationship, $results); return; @@ -326,6 +330,7 @@ public function processRelationship($relationship, $results) } elseif ($relationship->type == EntryRelationship::TYPE_MANY_TO_ONE) { $this->processManyToOne($results, $relationship); } + $this->processingRelationships = false; UpdatedRelationshipsEvent::dispatch($relationship, $results); } @@ -354,6 +359,25 @@ protected function shouldProcessRelationship(EntryRelationship $relationship, $i return true; } + protected function getDependency(EntryRelationship $relationship, $id) + { + $data = null; + + if ($relationship->rightType == 'entry') { + $data = $this->effectedEntries[$id]; + } elseif ($relationship->rightType == 'user') { + $data = $this->effectedUsers[$id]; + } elseif ($relationship->rightType == 'term') { + $data = $this->effectedTerms[$id]; + } + + if ($data === null || ! method_exists($data, 'get')) { + return null; + } + + return $data->get($relationship->rightField); + } + protected function getEffectedEntity(EntryRelationship $relationship, $id) { if ($relationship->rightType == 'entry') { diff --git a/src/RelationshipManager.php b/src/RelationshipManager.php index 973ed91..11c669e 100644 --- a/src/RelationshipManager.php +++ b/src/RelationshipManager.php @@ -125,7 +125,10 @@ private function buildOneToOneRelationships($relationships) $right = $this->getFieldDetails($relationship[1]); $builtRelationships[] = $this->getRelationship($left, $right)->oneToOne(); - $builtRelationships[] = $this->getRelationship($right, $left)->oneToOne()->isAutomaticInverse(); + $builtRelationships[] = $this->getRelationship($right, $left) + ->oneToOne() + ->isAutomaticInverse() + ->withOriginRelationship($builtRelationships[0]); } return new RelationshipProxy($builtRelationships); @@ -150,7 +153,10 @@ private function buildOneToManyRelationships($relationships) $right = $this->getFieldDetails($relationship[1]); $builtRelationships[] = $this->getRelationship($left, $right)->manyToOne(); - $builtRelationships[] = $this->getRelationship($right, $left)->oneToMany()->isAutomaticInverse(); + $builtRelationships[] = $this->getRelationship($right, $left) + ->oneToMany() + ->isAutomaticInverse() + ->withOriginRelationship($builtRelationships[0]); } return new RelationshipProxy($builtRelationships); @@ -175,7 +181,10 @@ private function buildManyToOneRelationships($relationships) $right = $this->getFieldDetails($relationship[1]); $builtRelationships[] = $this->getRelationship($left, $right)->oneToOne(); - $builtRelationships[] = $this->getRelationship($right, $left)->manyToOne()->isAutomaticInverse(); + $builtRelationships[] = $this->getRelationship($right, $left) + ->manyToOne() + ->isAutomaticInverse() + ->withOriginRelationship($builtRelationships[0]); } return new RelationshipProxy($builtRelationships); @@ -254,7 +263,10 @@ private function buildManyToManyRelationships($relationships) $right = $this->getFieldDetails($relationship[1]); $builtRelationships[] = $this->getRelationship($left, $right)->manyToMany(); - $builtRelationships[] = $this->getRelationship($right, $left)->manyToMany()->isAutomaticInverse(); + $builtRelationships[] = $this->getRelationship($right, $left) + ->manyToMany() + ->isAutomaticInverse() + ->withOriginRelationship($builtRelationships[0]); } return new RelationshipProxy($builtRelationships); diff --git a/tests/OneToManyTest.php b/tests/OneToManyTest.php index 9ff102d..e30afa8 100644 --- a/tests/OneToManyTest.php +++ b/tests/OneToManyTest.php @@ -117,4 +117,54 @@ public function test_one_to_many_term_relationships() $this->assertSame([], $this->getTerm('topics-one')->get('posts', [])); $this->assertSame([], $this->getTerm('topics-two')->get('posts', [])); } + + public function test_one_to_many_updates_dependents() + { + Relate::clear(); + Relate::oneToMany( + 'books.author', + 'authors.books' + ); + + Entry::find('books-1')->set('author', 'authors-1')->save(); + Entry::find('books-2')->set('author', 'authors-1')->save(); + Entry::find('books-3')->set('author', 'authors-1')->save(); + + Entry::find('books-4')->set('author', 'authors-1')->save(); + Entry::find('books-5')->set('author', 'authors-2')->save(); + + Entry::find('authors-1')->set('books', [ + 'books-1', + 'books-2', + 'books-3', + ])->save(); + + Entry::find('authors-2')->set('books', [ + 'books-4', + 'books-5', + ])->save(); + + Entry::find('authors-1')->set('books', [ + 'books-1', + 'books-2', + 'books-3', + 'books-4', + ])->save(); + + $this->assertSame(['books-5'], Entry::find('authors-2')->get('books')); + + $this->assertSame([ + 'books-1', + 'books-2', + 'books-3', + 'books-4', + ], Entry::find('authors-1')->get('books')); + + $this->assertSame('authors-1', Entry::find('books-1')->get('author')); + $this->assertSame('authors-1', Entry::find('books-2')->get('author')); + $this->assertSame('authors-1', Entry::find('books-3')->get('author')); + $this->assertSame('authors-1', Entry::find('books-4')->get('author')); + + $this->assertSame('authors-2', Entry::find('books-5')->get('author')); + } } diff --git a/tests/RelationshipTestCase.php b/tests/RelationshipTestCase.php index 1f5ed14..b0381d9 100644 --- a/tests/RelationshipTestCase.php +++ b/tests/RelationshipTestCase.php @@ -157,6 +157,15 @@ protected function createCollectionEntries() [ 'title' => 'Book Two', ], + [ + 'title' => 'Book Three', + ], + [ + 'title' => 'Book Four', + ], + [ + 'title' => 'Book Five', + ], ]); $this->createEntries('conferences', [