RepositoryTrait::getGetter()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
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) {
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) {
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 = self::getRelationClassFor($relation_name);
64
        if ($related_class !== null && 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();
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
    protected function isRelationGetter(string $method): bool
137
    {
138
        $relation = strtolower(Inflect::snakeize(substr($method, 3)));
139
        return str_starts_with($method, "get") && array_key_exists($relation, $this->getRelationClassMap());
140
    }
141
142
    protected function isRelationAdder(string $method): bool
143
    {
144
        $relation = strtolower(Inflect::pluralize(Inflect::snakeize(substr($method, 3))));
145
        return str_starts_with($method, "add") && array_key_exists($relation, $this->getRelationClassMap());
146
    }
147
148
    protected function getAdder(string $property): string
149
    {
150
        return "add" . Inflect::camelize($property);
151
    }
152
153
    protected function getGetter(string $property): string
154
    {
155
        return "get" . Inflect::camelize($property);
156
    }
157
158
    protected function initCollectionFromResponse(ApiResponse $response): Collection
159
    {
160
        if ($response->isPaginated()) {
161
            $collection = new PaginatedCollection();
162
            $collection->setLimit($response->getLimit());
163
            $collection->setOffset($response->getOffset());
164
            $collection->setTotalCount($response->getTotalCount());
165
        } else {
166
            $collection = new IdentityCollection();
167
        }
168
169
        return $collection;
170
    }
171
172
    abstract public function getClient(): ClientInterface;
173
    abstract public static function getRelationClassFor(string $relation): ?string;
174
    abstract public function getFetchRelations(): array;
175
    abstract public function getRelationClassMap(): array;
176
    abstract public function getEndpoint(): string;
177
}
178