1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Fousky\Component\iDoklad\Functions; |
4
|
|
|
|
5
|
|
|
use Fousky\Component\iDoklad\Exception\InvalidModelException; |
6
|
|
|
use Fousky\Component\iDoklad\Model\iDokladAbstractModel; |
7
|
|
|
use Fousky\Component\iDoklad\Model\iDokladModelInterface; |
8
|
|
|
use Fousky\Component\iDoklad\UrlExtension\iDokladFilter; |
9
|
|
|
use Fousky\Component\iDoklad\UrlExtension\iDokladPaginator; |
10
|
|
|
use Fousky\Component\iDoklad\UrlExtension\iDokladSortable; |
11
|
|
|
use Psr\Http\Message\ResponseInterface; |
12
|
|
|
use Symfony\Component\OptionsResolver\Exception\ExceptionInterface; |
13
|
|
|
use Symfony\Component\OptionsResolver\OptionsResolver; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* @author Lukáš Brzák <[email protected]> |
17
|
|
|
*/ |
18
|
|
|
abstract class iDokladAbstractFunction implements iDokladFunctionInterface |
19
|
|
|
{ |
20
|
|
|
/** @var ResponseInterface|null $response */ |
21
|
|
|
protected $response; |
22
|
|
|
|
23
|
|
|
/** @var array $config */ |
24
|
|
|
protected $config; |
25
|
|
|
|
26
|
|
|
/** @var null|iDokladFilter $filter */ |
27
|
|
|
protected $filter; |
28
|
|
|
|
29
|
|
|
/** @var null|iDokladPaginator $paginator */ |
30
|
|
|
protected $paginator; |
31
|
|
|
|
32
|
|
|
/** @var null|iDokladSortable $sortable */ |
33
|
|
|
protected $sortable; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @param iDokladPaginator $paginator |
37
|
|
|
* |
38
|
|
|
* @return iDokladAbstractFunction |
39
|
|
|
*/ |
40
|
|
|
public function paginator(iDokladPaginator $paginator): self |
41
|
|
|
{ |
42
|
|
|
$this->setPaginator($paginator); |
43
|
|
|
|
44
|
|
|
return $this; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @param iDokladSortable $sortable |
49
|
|
|
* |
50
|
|
|
* @return iDokladAbstractFunction |
51
|
|
|
*/ |
52
|
|
|
public function sortable(iDokladSortable $sortable): self |
53
|
|
|
{ |
54
|
|
|
$this->setSortable($sortable); |
55
|
|
|
|
56
|
|
|
return $this; |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @param iDokladFilter $filter |
61
|
|
|
* |
62
|
|
|
* @return $this |
63
|
|
|
*/ |
64
|
|
|
public function filter(iDokladFilter $filter) |
65
|
|
|
{ |
66
|
|
|
$this->setFilter($filter); |
67
|
|
|
|
68
|
|
|
return $this; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @return bool |
73
|
|
|
*/ |
74
|
|
|
public function injectAccessTokenToHeaders(): bool |
75
|
|
|
{ |
76
|
|
|
return true; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Handle response from iDoklad and create model instance. |
81
|
|
|
* |
82
|
|
|
* @param ResponseInterface $response |
83
|
|
|
* |
84
|
|
|
* @throws \InvalidArgumentException |
85
|
|
|
* @throws \Fousky\Component\iDoklad\Exception\InvalidResponseException |
86
|
|
|
* @throws \ReflectionException |
87
|
|
|
* |
88
|
|
|
* @return iDokladModelInterface |
89
|
|
|
*/ |
90
|
|
|
public function handleResponse(ResponseInterface $response): iDokladModelInterface |
91
|
|
|
{ |
92
|
|
|
return $this->createModel($this->getModelClass(), $response); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* @param string|iDokladModelInterface $modelClass |
97
|
|
|
* @param ResponseInterface $response |
98
|
|
|
* |
99
|
|
|
* @throws \ReflectionException |
100
|
|
|
* @throws \InvalidArgumentException |
101
|
|
|
* @throws \Fousky\Component\iDoklad\Exception\InvalidResponseException |
102
|
|
|
* |
103
|
|
|
* @return iDokladModelInterface |
104
|
|
|
*/ |
105
|
|
|
protected function createModel($modelClass, ResponseInterface $response): iDokladModelInterface |
106
|
|
|
{ |
107
|
|
|
if (!class_exists($modelClass) || |
108
|
|
|
!(new \ReflectionClass($modelClass))->implementsInterface(iDokladModelInterface::class) |
109
|
|
|
) { |
110
|
|
|
throw new \InvalidArgumentException(sprintf( |
111
|
|
|
'Class %s not exists or does not implements interface %s', |
112
|
|
|
$modelClass, |
113
|
|
|
iDokladModelInterface::class |
114
|
|
|
)); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
return $modelClass::createFromResponse($response); |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* @param iDokladAbstractModel $model |
122
|
|
|
* |
123
|
|
|
* @throws InvalidModelException |
124
|
|
|
*/ |
125
|
|
|
protected function validate(iDokladAbstractModel $model) |
126
|
|
|
{ |
127
|
|
|
try { |
128
|
|
|
$errorList = $model->validate(); |
129
|
|
|
} catch (\Throwable $e) { |
130
|
|
|
throw new InvalidModelException([$e->getMessage()]); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
if (0 === $errorList->count()) { |
134
|
|
|
return; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
$errors = [ |
138
|
|
|
sprintf('Model class %s has following errors:', \get_class($model)), |
139
|
|
|
]; |
140
|
|
|
|
141
|
|
|
foreach ($errorList as $error) { |
142
|
|
|
$errors[] = sprintf('- %s: %s', $error->getPropertyPath(), $error->getMessage()); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
throw new InvalidModelException($errors); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* @param array $config |
150
|
|
|
* |
151
|
|
|
* @throws ExceptionInterface |
152
|
|
|
* |
153
|
|
|
* @return array |
154
|
|
|
*/ |
155
|
|
|
public static function assertConfiguration(array $config): array |
156
|
|
|
{ |
157
|
|
|
return (new OptionsResolver()) |
158
|
|
|
->setDefaults([ |
159
|
|
|
'debug' => false, |
160
|
|
|
'url' => 'https://app.idoklad.cz/developer/api/v2/', |
161
|
|
|
'token_endpoint' => 'https://app.idoklad.cz/identity/server/connect/token', |
162
|
|
|
'scope' => 'idoklad_api', |
163
|
|
|
// iDoklad sends DateTime in UTC, so we tranform to another PHP \DateTimeZone |
164
|
|
|
'timezone' => 'Europe/Prague', |
165
|
|
|
// iDoklad generates invoices in available locales: ['cs-CZ', 'sk-SK', 'de-DE', 'en-US'] |
|
|
|
|
166
|
|
|
'language' => 'cs-CZ', |
167
|
|
|
]) |
168
|
|
|
->setRequired([ |
169
|
|
|
'client_id', |
170
|
|
|
'client_secret', |
171
|
|
|
]) |
172
|
|
|
->setAllowedTypes('debug', ['boolean']) |
173
|
|
|
->setAllowedTypes('url', ['string']) |
174
|
|
|
->setAllowedTypes('client_id', ['string']) |
175
|
|
|
->setAllowedTypes('client_secret', ['string']) |
176
|
|
|
->setAllowedTypes('token_endpoint', ['string']) |
177
|
|
|
->setAllowedTypes('scope', ['string']) |
178
|
|
|
->setAllowedTypes('language', ['string']) |
179
|
|
|
->setAllowedValues('language', ['cs-CZ', 'sk-SK', 'de-DE', 'en-US']) |
180
|
|
|
->resolve($config); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* @param array $config |
185
|
|
|
* |
186
|
|
|
* @return iDokladFunctionInterface |
187
|
|
|
*/ |
188
|
|
|
public function setConfig(array $config): iDokladFunctionInterface |
189
|
|
|
{ |
190
|
|
|
$this->config = $config; |
191
|
|
|
|
192
|
|
|
return $this; |
|
|
|
|
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* @return bool |
197
|
|
|
*/ |
198
|
|
|
public function hasSortable(): bool |
199
|
|
|
{ |
200
|
|
|
return null !== $this->sortable; |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* @throws \RuntimeException |
205
|
|
|
* |
206
|
|
|
* @return iDokladSortable |
207
|
|
|
*/ |
208
|
|
|
public function getSortable(): iDokladSortable |
209
|
|
|
{ |
210
|
|
|
if (null === $this->sortable) { |
211
|
|
|
throw new \RuntimeException(sprintf( |
212
|
|
|
'Function %s does not have iDokladSortable instance.', |
213
|
|
|
__CLASS__ |
214
|
|
|
)); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
return $this->sortable; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* @param null|iDokladSortable $sortable |
222
|
|
|
*/ |
223
|
|
|
public function setSortable(iDokladSortable $sortable = null) |
224
|
|
|
{ |
225
|
|
|
$this->sortable = $sortable; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* @return bool |
230
|
|
|
*/ |
231
|
|
|
public function hasPaginator(): bool |
232
|
|
|
{ |
233
|
|
|
return null !== $this->paginator; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* @throws \RuntimeException |
238
|
|
|
* |
239
|
|
|
* @return iDokladPaginator |
240
|
|
|
*/ |
241
|
|
|
public function getPaginator(): iDokladPaginator |
242
|
|
|
{ |
243
|
|
|
if (null === $this->paginator) { |
244
|
|
|
throw new \RuntimeException(sprintf( |
245
|
|
|
'Function %s does not have iDokladPaginator instance.', |
246
|
|
|
__CLASS__ |
247
|
|
|
)); |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
return $this->paginator; |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* @param null|iDokladPaginator $paginator |
255
|
|
|
*/ |
256
|
|
|
public function setPaginator(iDokladPaginator $paginator = null) |
257
|
|
|
{ |
258
|
|
|
$this->paginator = $paginator; |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* @return bool |
263
|
|
|
*/ |
264
|
|
|
public function hasFilter(): bool |
265
|
|
|
{ |
266
|
|
|
return null !== $this->filter; |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* @throws \RuntimeException |
271
|
|
|
* |
272
|
|
|
* @return iDokladFilter |
273
|
|
|
*/ |
274
|
|
|
public function getFilter(): iDokladFilter |
275
|
|
|
{ |
276
|
|
|
if (null === $this->filter) { |
277
|
|
|
throw new \RuntimeException(sprintf( |
278
|
|
|
'Function %s does not have iDokladFilter instance.', |
279
|
|
|
__CLASS__ |
280
|
|
|
)); |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
return $this->filter; |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
/** |
287
|
|
|
* @param null|iDokladFilter $filter |
288
|
|
|
*/ |
289
|
|
|
public function setFilter(iDokladFilter $filter = null) |
290
|
|
|
{ |
291
|
|
|
$this->filter = $filter; |
292
|
|
|
} |
293
|
|
|
} |
294
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.