1 | <?php |
||
2 | |||
3 | namespace Bdf\Prime; |
||
4 | |||
5 | use Bdf\Prime\Connection\Configuration\ConfigurationResolver; |
||
6 | use Bdf\Prime\Connection\ConnectionInterface; |
||
7 | use Bdf\Prime\Connection\ConnectionRegistry; |
||
8 | use Bdf\Prime\Connection\Factory\ChainFactory; |
||
9 | use Bdf\Prime\Connection\Factory\ConnectionFactory; |
||
10 | use Bdf\Prime\Connection\Factory\MasterSlaveConnectionFactory; |
||
11 | use Bdf\Prime\Connection\Factory\ShardingConnectionFactory; |
||
12 | use Bdf\Prime\Exception\PrimeException; |
||
13 | use Bdf\Prime\Mapper\MapperFactory; |
||
14 | use Bdf\Prime\Repository\RepositoryInterface; |
||
15 | use Psr\Container\ContainerInterface; |
||
16 | use RuntimeException; |
||
17 | |||
18 | /** |
||
19 | * Prime |
||
20 | * |
||
21 | * Usefull facade to manipulate repositories |
||
22 | * Allow user to create, drop, truncate repositories |
||
23 | * Allow user to find, insert entities |
||
24 | */ |
||
25 | class Prime |
||
26 | { |
||
27 | /** |
||
28 | * @var mixed |
||
29 | */ |
||
30 | protected static $config; |
||
31 | |||
32 | /** |
||
33 | * @var ServiceLocator |
||
34 | */ |
||
35 | protected static $serviceLocator; |
||
36 | |||
37 | |||
38 | /** |
||
39 | * Configure the locator |
||
40 | * |
||
41 | * @param array|ContainerInterface|ServiceLocator $config |
||
42 | * |
||
43 | * @return void |
||
44 | */ |
||
45 | 368 | public static function configure($config): void |
|
46 | { |
||
47 | 368 | if ($config instanceof ServiceLocator) { |
|
48 | 1 | static::$config = null; |
|
49 | 1 | static::$serviceLocator = $config; |
|
50 | } else { |
||
51 | 367 | static::$config = $config; |
|
52 | 367 | static::$serviceLocator = null; |
|
53 | } |
||
54 | } |
||
55 | |||
56 | /** |
||
57 | * Check whether prime is configured |
||
58 | * |
||
59 | * @return bool |
||
60 | */ |
||
61 | 1650 | public static function isConfigured() |
|
62 | { |
||
63 | 1650 | return static::$config !== null; |
|
64 | } |
||
65 | |||
66 | // |
||
67 | //--------- repository |
||
68 | // |
||
69 | |||
70 | /** |
||
71 | * Get a repository |
||
72 | * |
||
73 | * @param class-string<T>|RepositoryInterface<T>|T $repository |
||
0 ignored issues
–
show
Documentation
Bug
introduced
by
![]() |
|||
74 | * |
||
75 | * @return RepositoryInterface<T>|null |
||
76 | * |
||
77 | * @template T as object |
||
78 | */ |
||
79 | 975 | public static function repository($repository) |
|
80 | { |
||
81 | 975 | if ($repository instanceof RepositoryInterface) { |
|
82 | /** @var RepositoryInterface<T> $repository */ |
||
83 | 6 | return $repository; |
|
84 | } |
||
85 | |||
86 | 975 | return static::service()->repository($repository); |
|
87 | } |
||
88 | |||
89 | /** |
||
90 | * Create repositories |
||
91 | * |
||
92 | * @param string|array|RepositoryInterface $repositories |
||
93 | * @param boolean $force @see EntityRepository::schema |
||
94 | * |
||
95 | * @throws PrimeException |
||
96 | * |
||
97 | * @return void |
||
98 | */ |
||
99 | 1149 | public static function create($repositories, $force = false): void |
|
100 | { |
||
101 | 1149 | static::callSchemaResolverMethod('migrate', $repositories, $force); |
|
102 | } |
||
103 | |||
104 | /** |
||
105 | * Drop repositories |
||
106 | * |
||
107 | * @param string|array|RepositoryInterface $repositories |
||
108 | * @param boolean $force @see EntityRepository::schema |
||
109 | * |
||
110 | * @throws PrimeException |
||
111 | * |
||
112 | * @return void |
||
113 | */ |
||
114 | 1134 | public static function drop($repositories, $force = false): void |
|
115 | { |
||
116 | 1134 | static::callSchemaResolverMethod('drop', $repositories, $force); |
|
117 | } |
||
118 | |||
119 | /** |
||
120 | * Truncate repositories |
||
121 | * |
||
122 | * @param string|array|RepositoryInterface $repositories |
||
123 | * @param boolean $force @see EntityRepository::schema |
||
124 | * |
||
125 | * @throws PrimeException |
||
126 | * |
||
127 | * @return void |
||
128 | */ |
||
129 | 1 | public static function truncate($repositories, $force = false): void |
|
130 | { |
||
131 | 1 | static::callSchemaResolverMethod('truncate', $repositories, $force); |
|
132 | } |
||
133 | |||
134 | /** |
||
135 | * Call schema resolver method |
||
136 | * |
||
137 | * @param string $method |
||
138 | * @param mixed $repositories |
||
139 | * @param boolean $force |
||
140 | * |
||
141 | * @throws PrimeException |
||
142 | * |
||
143 | * @return void |
||
144 | */ |
||
145 | 1162 | protected static function callSchemaResolverMethod($method, $repositories, $force): void |
|
146 | { |
||
147 | 1162 | if (!is_array($repositories)) { |
|
148 | 8 | $repositories = [$repositories]; |
|
149 | } |
||
150 | |||
151 | 1162 | foreach ($repositories as $repository) { |
|
152 | 856 | static::repository($repository)->schema($force)->$method(); |
|
153 | } |
||
154 | } |
||
155 | |||
156 | // |
||
157 | //--------- entities |
||
158 | // |
||
159 | |||
160 | /** |
||
161 | * Push multiple entities in repository |
||
162 | * launch replace method from repository |
||
163 | * |
||
164 | * User can add |
||
165 | * * entity object |
||
166 | * * collection of entity object |
||
167 | * * an array of entity attributes |
||
168 | * |
||
169 | * <code> |
||
170 | * Prime::push(new EntityClass()); |
||
171 | * Prime::push([new EntityClass()]); |
||
172 | * Prime::push($repository, ['id' => '...']); |
||
173 | * Prime::push('EntityClass', ['id' => '...']); |
||
174 | * Prime::push('EntityClass', [['id' => '...']]); |
||
175 | * </code> |
||
176 | * |
||
177 | * @param mixed $repositoryName |
||
178 | * @param mixed $entities |
||
179 | * |
||
180 | * @throws PrimeException |
||
181 | * |
||
182 | * @return void |
||
183 | */ |
||
184 | 43 | public static function push($repositoryName, $entities = null): void |
|
185 | { |
||
186 | 43 | static::callRepositoryMethod('replace', $repositoryName, $entities); |
|
187 | } |
||
188 | |||
189 | /** |
||
190 | * Save multiple entities in repository |
||
191 | * launch save method from repository |
||
192 | * |
||
193 | * User can add |
||
194 | * * entity object |
||
195 | * * collection of entity object |
||
196 | * * an array of entity attributes |
||
197 | * |
||
198 | * <code> |
||
199 | * Prime::save(new EntityClass()); |
||
200 | * Prime::save([new EntityClass()]); |
||
201 | * Prime::save($repository, ['id' => '...']); |
||
202 | * Prime::save('EntityClass', ['id' => '...']); |
||
203 | * Prime::save('EntityClass', [['id' => '...']]); |
||
204 | * </code> |
||
205 | * |
||
206 | * @param mixed $repositoryName |
||
207 | * @param mixed $entities |
||
208 | * |
||
209 | * @throws PrimeException |
||
210 | * |
||
211 | * @return void |
||
212 | */ |
||
213 | 3 | public static function save($repositoryName, $entities = null): void |
|
214 | { |
||
215 | 3 | static::callRepositoryMethod('save', $repositoryName, $entities); |
|
216 | } |
||
217 | |||
218 | /** |
||
219 | * Remove multiple entities in repository |
||
220 | * |
||
221 | * User can add |
||
222 | * * entity object |
||
223 | * * collection of entity object |
||
224 | * * an array of entity attributes |
||
225 | * |
||
226 | * <code> |
||
227 | * Prime::remove(new EntityClass()); |
||
228 | * Prime::remove([new EntityClass()]); |
||
229 | * Prime::remove($repository, ['id' => '...']); |
||
230 | * Prime::remove('EntityClass', ['id' => '...']); |
||
231 | * Prime::remove('EntityClass', [['id' => '...']]); |
||
232 | * </code> |
||
233 | * |
||
234 | * @param mixed $repositoryName |
||
235 | * @param mixed $entities |
||
236 | * |
||
237 | * @throws PrimeException |
||
238 | * |
||
239 | * @return void |
||
240 | */ |
||
241 | 1 | public static function remove($repositoryName, $entities = null): void |
|
242 | { |
||
243 | 1 | static::callRepositoryMethod('delete', $repositoryName, $entities); |
|
244 | } |
||
245 | |||
246 | /** |
||
247 | * Call repository method for entities |
||
248 | * |
||
249 | * @param string $method |
||
250 | * @param mixed $repositoryName |
||
251 | * @param mixed $entities |
||
252 | * |
||
253 | * @throws PrimeException |
||
254 | * |
||
255 | * @return void |
||
256 | */ |
||
257 | 46 | protected static function callRepositoryMethod($method, $repositoryName, $entities): void |
|
258 | { |
||
259 | 46 | if (!is_string($repositoryName) && !$repositoryName instanceof RepositoryInterface) { |
|
260 | 29 | $entities = $repositoryName; |
|
261 | 29 | $repositoryName = null; |
|
262 | } |
||
263 | |||
264 | 46 | if (!is_array($entities) || !isset($entities[0])) { |
|
265 | 46 | $entities = [$entities]; |
|
266 | } |
||
267 | |||
268 | 46 | foreach ($entities as $entity) { |
|
269 | 46 | $repository = static::repository($repositoryName ?: $entity); |
|
270 | |||
271 | 46 | if (is_array($entity)) { |
|
272 | 31 | $entity = $repository->entity($entity); |
|
273 | } |
||
274 | |||
275 | 46 | $repository->$method($entity); |
|
276 | } |
||
277 | } |
||
278 | |||
279 | /** |
||
280 | * Assert that entity exists |
||
281 | * |
||
282 | * @param object $entity |
||
283 | * @param bool $compare Will compare entity with the expected one |
||
284 | * |
||
285 | * @return bool |
||
286 | * |
||
287 | * @throws PrimeException |
||
288 | */ |
||
289 | 13 | public static function exists($entity, $compare = true) |
|
290 | { |
||
291 | 13 | $repository = static::repository($entity); |
|
292 | |||
293 | 13 | $expected = $repository->refresh($entity); |
|
294 | |||
295 | 13 | if ($expected === null) { |
|
296 | return false; |
||
297 | } |
||
298 | |||
299 | 13 | if (!$compare) { |
|
300 | 4 | return true; |
|
301 | } |
||
302 | |||
303 | 10 | return $entity == $expected |
|
304 | 10 | ? true |
|
305 | 10 | : serialize($entity) === serialize($expected); |
|
306 | } |
||
307 | |||
308 | /** |
||
309 | * Find entity |
||
310 | * |
||
311 | * <code> |
||
312 | * Prime::find(new EntityClass()); |
||
313 | * Prime::find('EntityClass', ['id' => '...']); |
||
314 | * Prime::find($repository, ['id' => '...']); |
||
315 | * </code> |
||
316 | * |
||
317 | * @param class-string<T>|RepositoryInterface<T>|T $repositoryName Repo name or Entity instance |
||
0 ignored issues
–
show
|
|||
318 | * @param array|object|null $criteria Array of criteria. Optional if repository name is an object |
||
319 | * |
||
320 | * @return T[]|\Bdf\Prime\Collection\CollectionInterface<T> |
||
321 | * |
||
322 | * @throws PrimeException |
||
323 | * |
||
324 | * @template T as object |
||
325 | */ |
||
326 | 1 | public static function find($repositoryName, $criteria = null) |
|
327 | { |
||
328 | /** @psalm-suppress InvalidArgument */ |
||
329 | 1 | $repository = static::repository($repositoryName); |
|
330 | |||
331 | // if $repositoryName is an entity |
||
332 | 1 | if (is_object($repositoryName) && !$repositoryName instanceof RepositoryInterface) { |
|
333 | $criteria = $repository->mapper()->prepareToRepository($repositoryName); |
||
334 | } |
||
335 | |||
336 | 1 | return $repository->queries()->builder()->where($criteria)->all(); |
|
337 | } |
||
338 | |||
339 | /** |
||
340 | * Find one entity |
||
341 | * |
||
342 | * <code> |
||
343 | * Prime::one(new EntityClass()); |
||
344 | * Prime::one('EntityClass', ['id' => '...']); |
||
345 | * Prime::one($repository, ['id' => '...']); |
||
346 | * </code> |
||
347 | * |
||
348 | * @param class-string<T>|RepositoryInterface<T>|T $repositoryName Repo name or Entity instance |
||
0 ignored issues
–
show
|
|||
349 | * @param array|object|null $criteria Array of criteria. Optional if repository name is an object |
||
350 | * |
||
351 | * @return T|null |
||
352 | * |
||
353 | * @throws PrimeException |
||
354 | * |
||
355 | * @template T as object |
||
356 | */ |
||
357 | 2 | public static function one($repositoryName, $criteria = null) |
|
358 | { |
||
359 | /** @psalm-suppress InvalidArgument */ |
||
360 | 2 | $repository = static::repository($repositoryName); |
|
361 | |||
362 | // if $repositoryName is an entity |
||
363 | 2 | if (is_object($repositoryName) && !$repositoryName instanceof RepositoryInterface) { |
|
364 | 1 | $criteria = $repository->mapper()->prepareToRepository($repositoryName); |
|
365 | } |
||
366 | |||
367 | 2 | return $repository->queries()->builder()->where($criteria)->first(); |
|
368 | } |
||
369 | |||
370 | /** |
||
371 | * Refresh entity from repository |
||
372 | * |
||
373 | * @param T $entity |
||
374 | * @param array $additionalCriteria Criteria to add to primary key |
||
375 | * |
||
376 | * @return T|null New refresh entity |
||
377 | * |
||
378 | * @throws PrimeException |
||
379 | * |
||
380 | * @template T as object |
||
381 | */ |
||
382 | 1 | public static function refresh($entity, $additionalCriteria = []) |
|
383 | { |
||
384 | 1 | $repository = static::repository($entity); |
|
385 | |||
386 | 1 | return $repository->refresh($entity, $additionalCriteria); |
|
387 | } |
||
388 | |||
389 | // |
||
390 | //--------- service |
||
391 | // |
||
392 | |||
393 | /** |
||
394 | * Get active connection from profile name |
||
395 | * |
||
396 | * @param string $name |
||
397 | * |
||
398 | * @return ConnectionInterface |
||
399 | */ |
||
400 | 312 | public static function connection($name = null) |
|
401 | { |
||
402 | 312 | return static::service()->connection($name); |
|
403 | } |
||
404 | |||
405 | /** |
||
406 | * Get service locator |
||
407 | * |
||
408 | * @return ServiceLocator |
||
409 | */ |
||
410 | 1664 | public static function service() |
|
411 | { |
||
412 | 1664 | if (static::$serviceLocator === null) { |
|
413 | 359 | static::initialize(); |
|
414 | } |
||
415 | |||
416 | 1664 | return static::$serviceLocator; |
|
417 | } |
||
418 | |||
419 | /** |
||
420 | * Initializes the service locator |
||
421 | * |
||
422 | * @return void |
||
423 | */ |
||
424 | 359 | protected static function initialize() |
|
425 | { |
||
426 | 359 | if (static::$config instanceof ContainerInterface) { |
|
427 | static::$serviceLocator = static::$config->get('prime'); |
||
428 | |||
429 | return; |
||
430 | } |
||
431 | |||
432 | 359 | if (!is_array(static::$config)) { |
|
433 | throw new RuntimeException('Prime is not configured'); |
||
434 | } |
||
435 | |||
436 | 359 | $factory = new ConnectionFactory(); |
|
437 | 359 | $registry = new ConnectionRegistry( |
|
438 | 359 | static::$config['connection']['config'] ?? [], |
|
439 | 359 | new ChainFactory([ |
|
440 | 359 | new MasterSlaveConnectionFactory($factory), |
|
441 | 359 | new ShardingConnectionFactory($factory), |
|
442 | 359 | $factory, |
|
443 | 359 | ]), |
|
444 | 359 | new ConfigurationResolver([], $configuration = new Configuration()) |
|
445 | 359 | ); |
|
446 | |||
447 | 359 | $mapperFactory = new MapperFactory( |
|
448 | 359 | null, |
|
449 | 359 | static::$config['metadataCache'] ?? null, |
|
450 | 359 | static::$config['resultCache'] ?? null |
|
451 | 359 | ); |
|
452 | |||
453 | 359 | static::$serviceLocator = new ServiceLocator( |
|
454 | 359 | new ConnectionManager($registry), |
|
455 | 359 | $mapperFactory |
|
456 | 359 | ); |
|
457 | |||
458 | 359 | if ($logger = static::$config['logger'] ?? null) { |
|
459 | /** @psalm-suppress InternalMethod */ |
||
460 | $configuration->setSQLLogger($logger); |
||
461 | } |
||
462 | |||
463 | 359 | if ($types = static::$config['types'] ?? null) { |
|
464 | 359 | foreach ($types as $alias => $type) { |
|
465 | 359 | $configuration->getTypes()->register($type, is_string($alias) ? $alias : null); |
|
466 | } |
||
467 | } |
||
468 | |||
469 | 359 | if ($serializer = static::$config['serializer'] ?? null) { |
|
470 | static::$serviceLocator->setSerializer($serializer); |
||
471 | } |
||
472 | } |
||
473 | } |
||
474 |