Issues (590)

src/Prime.php (3 issues)

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
The doc comment class-string<T>|RepositoryInterface<T>|T at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>|RepositoryInterface<T>|T.
Loading history...
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
Documentation Bug introduced by
The doc comment class-string<T>|RepositoryInterface<T>|T at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>|RepositoryInterface<T>|T.
Loading history...
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
Documentation Bug introduced by
The doc comment class-string<T>|RepositoryInterface<T>|T at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>|RepositoryInterface<T>|T.
Loading history...
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