Skip to content

Commit

Permalink
Detect and preserve shared multisite relationships
Browse files Browse the repository at this point in the history
  • Loading branch information
daftspunk committed Feb 20, 2024
1 parent cdf435a commit c6edb41
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 22 deletions.
51 changes: 30 additions & 21 deletions src/Database/Concerns/HasRelationships.php
Original file line number Diff line number Diff line change
Expand Up @@ -876,42 +876,51 @@ protected function setRelationSimpleValue($relationName, $value)
}

/**
* performDeleteOnRelations locates relations with delete flag and cascades
* the delete event.
* performDeleteOnRelations locates relations with delete flag and cascades the
* delete event. This is called before the parent model is deleted. This method
* checks in with the Multisite trait to preserve shared relations.
*
* @see \October\Rain\Database\Traits\Multisite::canDeleteMultisiteRelation
*/
protected function performDeleteOnRelations()
{
$definitions = $this->getRelationDefinitions();
$useMultisite = $this->isClassInstanceOf(\October\Contracts\Database\MultisiteInterface::class) && $this->isMultisiteEnabled();

foreach ($definitions as $type => $relations) {
// Hard 'delete' definition
foreach ($relations as $name => $options) {
if (!Arr::get($options, 'delete', false)) {
continue;
}

if (!$relation = $this->{$name}) {
// Detect and preserve shared multisite relationships
if ($useMultisite && !$this->canDeleteMultisiteRelation($name, $type)) {
continue;
}

if ($relation instanceof EloquentModel) {
$relation->forceDelete();
}
elseif ($relation instanceof CollectionBase) {
$relation->each(function ($model) {
$model->forceDelete();
});
}
}

// Belongs-To-Many should clean up after itself by default
if ($type === 'belongsToMany') {
foreach ($relations as $name => $options) {
// Belongs-To-Many should clean up after itself by default
if ($type === 'belongsToMany') {
if (!Arr::get($options, 'detach', true)) {
return;
}

$this->{$name}()->detach();
}
// Hard 'delete' definition
else {
if (!Arr::get($options, 'delete', false)) {
continue;
}

if (!$relation = $this->{$name}) {
continue;
}

if ($relation instanceof EloquentModel) {
$relation->forceDelete();
}
elseif ($relation instanceof CollectionBase) {
$relation->each(function ($model) {
$model->forceDelete();
});
}
}
}
}
}
Expand Down
26 changes: 25 additions & 1 deletion src/Database/Traits/Multisite.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,31 @@ protected function defineMultisiteRelations()
}

/**
* defineMultisiteRelation
* canDeleteMultisiteRelation checks if a relation has the potential to be shared with
* the current model. If there are 2 or more records in existence, then this method
* will prevent the cascading deletion of relations.
*
* @see \October\Rain\Database\Concerns\HasRelationships::performDeleteOnRelations
*/
public function canDeleteMultisiteRelation($name, $type = null): bool
{
if ($type === null) {
$type = $this->getRelationType($name);
}

if (!in_array($type, ['belongsToMany', 'belongsTo', 'hasOne', 'hasMany', 'attachOne', 'attachMany'])) {
return false;
}

// The current record counts for one so halt if we find more
return !($this->newOtherSiteQuery()->count() > 1);
}

/**
* defineMultisiteRelation will modify defined relations on this model so they share
* their association using the shared identifier (`site_root_id`). Only these relation
* types support relation sharing: `belongsToMany`, `belongsTo`, `hasOne`, `hasMany`,
* `attachOne`, `attachMany`.
*/
protected function defineMultisiteRelation($name, $type = null)
{
Expand Down

0 comments on commit c6edb41

Please sign in to comment.