-
Notifications
You must be signed in to change notification settings - Fork 60
feat: fire update events for documents a delete changes through a relationship #981
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
67b0856
464a146
b3e09c2
ded2862
a4e90ac
41e98c5
5e5753c
8fbd1ed
624cd66
36edf12
1f4ad1b
5ad94c6
794f9f5
f7855f2
6c08f53
451f224
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7959,6 +7959,8 @@ public function decreaseDocumentAttribute( | |||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Delete Document | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * Also fires EVENT_DOCUMENT_UPDATE for each document on the other side of a two-way relationship that the delete changed. | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * @param string $collection | ||||||||||||||||||||||||||||||||||||
| * @param string $id | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
|
|
@@ -7973,7 +7975,9 @@ public function deleteDocument(string $collection, string $id): bool | |||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| $collection = $this->silent(fn () => $this->getCollection($collection)); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| $deleted = $this->withTransaction(function () use ($collection, $id, &$document) { | ||||||||||||||||||||||||||||||||||||
| $related = []; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| $deleted = $this->withTransaction(function () use ($collection, $id, &$document, &$related) { | ||||||||||||||||||||||||||||||||||||
| $document = $this->authorization->skip(fn () => $this->silent( | ||||||||||||||||||||||||||||||||||||
| fn () => $this->getDocument($collection->getId(), $id, forUpdate: true) | ||||||||||||||||||||||||||||||||||||
| )); | ||||||||||||||||||||||||||||||||||||
|
|
@@ -8005,7 +8009,9 @@ public function deleteDocument(string $collection, string $id): bool | |||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if ($this->resolveRelationships) { | ||||||||||||||||||||||||||||||||||||
| $document = $this->silent(fn () => $this->deleteDocumentRelationships($collection, $document)); | ||||||||||||||||||||||||||||||||||||
| // A delete made while silenced, like a cascade's, has no one to report to | ||||||||||||||||||||||||||||||||||||
| $report = $this->silentListeners !== null; | ||||||||||||||||||||||||||||||||||||
| $related = $this->silent(fn () => $this->deleteDocumentRelationships($collection, $document, $report)); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| $result = $this->adapter->deleteDocument($collection->getId(), $id); | ||||||||||||||||||||||||||||||||||||
|
|
@@ -8019,6 +8025,10 @@ public function deleteDocument(string $collection, string $id): bool | |||||||||||||||||||||||||||||||||||
| // Purge again after commit so readers cannot re-cache the pre-commit version | ||||||||||||||||||||||||||||||||||||
| $this->purgeCachedDocumentInternal($collection->getId(), $id); | ||||||||||||||||||||||||||||||||||||
| $this->trigger(self::EVENT_DOCUMENT_DELETE, $document); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| foreach ($related as $relation) { | ||||||||||||||||||||||||||||||||||||
| $this->trigger(self::EVENT_DOCUMENT_UPDATE, $relation); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+8028
to
+8031
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: sed -n '1000,1030p' src/Database/Database.php
sed -n '7972,8045p' src/Database/Database.php
sed -n '4980,5030p' tests/e2e/Adapter/Scopes/RelationshipTests.php
git diff 37bdcdc4ebc405257e92d7ae096a2337562b11b3..f7855f2bc5c206337e06fc5e9795e81761c38215 -- src/Database/Database.php | head -170Repository: utopia-php/database Length of output: 14334 🏁 Script executed: set -o pipefail
printf '%s\n' '--- listener API and event references ---'
rg -n -C 5 "function (on|off|trigger)|EVENT_DOCUMENT_UPDATE|EVENT_DOCUMENT_DELETE|silentListeners|listeners\[|throw.*listener|listener.*throw" src tests --glob '*.php' | head -260
printf '%s\n' '--- transaction implementation ---'
rg -n -C 12 "function withTransaction|withTransaction\(" src/Database/Database.php | head -180
printf '%s\n' '--- focused current tests around relationship deletion ---'
sed -n '4920,5105p' tests/e2e/Adapter/Scopes/RelationshipTests.php
printf '%s\n' '--- relevant changed test diff ---'
git diff --unified=35 37bdcdc4ebc405257e92d7ae096a2337562b11b3..f7855f2bc5c206337e06fc5e9795e81761c38215 -- tests src/Database/Database.php | rg -n -C 12 "EVENT_DOCUMENT_UPDATE|related|onRelated|deleteDocument|throw|exception" | head -360
printf '%s\n' '--- full PR commit summaries ---'
git log --oneline --decorate --no-merges 37bdcdc4ebc405257e92d7ae096a2337562b11b3..f7855f2bc5c206337e06fc5e9795e81761c38215
printf '%s\n' '--- relevant commits and changed paths ---'
git log --name-only --format='%h %s' --no-merges 37bdcdc4ebc405257e92d7ae096a2337562b11b3..f7855f2bc5c206337e06fc5e9795e81761c38215 -- src/Database/Database.php tests/e2e/Adapter/Scopes/RelationshipTests.php | head -220Repository: utopia-php/database Length of output: 42698 🤖 get_repo_knowledge executed:
Length of output: 31081 🏁 Script executed: set -o pipefail
for commit in 8fbd1ed2b 794f9f5ad 1f4ad1b36 624cd66f0 5e5753c31 36edf12a9; do
printf '\n--- git show %s --stat ---\n' "$commit"
git show --stat --oneline --no-renames "$commit" -- src/Database/Database.php tests/e2e/Adapter/Scopes/RelationshipTests.php
printf '\n--- git show %s focused patch ---\n' "$commit"
git show --format=fuller --no-ext-diff --unified=18 --no-renames "$commit" -- src/Database/Database.php tests/e2e/Adapter/Scopes/RelationshipTests.php \
| rg -n -C 18 "report|related|onRelated|EVENT_DOCUMENT_UPDATE|trigger|catch|Throwable|exception|deleteDocument" | head -500
done
printf '\n--- current listener API and nearby docs ---\n'
sed -n '790,855p' src/Database/Database.php
sed -n '995,1035p' src/Database/Database.php
printf '\n--- current focused relationship test complete section ---\n'
sed -n '4950,5110p' tests/e2e/Adapter/Scopes/RelationshipTests.phpRepository: utopia-php/database Length of output: 42964 Preserve later peer events when one update listener fails. When the first Suggested fix+ $failure = null;
foreach ($related as $relation) {
- $this->trigger(self::EVENT_DOCUMENT_UPDATE, $relation);
+ try {
+ $this->trigger(self::EVENT_DOCUMENT_UPDATE, $relation);
+ } catch (\Throwable $th) {
+ $failure ??= $th;
+ }
+ }
+
+ if ($failure !== null) {
+ throw $failure;
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| return $deleted; | ||||||||||||||||||||||||||||||||||||
|
|
@@ -8027,15 +8037,19 @@ public function deleteDocument(string $collection, string $id): bool | |||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * @param Document $collection | ||||||||||||||||||||||||||||||||||||
| * @param Document $document | ||||||||||||||||||||||||||||||||||||
| * @return Document | ||||||||||||||||||||||||||||||||||||
| * @param bool $report | ||||||||||||||||||||||||||||||||||||
| * @return array<string, Document> The two-way related documents left changed, when $report is set | ||||||||||||||||||||||||||||||||||||
| * @throws AuthorizationException | ||||||||||||||||||||||||||||||||||||
| * @throws ConflictException | ||||||||||||||||||||||||||||||||||||
| * @throws DatabaseException | ||||||||||||||||||||||||||||||||||||
| * @throws RestrictedException | ||||||||||||||||||||||||||||||||||||
| * @throws StructureException | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| private function deleteDocumentRelationships(Document $collection, Document $document): Document | ||||||||||||||||||||||||||||||||||||
| private function deleteDocumentRelationships(Document $collection, Document $document, bool $report = false): array | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| $related = []; | ||||||||||||||||||||||||||||||||||||
| $cascaded = false; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| $attributes = $collection->getAttribute('attributes', []); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| $relationships = \array_filter($attributes, function ($attribute) { | ||||||||||||||||||||||||||||||||||||
|
|
@@ -8055,14 +8069,33 @@ private function deleteDocumentRelationships(Document $collection, Document $doc | |||||||||||||||||||||||||||||||||||
| $relationship->setAttribute('collection', $collection->getId()); | ||||||||||||||||||||||||||||||||||||
| $relationship->setAttribute('document', $document->getId()); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // This side holds the key, so deleting it takes the reference with it and nothing writes the other side | ||||||||||||||||||||||||||||||||||||
| $holdsKey = | ||||||||||||||||||||||||||||||||||||
| ($relationType === Database::RELATION_ONE_TO_MANY && $side === Database::RELATION_SIDE_CHILD) || | ||||||||||||||||||||||||||||||||||||
| ($relationType === Database::RELATION_MANY_TO_ONE && $side === Database::RELATION_SIDE_PARENT); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Whether the other side survives this delete without being written to | ||||||||||||||||||||||||||||||||||||
| $unwritten = false; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| switch ($onDelete) { | ||||||||||||||||||||||||||||||||||||
| case Database::RELATION_MUTATE_RESTRICT: | ||||||||||||||||||||||||||||||||||||
| $this->deleteRestrict($relatedCollection, $document, $value, $relationType, $twoWay, $twoWayKey, $side); | ||||||||||||||||||||||||||||||||||||
| $unwritten = true; | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
| case Database::RELATION_MUTATE_SET_NULL: | ||||||||||||||||||||||||||||||||||||
| $this->deleteSetNull($collection, $relatedCollection, $document, $relationType, $twoWay, $twoWayKey, $side); | ||||||||||||||||||||||||||||||||||||
| $updated = $this->deleteSetNull($collection, $relatedCollection, $document, $relationType, $twoWay, $twoWayKey, $side); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if ($twoWay) { | ||||||||||||||||||||||||||||||||||||
| foreach ($updated as $relation) { | ||||||||||||||||||||||||||||||||||||
| $related[$relatedCollection->getId() . ':' . $relation->getId()] = $relation; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| $unwritten = $holdsKey || $relationType === Database::RELATION_MANY_TO_MANY; | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
| case Database::RELATION_MUTATE_CASCADE: | ||||||||||||||||||||||||||||||||||||
| $unwritten = $holdsKey || ($relationType === Database::RELATION_MANY_TO_MANY && $side === Database::RELATION_SIDE_CHILD); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| foreach ($this->relationshipDeleteStack as $processedRelationship) { | ||||||||||||||||||||||||||||||||||||
| $existingKey = $processedRelationship['key']; | ||||||||||||||||||||||||||||||||||||
| $existingCollection = $processedRelationship['collection']; | ||||||||||||||||||||||||||||||||||||
|
|
@@ -8110,9 +8143,56 @@ private function deleteDocumentRelationships(Document $collection, Document $doc | |||||||||||||||||||||||||||||||||||
| $this->deleteCascade($collection, $relatedCollection, $document, $key, $value, $relationType, $twoWayKey, $side, $relationship); | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| foreach (\is_array($value) ? $value : [$value] as $relation) { | ||||||||||||||||||||||||||||||||||||
| if (!$relation instanceof Document || $relation->isEmpty()) { | ||||||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if ($onDelete === Database::RELATION_MUTATE_CASCADE && !$unwritten) { | ||||||||||||||||||||||||||||||||||||
| $cascaded = true; | ||||||||||||||||||||||||||||||||||||
| } elseif ($twoWay && $unwritten) { | ||||||||||||||||||||||||||||||||||||
| $related[$relatedCollection->getId() . ':' . $relation->getId()] = $relation; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| return $document; | ||||||||||||||||||||||||||||||||||||
| if (!$report) { | ||||||||||||||||||||||||||||||||||||
| return []; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // A document related to itself is deleted, not changed | ||||||||||||||||||||||||||||||||||||
| unset($related[$collection->getId() . ':' . $document->getId()]); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (!$cascaded || empty($related)) { | ||||||||||||||||||||||||||||||||||||
| return $related; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // A cascade can remove a related document anywhere down its chain, so keep only the ones still there | ||||||||||||||||||||||||||||||||||||
| $idsByCollection = []; | ||||||||||||||||||||||||||||||||||||
| foreach ($related as $relation) { | ||||||||||||||||||||||||||||||||||||
| $idsByCollection[$relation->getCollection()][] = $relation->getId(); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| $existing = []; | ||||||||||||||||||||||||||||||||||||
| foreach ($idsByCollection as $collectionId => $ids) { | ||||||||||||||||||||||||||||||||||||
| foreach (\array_chunk($ids, \max(1, $this->maxQueryValues)) as $chunk) { | ||||||||||||||||||||||||||||||||||||
| $found = $this->authorization->skip(fn () => $this->find($collectionId, [ | ||||||||||||||||||||||||||||||||||||
| Query::equal('$id', $chunk), | ||||||||||||||||||||||||||||||||||||
| Query::select(['$id']), | ||||||||||||||||||||||||||||||||||||
| Query::limit(\count($chunk)), | ||||||||||||||||||||||||||||||||||||
| ])); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| foreach ($found as $doc) { | ||||||||||||||||||||||||||||||||||||
| $existing[$collectionId][$doc->getId()] = true; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| return \array_filter( | ||||||||||||||||||||||||||||||||||||
| $related, | ||||||||||||||||||||||||||||||||||||
| fn (Document $relation) => isset($existing[$relation->getCollection()][$relation->getId()]), | ||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
|
|
@@ -8221,23 +8301,25 @@ private function findReferencingDocuments(Document $relatedCollection, Document | |||||||||||||||||||||||||||||||||||
| * @param bool $twoWay | ||||||||||||||||||||||||||||||||||||
| * @param string $twoWayKey | ||||||||||||||||||||||||||||||||||||
| * @param string $side | ||||||||||||||||||||||||||||||||||||
| * @return void | ||||||||||||||||||||||||||||||||||||
| * @return array<Document> The documents written | ||||||||||||||||||||||||||||||||||||
| * @throws AuthorizationException | ||||||||||||||||||||||||||||||||||||
| * @throws ConflictException | ||||||||||||||||||||||||||||||||||||
| * @throws DatabaseException | ||||||||||||||||||||||||||||||||||||
| * @throws RestrictedException | ||||||||||||||||||||||||||||||||||||
| * @throws StructureException | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| private function deleteSetNull(Document $collection, Document $relatedCollection, Document $document, string $relationType, bool $twoWay, string $twoWayKey, string $side): void | ||||||||||||||||||||||||||||||||||||
| private function deleteSetNull(Document $collection, Document $relatedCollection, Document $document, string $relationType, bool $twoWay, string $twoWayKey, string $side): array | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| $updated = []; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| switch ($relationType) { | ||||||||||||||||||||||||||||||||||||
| case Database::RELATION_ONE_TO_ONE: | ||||||||||||||||||||||||||||||||||||
| if (!$twoWay && $side === Database::RELATION_SIDE_PARENT) { | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Shouldn't need read or update permission to delete | ||||||||||||||||||||||||||||||||||||
| $this->authorization->skip(function () use ($document, $relatedCollection, $twoWayKey) { | ||||||||||||||||||||||||||||||||||||
| $result = $this->authorization->skip(function () use ($document, $relatedCollection, $twoWayKey) { | ||||||||||||||||||||||||||||||||||||
| $related = $this->findOne($relatedCollection->getId(), [ | ||||||||||||||||||||||||||||||||||||
| Query::select(['$id']), | ||||||||||||||||||||||||||||||||||||
| Query::equal($twoWayKey, [$document->getId()]) | ||||||||||||||||||||||||||||||||||||
|
|
@@ -8247,14 +8329,18 @@ private function deleteSetNull(Document $collection, Document $relatedCollection | |||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| $this->skipRelationships(fn () => $this->updateDocument( | ||||||||||||||||||||||||||||||||||||
| return $this->skipRelationships(fn () => $this->updateDocument( | ||||||||||||||||||||||||||||||||||||
| $relatedCollection->getId(), | ||||||||||||||||||||||||||||||||||||
| $related->getId(), | ||||||||||||||||||||||||||||||||||||
| new Document([ | ||||||||||||||||||||||||||||||||||||
| $twoWayKey => null | ||||||||||||||||||||||||||||||||||||
| ]) | ||||||||||||||||||||||||||||||||||||
| )); | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if ($result !== null && !$result->isEmpty()) { | ||||||||||||||||||||||||||||||||||||
| $updated[] = $result; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| case Database::RELATION_ONE_TO_MANY: | ||||||||||||||||||||||||||||||||||||
|
|
@@ -8265,15 +8351,19 @@ private function deleteSetNull(Document $collection, Document $relatedCollection | |||||||||||||||||||||||||||||||||||
| $relations = $this->findReferencingDocuments($relatedCollection, $document, $twoWayKey); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| foreach ($relations as $relation) { | ||||||||||||||||||||||||||||||||||||
| $this->authorization->skip(function () use ($relatedCollection, $twoWayKey, $relation) { | ||||||||||||||||||||||||||||||||||||
| $this->skipRelationships(fn () => $this->updateDocument( | ||||||||||||||||||||||||||||||||||||
| $result = $this->authorization->skip(function () use ($relatedCollection, $twoWayKey, $relation) { | ||||||||||||||||||||||||||||||||||||
| return $this->skipRelationships(fn () => $this->updateDocument( | ||||||||||||||||||||||||||||||||||||
| $relatedCollection->getId(), | ||||||||||||||||||||||||||||||||||||
| $relation->getId(), | ||||||||||||||||||||||||||||||||||||
| new Document([ | ||||||||||||||||||||||||||||||||||||
| $twoWayKey => null | ||||||||||||||||||||||||||||||||||||
| ]), | ||||||||||||||||||||||||||||||||||||
| )); | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (!$result->isEmpty()) { | ||||||||||||||||||||||||||||||||||||
| $updated[] = $result; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
|
|
@@ -8285,15 +8375,19 @@ private function deleteSetNull(Document $collection, Document $relatedCollection | |||||||||||||||||||||||||||||||||||
| $relations = $this->findReferencingDocuments($relatedCollection, $document, $twoWayKey); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| foreach ($relations as $relation) { | ||||||||||||||||||||||||||||||||||||
| $this->authorization->skip(function () use ($relatedCollection, $twoWayKey, $relation) { | ||||||||||||||||||||||||||||||||||||
| $this->skipRelationships(fn () => $this->updateDocument( | ||||||||||||||||||||||||||||||||||||
| $result = $this->authorization->skip(function () use ($relatedCollection, $twoWayKey, $relation) { | ||||||||||||||||||||||||||||||||||||
| return $this->skipRelationships(fn () => $this->updateDocument( | ||||||||||||||||||||||||||||||||||||
| $relatedCollection->getId(), | ||||||||||||||||||||||||||||||||||||
| $relation->getId(), | ||||||||||||||||||||||||||||||||||||
| new Document([ | ||||||||||||||||||||||||||||||||||||
| $twoWayKey => null | ||||||||||||||||||||||||||||||||||||
| ]) | ||||||||||||||||||||||||||||||||||||
| )); | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (!$result->isEmpty()) { | ||||||||||||||||||||||||||||||||||||
| $updated[] = $result; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
|
|
@@ -8314,6 +8408,8 @@ private function deleteSetNull(Document $collection, Document $relatedCollection | |||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| return $updated; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
|
|
@@ -8532,7 +8628,7 @@ public function deleteDocuments( | |||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if ($this->resolveRelationships) { | ||||||||||||||||||||||||||||||||||||
| $document = $this->silent(fn () => $this->deleteDocumentRelationships( | ||||||||||||||||||||||||||||||||||||
| $this->silent(fn () => $this->deleteDocumentRelationships( | ||||||||||||||||||||||||||||||||||||
| $collection, | ||||||||||||||||||||||||||||||||||||
| $document | ||||||||||||||||||||||||||||||||||||
| )); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Do not let a delete-event listener suppress the related report.
If an
EVENT_DOCUMENT_DELETElistener throws during a delete without a caller-owned transaction, the row has already committed, but execution stops before$onRelatedruns. Catch the listener failure or otherwise ensure that committed deletes still deliver their related reports. This listener runs before the new callback dispatch. (raw.githubusercontent.com)🤖 Prompt for AI Agents