Issues (590)

src/Prime.php (4 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);
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Configuration::setSQLLogger() has been deprecated: Use {@see setMiddlewares()} and {@see \Doctrine\DBAL\Logging\Middleware} instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

460
            /** @scrutinizer ignore-deprecated */ $configuration->setSQLLogger($logger);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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