Passed
Push — main ( af650d...5879cf )
by Diego
03:45
created

RepositoryTrait::updateRelations()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 10
nc 6
nop 1
dl 0
loc 19
rs 9.2222
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Blackmine\Repository;
6
7
use Blackmine\Client\ClientInterface;
8
use Blackmine\Client\Response\ApiResponse;
9
use Blackmine\Collection\IdentityCollection;
10
use Blackmine\Collection\PaginatedCollection;
11
use Blackmine\Exception\Api\AbstractApiException;
12
use Blackmine\Model\AbstractModel;
13
use Blackmine\Model\FetchableInterface;
14
use Blackmine\Tool\Inflect;
15
use Doctrine\Common\Collections\Collection;
16
use JsonException;
17
18
use function is_initialized;
19
20
trait RepositoryTrait
21
{
22
    protected function hydrateRelations(AbstractModel $model): AbstractModel
23
    {
24
        foreach ($this->getFetchRelations() as $relation) {
0 ignored issues
show
Bug introduced by
The method getFetchRelations() does not exist on Blackmine\Repository\RepositoryTrait. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

24
        foreach ($this->/** @scrutinizer ignore-call */ getFetchRelations() as $relation) {
Loading history...
25
            if ($this->isFetchable($relation)) {
26
                $getter = "get" . ucfirst(Inflect::camelize($relation));
27
                $setter = "set" . ucfirst(Inflect::camelize($relation));
28
                if (method_exists($this, $getter) || method_exists($this, "__call")) {
29
                    $collection = $this->$getter($model);
30
                    if ($collection) {
31
                        $model->$setter($collection);
32
                    }
33
                }
34
            }
35
        }
36
37
        return $model;
38
    }
39
40
    protected function updateRelations(AbstractModel $model): AbstractModel
41
    {
42
        foreach ($this->getRelationClassMap() as $relation_name => $relation_class) {
0 ignored issues
show
Bug introduced by
The method getRelationClassMap() does not exist on Blackmine\Repository\RepositoryTrait. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

42
        foreach ($this->/** @scrutinizer ignore-call */ getRelationClassMap() as $relation_name => $relation_class) {
Loading history...
43
            $model_getter = $this->getGetter($relation_name);
44
            $repository_adder = $this->getAdder(Inflect::singularize($relation_name));
45
46
            if (is_initialized($model, $relation_name)) {
47
                $related_collection = $model->$model_getter();
48
                if ($related_collection instanceof Collection) {
49
                    foreach ($related_collection as $related_model) {
50
                        if (!$related_model->isPersisted()) {
51
                            $this->$repository_adder($model, $related_model);
52
                        }
53
                    }
54
                }
55
            }
56
        }
57
58
        return $model;
59
    }
60
61
    protected function isFetchable(string $relation_name): bool
62
    {
63
        $related_class = static::getRelationClassFor($relation_name);
0 ignored issues
show
Bug introduced by
The method getRelationClassFor() does not exist on Blackmine\Repository\RepositoryTrait. Did you maybe mean getRelation()? ( Ignorable by Annotation )

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

63
        /** @scrutinizer ignore-call */ 
64
        $related_class = static::getRelationClassFor($relation_name);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
64
        if (class_exists($related_class)) {
65
            $interfaces = class_implements($related_class);
66
            return is_array($interfaces) && in_array(FetchableInterface::class, $interfaces, true);
67
        }
68
69
        return false;
70
    }
71
72
    /**
73
     * @throws JsonException
74
     * @throws AbstractApiException
75
     */
76
    public function __call(string $method, array $args): mixed
77
    {
78
        if ($this->isRelationGetter($method)) {
79
            return $this->getRelation($method, $args);
80
        }
81
82
        if ($this->isRelationAdder($method)) {
83
            return $this->addRelation($method, $args);
84
        }
85
86
        return null;
87
    }
88
89
    protected function getRelation(string $method, array $args): ?Collection
90
    {
91
        $relation_name = strtolower(Inflect::snakeize(substr($method, 3)));
92
        $relation_class = $this->getRelationClassMap()[$relation_name];
93
94
        if ($args[0] instanceof AbstractModel) {
95
            $endpoint = $this->getEndpoint() . "/" . $args[0]->getId() . "/" . $relation_name . "." . $this->getClient()->getFormat();
0 ignored issues
show
Bug introduced by
The method getId() does not exist on Blackmine\Model\AbstractModel. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

95
            $endpoint = $this->getEndpoint() . "/" . $args[0]->/** @scrutinizer ignore-call */ getId() . "/" . $relation_name . "." . $this->getClient()->getFormat();
Loading history...
Bug introduced by
The method getEndpoint() does not exist on Blackmine\Repository\RepositoryTrait. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

95
            $endpoint = $this->/** @scrutinizer ignore-call */ getEndpoint() . "/" . $args[0]->getId() . "/" . $relation_name . "." . $this->getClient()->getFormat();
Loading history...
96
            $response = $this->getClient()->get($endpoint);
97
98
            if ($response->isSuccess()) {
99
                $collection = $this->initCollectionFromResponse($response);
100
101
                foreach ($response->getData()[$relation_name] as $relation_data) {
102
                    $relation = (new $relation_class())->fromArray($relation_data);
103
                    $collection->add($relation);
104
                }
105
106
                return $collection;
107
            }
108
109
            throw AbstractApiException::fromApiResponse($response);
110
        }
111
112
        return null;
113
    }
114
115
    /**
116
     * @throws JsonException
117
     */
118
    protected function addRelation(string $method, array $args): ?AbstractModel
119
    {
120
        $relation = Inflect::pluralize(strtolower(Inflect::snakeize(substr($method, 3))));
121
        if ($args[0] instanceof AbstractModel && $args[1] instanceof AbstractModel) {
122
            $endpoint = $this->getEndpoint() . "/" . $args[0]->getId() . "/" . $relation . "." . $this->getClient()->getFormat();
123
            $response = $this->getClient()->post($endpoint, json_encode($args[1]->getPayload(), JSON_THROW_ON_ERROR));
124
125
            if ($response->isSuccess()) {
126
                $adder = Inflect::ADDER_PREFIX . Inflect::singularize(Inflect::camelize($relation));
127
                $args[0]->$adder($args[1]);
128
            }
129
            return $args[0];
130
        }
131
132
        return null;
133
134
    }
135
136
137
    protected function isRelationGetter(string $method): bool
138
    {
139
        $relation = strtolower(Inflect::snakeize(substr($method, 3)));
140
        return str_starts_with($method, "get") && array_key_exists($relation, $this->getRelationClassMap());
141
    }
142
143
    protected function isRelationAdder(string $method): bool
144
    {
145
        $relation = strtolower(Inflect::pluralize(Inflect::snakeize(substr($method, 3))));
146
        return str_starts_with($method, "add") && array_key_exists($relation, $this->getRelationClassMap());
147
    }
148
149
    protected function getAdder(string $property): string
150
    {
151
        return "add" . Inflect::camelize($property);
152
    }
153
154
    protected function getGetter(string $property): string
155
    {
156
        return "get" . Inflect::camelize($property);
157
    }
158
159
    protected function initCollectionFromResponse(ApiResponse $response): Collection
160
    {
161
        if ($response->isPaginated()) {
162
            $collection = new PaginatedCollection();
163
            $collection->setLimit($response->getLimit());
0 ignored issues
show
Bug introduced by
It seems like $response->getLimit() can also be of type null; however, parameter $limit of Blackmine\Collection\Pag...dCollection::setLimit() 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

163
            $collection->setLimit(/** @scrutinizer ignore-type */ $response->getLimit());
Loading history...
164
            $collection->setOffset($response->getOffset());
0 ignored issues
show
Bug introduced by
It seems like $response->getOffset() can also be of type null; however, parameter $offset of Blackmine\Collection\Pag...Collection::setOffset() 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

164
            $collection->setOffset(/** @scrutinizer ignore-type */ $response->getOffset());
Loading history...
165
            $collection->setTotalCount($response->getTotalCount());
0 ignored issues
show
Bug introduced by
It seems like $response->getTotalCount() can also be of type null; however, parameter $total_count of Blackmine\Collection\Pag...ection::setTotalCount() 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

165
            $collection->setTotalCount(/** @scrutinizer ignore-type */ $response->getTotalCount());
Loading history...
166
        } else {
167
            $collection = new IdentityCollection();
168
        }
169
170
        return $collection;
171
    }
172
173
    abstract public function getClient(): ClientInterface;
174
}
175