Passed
Push — master ( da1b99...92df87 )
by Aleksandr
03:12
created

EntityGnome   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 194
Duplicated Lines 0 %

Test Coverage

Coverage 64.71%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 113
c 1
b 0
f 1
dl 0
loc 194
ccs 77
cts 119
cp 0.6471
rs 10
wmc 29

11 Methods

Rating   Name   Duplication   Size   Complexity  
A checkAttrSetExist() 0 6 2
A save() 0 20 3
A beforeSave() 0 31 4
A find() 0 26 3
A validate() 0 20 4
A getEntity() 0 3 1
A toArray() 0 9 2
A checkDomainExists() 0 7 2
A checkEntityExists() 0 6 2
A delete() 0 33 5
A __construct() 0 3 1
1
<?php
2
/**
3
 * This file is part of the eav package.
4
 *
5
 * @author    Aleksandr Drobotik <[email protected]>
6
 * @copyright 2023 Aleksandr Drobotik
7
 * @license   https://opensource.org/license/mit  The MIT License
8
 */
9
declare(strict_types=1);
10
11
namespace Drobotik\Eav;
12
13
use Drobotik\Eav\Exception\EntityException;
14
use Drobotik\Eav\Model\AttributeSetModel;
15
use Drobotik\Eav\Model\EntityModel;
16
use Drobotik\Eav\Result\Result;
17
use Drobotik\Eav\Trait\SingletonsTrait;
18
use Throwable;
19
20
class EntityGnome
21
{
22
    use SingletonsTrait;
23
24
    private Entity $entity;
25
26
    public function __construct(Entity $entity)
27
    {
28
        $this->entity = $entity;
29
    }
30
31
    public function getEntity(): Entity
32
    {
33
        return $this->entity;
34
    }
35
36
    public function beforeSave(): int
37
    {
38
        $entity = $this->getEntity();
39
        $set = $entity->getAttributeSet();
40
        if (!$entity->hasKey()) {
41
            if (!$entity->hasDomainKey()) {
42
                EntityException::undefinedDomainKey();
43
            }
44
            if (!$set->hasKey()) {
45
                EntityException::undefinedAttributeSetKey();
46
            }
47
            $domainKey = $entity->getDomainKey();
48
            $this->checkDomainExists($domainKey);
49
            $setKey = $set->getKey();
50
            $this->checkAttrSetExist($setKey);
51
            $model = $this->makeEntityModel();
52
            $model->setDomainKey($domainKey);
53
            $model->setAttrSetKey($setKey);
54
            $model->save();
55
            $entity->setKey($model->getKey());
56
57
            return 1;
58
        }
59
        $key = $entity->getKey();
60
        $record = $this->checkEntityExists($key);
61
        $this->checkDomainExists($record->getDomainKey());
62
        $this->checkAttrSetExist($record->getAttrSetKey());
63
        $set->setKey($record->getAttrSetKey());
64
        $entity->setDomainKey($record->getDomainKey());
65
66
        return 2;
67
    }
68
69 3
    public function find(): Result
70
    {
71 3
        $result = new Result();
72
73 3
        $entity = $this->getEntity();
74
75 3
        if (!$entity->hasKey()) {
76 1
            return $result->empty();
77
        }
78 2
        $key = $entity->getKey();
79
80 2
        $model = $this->makeEntityModel();
81 2
        $record = $model->find($key);
82
83 2
        if (is_null($record)) {
84 1
            return $result->notFound();
85
        }
86
87 1
        $entity->setDomainKey($record->getDomainKey());
0 ignored issues
show
Bug introduced by
It seems like $record->getDomainKey() can also be of type Illuminate\Database\Eloq...elations\HasManyThrough and Illuminate\Database\Eloq...Relations\HasOneThrough; however, parameter $key of Drobotik\Eav\Entity::setDomainKey() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

87
        $entity->setDomainKey(/** @scrutinizer ignore-type */ $record->getDomainKey());
Loading history...
88 1
        $set = $entity->getAttributeSet();
89 1
        $set->setKey($record->getAttrSetKey());
0 ignored issues
show
Bug introduced by
It seems like $record->getAttrSetKey() can also be of type Illuminate\Database\Eloq...elations\HasManyThrough and Illuminate\Database\Eloq...Relations\HasOneThrough; however, parameter $key of Drobotik\Eav\AttributeSet::setKey() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

89
        $set->setKey(/** @scrutinizer ignore-type */ $record->getAttrSetKey());
Loading history...
90 1
        $set->fetchContainers();
91
92 1
        $result->found();
93
94 1
        return $result;
95
    }
96
97 14
    public function save(): Result
98
    {
99 14
        $entity = $this->getEntity();
100 14
        $result = new Result();
101 14
        $operationType = $this->beforeSave();
102 7
        $set = $entity->getAttributeSet();
103 7
        $set->fetchContainers();
104 7
        $valueResults = [];
105 7
        foreach ($set->getContainers() as $container) {
106 2
            $attribute = $container->getAttribute();
107 2
            $valueResults[$attribute->getName()] = $container->getStrategy()->save();
108
        }
109 7
        1 == $operationType
110 3
            ? $result->created()
111 4
            : $result->updated();
112 7
        $result->setData($valueResults);
113 7
        $bag = $entity->getBag();
114 7
        $bag->clear();
115
116 7
        return $result;
117
    }
118
119 4
    public function delete()
120
    {
121 4
        $entity = $this->getEntity();
122 4
        $set = $entity->getAttributeSet();
123
124 4
        if (!$entity->hasKey()) {
125 1
            EntityException::undefinedEntityKey();
126
        }
127 3
        if (!$set->hasKey()) {
128 1
            EntityException::undefinedAttributeSetKey();
129
        }
130
131 2
        $result = new Result();
132 2
        $set->fetchContainers();
133 2
        $deleteResults = [];
134 2
        foreach ($set->getContainers() as $container) {
135 1
            $attribute = $container->getAttribute();
136 1
            $deleteResults[$attribute->getName()] = $container->getStrategy()->delete();
137
        }
138 2
        $recordResult = $this->makeEntityModel()->findAndDelete($entity->getKey());
139 2
        if (!$recordResult) {
140 1
            return $result->notDeleted();
141
        }
142
143 1
        $entity->setKey(0);
144 1
        $entity->setDomainKey(0);
145 1
        $set->setKey(0);
146 1
        $set->resetContainers();
147
148 1
        $result->deleted();
149 1
        $result->setData($deleteResults);
150
151 1
        return $result;
152
    }
153
154 2
    public function validate(): Result
155
    {
156 2
        $result = new Result();
157 2
        $result->validationPassed();
158 2
        $entity = $this->getEntity();
159 2
        $set = $entity->getAttributeSet();
160 2
        $set->fetchContainers();
161 2
        $errors = [];
162 2
        foreach ($set->getContainers() as $container) {
163 2
            $validationResult = $container->getValueValidator()->validateField();
164 2
            if (!is_null($validationResult)) {
165 1
                $errors[$container->getAttribute()->getName()] = $validationResult;
166
            }
167
        }
168 2
        if (count($errors) > 0) {
169 1
            $result->validationFails();
170 1
            $result->setData($errors);
171
        }
172
173 2
        return $result;
174
    }
175
176 1
    public function toArray(): array
177
    {
178 1
        $result = [];
179 1
        $entity = $this->getEntity();
180 1
        foreach ($entity->getAttributeSet()->getContainers() as $container) {
181 1
            $result[$container->getAttribute()->getName()] = $container->getValueManager()->getValue();
182
        }
183
184 1
        return $result;
185
    }
186
187
    private function checkEntityExists(int $key): EntityModel
188
    {
189
        try {
190
            return $this->makeEntityModel()->findOrFail($key);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->makeEntityModel()->findOrFail($key) could return the type Illuminate\Database\Eloq...gHasThroughRelationship which is incompatible with the type-hinted return Drobotik\Eav\Model\EntityModel. Consider adding an additional type-check to rule them out.
Loading history...
191
        } catch (Throwable) {
192
            EntityException::entityNotFound();
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return Drobotik\Eav\Model\EntityModel. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
193
        }
194
    }
195
196
    /**
197
     * @throws EntityException
198
     */
199
    private function checkDomainExists(int $key): void
200
    {
201
        $domain = $this->makeDomainModel();
202
        $domain->setKey($key);
203
204
        if ($domain->findMe() === false)
205
            EntityException::domainNotFound();
206
    }
207
208
    private function checkAttrSetExist(int $key): AttributeSetModel
209
    {
210
        try {
211
            return $this->makeAttributeSetModel()->findOrFail($key);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->makeAttrib...del()->findOrFail($key) could return the type Illuminate\Database\Eloq...gHasThroughRelationship which is incompatible with the type-hinted return Drobotik\Eav\Model\AttributeSetModel. Consider adding an additional type-check to rule them out.
Loading history...
212
        } catch (Throwable) {
213
            EntityException::attrSetNotFound();
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return Drobotik\Eav\Model\AttributeSetModel. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
214
        }
215
    }
216
}
217