Passed
Pull Request — master (#3)
by
unknown
02:39
created

CloneService   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 100
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 34
c 2
b 0
f 0
dl 0
loc 100
ccs 26
cts 26
cp 1
rs 10
wmc 9

6 Methods

Rating   Name   Duplication   Size   Complexity  
A clone() 0 5 1
A mapOriginalToClonedKey() 0 5 1
A getFreshInstance() 0 27 3
A getItemOrCollection() 0 3 2
A cloneRecursive() 0 14 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
    /**
15
     * Clones a model and its relationships
16
     *
17
     * @param Model $model
18 24
     * @return Model
19
     */
20 24
    public function clone(Model $model) : Model
21
    {
22
        return $this->cloneRecursive(
23
            $this->getFreshInstance($model)
24
        )->first();
25
    }
26
27
    /**
28
     * Recursively clones a model and its relationships
29 24
     *
30
     * @param $model
31
     * @return mixed
32
     */
33 20
    private function cloneRecursive($model)
34
    {
35 20
        Collection::wrap($model)->each(function ($item) {
36 20
            Collection::wrap($item->getRelations())->each(function ($method, $relation) use ($item) {
37 20
                $collection = $this->getFreshInstance($this->cloneRecursive($method), $item);
38
39 24
                $item->setRelation(
40 24
                    $relation,
41
                    $this->getItemOrCollection($collection)
42 24
                );
43
            });
44
        });
45
46
        return $model;
47
    }
48
49
    /**
50
     * Gets the first item of the collection if the collection
51
     * only contains one item, otherwise it returns the collection
52 20
     *
53
     * @param Collection $collection
54 20
     * @return Collection|mixed
55
     */
56
    private function getItemOrCollection(Collection $collection)
57
    {
58
        return $collection->count() > 1 ? $collection : $collection->first();
59
    }
60
61
    /**
62
     * Gets a fresh cloned instance of the model
63
     * which is stripped of the original models unique attributes
64
     *
65 20
     * @param object $model
66
     * @param object $parent
67
     * @return Collection
68
     */
69
    private function getFreshInstance($model, $parent = null) : Collection
70 20
    {
71 20
        return Collection::wrap($model)->map(function ($original) use ($parent) {
72 20
            return tap(new $original, function ($instance) use ($original, $parent) {
73 20
                // Ensure we can get hold of the new ID relative to the original
74
                $instance->saved(function () use ($original, $instance) {
75
                    $this->mapOriginalToClonedKey($original, $instance);
76 20
                });
77 20
78
                $filter = [
79
                    $original->getForeignKey(),
80 20
                    $original->getKeyName(),
81 20
                    $original->getCreatedAtColumn(),
82 20
                    $original->getUpdatedAtColumn(),
83
                ];
84
85 20
                if ($parent && ! is_a($instance, Pivot::class)) {
86 20
                    array_push($filter, $parent->getForeignKey());
87 20
                }
88 20
89
                $attributes = Arr::except(
90
                    $original->getAttributes(),
91
                    $filter
92
                );
93
94
                $instance->setRawAttributes($attributes);
95
                $instance->setRelations($original->getRelations());
96
            });
97
        });
98
    }
99
100
    public function getKeyMap(): array
101
    {
102
        return $this->originalKeyToClonedKeyMap;
103
    }
104
105
    public function mapOriginalToClonedKey(Model $original, Model $cloned): void
106
    {
107
        $this->originalKeyToClonedKeyMap[get_class($original)][
108
            $original->getKey()
109
        ] = $cloned->getKey();
110
    }
111
}
112