AbstractRepository::get()   A
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 17
nc 6
nop 1
dl 0
loc 30
rs 9.7
c 1
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\ClientOptions;
9
use Blackmine\Client\Client;
10
use Blackmine\Model\AbstractModel;
11
use Blackmine\Model\User\User;
12
use Doctrine\Common\Collections\ArrayCollection;
13
use Error;
14
use JsonException;
15
use Blackmine\Exception\Api\AbstractApiException;
16
use Blackmine\Exception\InvalidModelException;
17
use Blackmine\Exception\Api\EntityNotFoundException;
18
19
abstract class AbstractRepository implements RepositoryInterface
20
{
21
    use RepositoryTrait;
22
    use SearchableTrait;
23
24
    protected static array $relation_class_map = [];
25
26
    public function __construct(
27
        protected Client $client,
28
        protected array $options = [
29
            ClientOptions::CLIENT_OPTION_REQUEST_HEADERS => []
30
        ]
31
    ) {
32
        $this->reset();
33
    }
34
35
    public function getOptions(): array
36
    {
37
        return $this->options;
38
    }
39
40
    public function getClient(): ClientInterface
41
    {
42
        return $this->client;
43
    }
44
45
    public function actingAs(string | User $user): self
46
    {
47
        if ($user instanceof User) {
48
            $this->options[ClientOptions::CLIENT_OPTION_REQUEST_HEADERS][ClientOptions::REDMINE_IMPERSONATE_HEADER] =
49
                $user->getLogin();
50
        } else {
51
            $this->options[ClientOptions::CLIENT_OPTION_REQUEST_HEADERS][ClientOptions::REDMINE_IMPERSONATE_HEADER] =
52
                $user;
53
        }
54
55
        return $this;
56
    }
57
58
    /**
59
     * @throws JsonException
60
     * @throws EntityNotFoundException
61
     * @throws AbstractApiException
62
     */
63
    public function get(mixed $id): ?AbstractModel
64
    {
65
        $params = [];
66
        $endpoint_url = $this->getEndpoint() . "/" . $id . "." . $this->client->getFormat();
67
68
        if (!empty($this->getFetchRelations())) {
69
            $params["include"] = implode(",", $this->getFetchRelations());
70
        }
71
72
        $api_response = $this->client->get(
73
            endpoint: $this->constructEndpointUrl($endpoint_url, $params),
74
            headers: $this->options[ClientOptions::CLIENT_OPTION_REQUEST_HEADERS] ?? []
75
        );
76
77
        if ($api_response->isSuccess()) {
78
            $model_class = $this->getModelClass();
79
            $model = new $model_class();
80
            $model_data = $api_response->getData()[$model->getEntityName()] ?? null;
81
82
            if ($model_data) {
83
                $model->fromArray($model_data);
84
                $this->hydrateRelations($model);
85
86
                return $model;
87
            }
88
89
            throw new EntityNotFoundException();
90
        }
91
92
        throw AbstractApiException::fromApiResponse($api_response);
93
    }
94
95
    /**
96
     * @throws JsonException
97
     */
98
    public function all(?string $endpoint = null): ArrayCollection
99
    {
100
        $ret = new ArrayCollection();
101
102
        $api_endpoint = $endpoint ?? $this->getEndpoint();
103
104
        $api_response = $this->client->get(
105
            endpoint: $api_endpoint . "." . $this->client->getFormat(),
106
            headers: $this->options[ClientOptions::CLIENT_OPTION_REQUEST_HEADERS] ?? []
107
        );
108
        if (isset($api_response->getData()[static::API_ROOT])) {
0 ignored issues
show
Bug introduced by
The constant Blackmine\Repository\AbstractRepository::API_ROOT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
109
            $ret = $this->getCollection($api_response->getData()[static::API_ROOT]);
110
        }
111
112
        return $ret;
113
    }
114
115
    /**
116
     * @throws JsonException
117
     * @throws InvalidModelException
118
     * @throws AbstractApiException
119
     */
120
    public function create(AbstractModel $model): ?AbstractModel
121
    {
122
        $model_class = $this->getModelClass();
123
        if (!$model instanceof $model_class) {
124
            throw new InvalidModelException(
125
                'Wrong model class for ' . $this->getEndpoint() . " api. Expected " . $this->getModelClass()
126
            );
127
        }
128
129
        $api_response = $this->client->post(
130
            endpoint: $this->getEndpoint() . "." . $this->client->getFormat(),
131
            body: json_encode($model->getPayload(), JSON_THROW_ON_ERROR),
132
            headers: $this->options[ClientOptions::CLIENT_OPTION_REQUEST_HEADERS] ?? []
133
        );
134
135
        if ($api_response->isSuccess()) {
136
            $model_data = $api_response->getData()[$model->getEntityName()] ?? null;
137
138
            if ($model_data) {
139
                $model->fromArray($model_data);
140
                $this->updateRelations($model);
141
142
                return $model;
143
            }
144
        }
145
146
        throw AbstractApiException::fromApiResponse($api_response);
147
    }
148
149
    /**
150
     * @throws JsonException
151
     * @throws InvalidModelException
152
     * @throws AbstractApiException
153
     */
154
    public function update(AbstractModel $model): ?AbstractModel
155
    {
156
        $model_class = $this->getModelClass();
157
        if (!$model instanceof $model_class) {
158
            throw new InvalidModelException(
159
                'Wrong model class for ' . $this->getEndpoint() . " api. Expected " . $this->getModelClass()
160
            );
161
        }
162
163
        $this->updateRelations($model);
164
165
        $api_response = $this->client->put(
166
            endpoint: $this->getEndpoint() . "/" . $model->getId() . "." . $this->client->getFormat(),
167
            body: json_encode($model->getPayload(), JSON_THROW_ON_ERROR),
168
            headers: $this->options[ClientOptions::CLIENT_OPTION_REQUEST_HEADERS] ?? []
169
        );
170
171
        if ($api_response->isSuccess()) {
172
            return $model;
173
        }
174
175
        throw AbstractApiException::fromApiResponse($api_response);
176
    }
177
178
    /**
179
     * @throws AbstractApiException
180
     * @throws InvalidModelException
181
     * @throws JsonException
182
     */
183
    public function delete(AbstractModel $model): void
184
    {
185
        $model_class = $this->getModelClass();
186
        if (!$model instanceof $model_class) {
187
            throw new InvalidModelException(
188
                'Wrong model class for ' . $this->getEndpoint() . " api. Expected " . $this->getModelClass()
189
            );
190
        }
191
192
        $endpoint_url = $this->getEndpoint() . "/" . $model->getId() . "." . $this->client->getFormat();
193
        $api_response = $this->client->delete(
194
            endpoint: $endpoint_url,
195
            headers: $this->options[ClientOptions::CLIENT_OPTION_REQUEST_HEADERS] ?? []
196
        );
197
198
        if (!$api_response->isSuccess()) {
199
            throw AbstractApiException::fromApiResponse($api_response);
200
        }
201
    }
202
203
    /**
204
     * @throws JsonException
205
     */
206
    public function search(): ArrayCollection
207
    {
208
        return $this->doSearch();
209
    }
210
211
    public static function getRelationClassFor(string $relation): ?string
212
    {
213
        return static::$relation_class_map[$relation] ?? null;
214
    }
215
216
    public function getEndpoint(): string
217
    {
218
        if (defined('static::API_ROOT')) {
219
            return static::API_ROOT;
0 ignored issues
show
Bug introduced by
The constant Blackmine\Repository\AbstractRepository::API_ROOT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
220
        }
221
222
        throw new Error('Mandatory constant API_ROOT not defined in class: ' . get_class($this));
223
    }
224
225
    public function getAllowedFilters(): array
226
    {
227
        return static::$allowed_filters;
228
    }
229
230
    public function getRelationClassMap(): array
231
    {
232
        return static::$relation_class_map;
233
    }
234
235
    public function getFetchRelations(): array
236
    {
237
        return $this->fetch_relations;
238
    }
239
240
    public function addRelationToFetch(string $relation): void
241
    {
242
        if (array_key_exists($relation, $this->getRelationClassMap())) {
243
            $this->fetch_relations[] = $relation;
244
        }
245
    }
246
247
    public function constructEndpointUrl(string $url, array $params): string
248
    {
249
        if (empty($params)) {
250
            return $url;
251
        }
252
253
        return $url . '?' . preg_replace(
254
            '/%5B[0-9]+%5D/simU',
255
            '%5B%5D',
256
            http_build_query($params)
257
        );
258
    }
259
260
    abstract public function getModelClass(): string;
261
}
262