CloneService   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 113
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 38
c 3
b 1
f 0
dl 0
loc 113
ccs 39
cts 39
cp 1
rs 10
wmc 9

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A clone() 0 5 1
A getFreshInstance() 0 29 3
A cloneRecursive() 0 16 2
A pushToKeyMap() 0 9 1
A getKeyMap() 0 3 1
1
<?php
2
3
namespace Anfischer\Cloner;
4
5
use Illuminate\Database\Eloquent\Model;
6
use Illuminate\Database\Eloquent\Relations\Pivot;
7
use Illuminate\Support\Arr;
8
use Illuminate\Support\Collection;
9
10
class CloneService implements CloneServiceInterface
11
{
12
    protected $originalKeyToClonedKeyMap;
13
14 34
    public function __construct()
15
    {
16 34
        $this->originalKeyToClonedKeyMap = new Collection();
17
    }
18
19
    /**
20
     * Clones a model and its relationships
21
     *
22
     * @param Model $model
23
     * @return Model
24
     */
25 32
    public function clone(Model $model) : Model
26
    {
27 32
        return $this->cloneRecursive(
28 32
            $this->getFreshInstance($model)
29 32
        )->first();
30
    }
31
32
    /**
33
     * Recursively clones a model and its relationships
34
     *
35
     * @param $model
36
     * @return mixed
37
     */
38 32
    private function cloneRecursive($model)
39
    {
40 32
        Collection::wrap($model)->each(function ($item) {
41 32
            Collection::wrap($item->getRelations())->each(function ($method, $relation) use ($item) {
42 26
                $collection = $this->getFreshInstance($this->cloneRecursive($method), $item);
43
44 26
                $isCollection = $item->getRelation($relation) instanceof Collection;
45
46 26
                $item->setRelation(
47
                    $relation,
48 26
                    $isCollection ? $collection : $collection->first()
49
                );
50
            });
51
        });
52
53 32
        return $model;
54
    }
55
56
    /**
57
     * Gets a fresh cloned instance of the model
58
     * which is stripped of the original models unique attributes
59
     *
60
     * @param object $model
61
     * @param object $parent
62
     * @return Collection
63
     */
64 32
    private function getFreshInstance($model, $parent = null) : Collection
65
    {
66 32
        return Collection::wrap($model)->map(function ($original) use ($parent) {
67 32
            return tap(new $original, function ($instance) use ($original, $parent) {
68
                // Ensure we can get hold of the new ID relative to the original
69 32
                $instance->saved(function () use ($original, $instance) {
70 20
                    $this->pushToKeyMap($original, $instance);
71
                });
72
73 16
                $filter = [
74 32
                    $original->getForeignKey(),
75 32
                    $original->getKeyName(),
76 32
                    $original->getCreatedAtColumn(),
77 32
                    $original->getUpdatedAtColumn(),
78
                ];
79
80 32
                if ($parent && ! is_a($instance, Pivot::class)) {
81 26
                    array_push($filter, $parent->getForeignKey());
82
                }
83
84 32
                $attributes = Arr::except(
85 32
                    $original->getAttributes(),
86
                    $filter
87
                );
88
89 32
                $instance->setRawAttributes($attributes);
90 32
                $instance->setRelations($original->getRelations());
91
92 32
                $instance->setTouchedRelations([]);
93
            });
94
        });
95
    }
96
97
    /**
98
     * Get the key map Collection.
99
     *
100
     * @return Collection
101
     */
102 12
    public function getKeyMap(): Collection
103
    {
104 12
        return $this->originalKeyToClonedKeyMap;
105
    }
106
107
    /**
108
     * Add an old to new object key to the map.
109
     *
110
     * @param Model $original The original model.
111
     * @param Model $cloned The model cloned from the original.
112
     * @return void
113
     */
114 20
    public function pushToKeyMap(Model $original, Model $cloned): void
115
    {
116 20
        $class = get_class($original);
117
118 20
        $this->originalKeyToClonedKeyMap->get($class, function () use ($class) {
119 20
            return tap(new Collection, function ($collection) use ($class) {
120 20
                $this->originalKeyToClonedKeyMap->put($class, $collection);
121
            });
122 20
        })->put($original->getKey(), $cloned->getKey());
123
    }
124
}
125