-
Notifications
You must be signed in to change notification settings - Fork 170
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move replication logic to db.replicator
This gives more room for logic and reduces the memory footprint
- Loading branch information
Showing
3 changed files
with
161 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
<?php namespace October\Rain\Database; | ||
|
||
use October\Rain\Support\Arr; | ||
use Illuminate\Database\Eloquent\Model as EloquentModel; | ||
use Illuminate\Database\Eloquent\Collection as CollectionBase; | ||
use Illuminate\Database\Eloquent\Relations\HasOneOrMany; | ||
|
||
/** | ||
* Replicator service to duplicating and replicating model records | ||
*/ | ||
class Replicator | ||
{ | ||
/** | ||
* @var \Model model context | ||
*/ | ||
protected $model; | ||
|
||
/** | ||
* @var bool isDuplicating the record or just returning a non-existing instance | ||
*/ | ||
protected $isDuplicating = false; | ||
|
||
/** | ||
* @var bool isMultisite context | ||
*/ | ||
protected $isMultisite = false; | ||
|
||
/** | ||
* __construct | ||
*/ | ||
public function __construct($model) | ||
{ | ||
$this->model = $model; | ||
$this->isMultisite = $model->isClassInstanceOf(\October\Contracts\Database\MultisiteInterface::class); | ||
} | ||
|
||
/** | ||
* replicate replicates the model into a new, non-existing instance, | ||
* including replicating relations. | ||
* | ||
* @param array|null $except | ||
* @return static | ||
*/ | ||
public function replicate(array $except = null) | ||
{ | ||
$this->isDuplicating = false; | ||
|
||
return $this->replicateRelationsInternal($except); | ||
} | ||
|
||
/** | ||
* duplicate replicates a model with special multisite duplication logic. | ||
* To avoid duplication of has many relations, the logic only propagates relations on | ||
* the parent model since they are shared via site_root_id beyond this point. | ||
* | ||
* @param array|null $except | ||
* @return static | ||
*/ | ||
public function duplicate(array $except = null) | ||
{ | ||
$this->isDuplicating = true; | ||
|
||
return $this->replicateRelationsInternal($except); | ||
} | ||
|
||
/** | ||
* replicateRelationsInternal | ||
*/ | ||
protected function replicateRelationsInternal(array $except = null) | ||
{ | ||
$defaults = [ | ||
$this->model->getKeyName(), | ||
$this->model->getCreatedAtColumn(), | ||
$this->model->getUpdatedAtColumn(), | ||
]; | ||
|
||
if ($this->isMultisite) { | ||
$defaults[] = 'site_root_id'; | ||
} | ||
|
||
$attributes = Arr::except( | ||
$this->model->attributes, | ||
$except ? array_unique(array_merge($except, $defaults)) : $defaults | ||
); | ||
|
||
$instance = $this->model->newReplicationInstance($attributes); | ||
|
||
$definitions = $this->model->getRelationDefinitions(); | ||
|
||
foreach ($definitions as $type => $relations) { | ||
foreach ($relations as $name => $options) { | ||
if ($this->isRelationReplicable($name)) { | ||
$this->replicateRelationInternal($instance->$name(), $this->model->$name); | ||
} | ||
} | ||
} | ||
|
||
return $instance; | ||
} | ||
|
||
/** | ||
* replicateRelationInternal on the model instance with the supplied ones | ||
*/ | ||
protected function replicateRelationInternal($relationObject, $models) | ||
{ | ||
if ($models instanceof CollectionBase) { | ||
$models = $models->all(); | ||
} | ||
elseif ($models instanceof EloquentModel) { | ||
$models = [$models]; | ||
} | ||
else { | ||
$models = (array) $models; | ||
} | ||
|
||
foreach (array_filter($models) as $model) { | ||
if ($relationObject instanceof HasOneOrMany) { | ||
$relationObject->add($model->replicateWithRelations()); | ||
} | ||
else { | ||
$relationObject->add($model); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* isRelationReplicable determines whether the specified relation should be replicated | ||
* when replicateWithRelations() is called instead of save() on the model. Default: true. | ||
*/ | ||
protected function isRelationReplicable(string $name): bool | ||
{ | ||
$relationType = $this->model->getRelationType($name); | ||
if ($relationType === 'morphTo') { | ||
return false; | ||
} | ||
|
||
// Relation is shared via propagation | ||
if ( | ||
!$this->isDuplicating && | ||
$this->isMultisite && | ||
$this->model->isAttributePropagatable($name) | ||
) { | ||
return false; | ||
} | ||
|
||
$definition = $this->model->getRelationDefinition($name); | ||
if (!array_key_exists('replicate', $definition)) { | ||
return true; | ||
} | ||
|
||
return (bool) $definition['replicate']; | ||
} | ||
} |