ConfigManager::getProvider()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 4
rs 10
cc 2
eloc 2
nc 2
nop 1
1
<?php
2
3
namespace Oro\Bundle\EntityConfigBundle\Config;
4
5
use Doctrine\ORM\EntityManager;
6
7
use Metadata\MetadataFactory;
8
9
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
10
11
use Oro\Bundle\EntityConfigBundle\Audit\AuditManager;
12
use Oro\Bundle\EntityConfigBundle\Config\Id\ConfigIdInterface;
13
use Oro\Bundle\EntityConfigBundle\Config\Id\EntityConfigId;
14
use Oro\Bundle\EntityConfigBundle\Config\Id\FieldConfigId;
15
use Oro\Bundle\EntityConfigBundle\Entity\ConfigModel;
16
use Oro\Bundle\EntityConfigBundle\Entity\EntityConfigModel;
17
use Oro\Bundle\EntityConfigBundle\Entity\FieldConfigModel;
18
use Oro\Bundle\EntityConfigBundle\Event;
19
use Oro\Bundle\EntityConfigBundle\Event\Events;
20
use Oro\Bundle\EntityConfigBundle\Exception\LogicException;
21
use Oro\Bundle\EntityConfigBundle\Exception\RuntimeException;
22
use Oro\Bundle\EntityConfigBundle\Metadata\EntityMetadata;
23
use Oro\Bundle\EntityConfigBundle\Metadata\FieldMetadata;
24
use Oro\Bundle\EntityConfigBundle\Provider\ConfigProvider;
25
use Oro\Bundle\EntityConfigBundle\Provider\PropertyConfigContainer;
26
use Oro\Bundle\EntityConfigBundle\Tools\ConfigHelper;
27
28
use Oro\Bundle\EntityExtendBundle\EntityConfig\ExtendScope;
29
30
/**
31
 * IMPORTANT: A performance of this class is very crucial. Double check a performance during a refactoring.
32
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
33
 * @SuppressWarnings(PHPMD.ExcessiveClassLength)
34
 */
35
class ConfigManager
36
{
37
    /** @var EventDispatcherInterface */
38
    protected $eventDispatcher;
39
40
    /** @var MetadataFactory */
41
    protected $metadataFactory;
42
43
    /** @var ConfigCache */
44
    protected $cache;
45
46
    /** @var AuditManager */
47
    protected $auditManager;
48
49
    /** @var ConfigModelManager */
50
    protected $modelManager;
51
52
    /** @var ConfigProvider[] */
53
    protected $providers = [];
54
55
    /** @var PropertyConfigContainer[] */
56
    protected $propertyConfigs = [];
57
58
    /** @var array */
59
    protected $originalValues = [];
60
61
    /** @var ConfigInterface[] */
62
    protected $persistConfigs = [];
63
64
    /** @var array */
65
    protected $configChangeSets = [];
66
67
    /**
68
     * @deprecated since 1.9. Should be removed together with deprecated events
69
     * @see Oro\Bundle\EntityConfigBundle\Event\Events
70
     * @var array
71
     */
72
    private $hasListenersCache = [];
73
74
    /**
75
     * @param EventDispatcherInterface $eventDispatcher
76
     * @param MetadataFactory          $metadataFactory
77
     * @param ConfigModelManager       $modelManager
78
     * @param AuditManager             $auditManager
79
     * @param ConfigCache              $cache
80
     */
81
    public function __construct(
82
        EventDispatcherInterface $eventDispatcher,
83
        MetadataFactory $metadataFactory,
84
        ConfigModelManager $modelManager,
85
        AuditManager $auditManager,
86
        ConfigCache $cache
87
    ) {
88
        $this->eventDispatcher = $eventDispatcher;
89
        $this->metadataFactory = $metadataFactory;
90
        $this->modelManager    = $modelManager;
91
        $this->auditManager    = $auditManager;
92
        $this->cache           = $cache;
93
    }
94
95
    /**
96
     * Gets the EntityManager responsible to work with configuration entities
97
     * IMPORTANT: configuration entities may use own entity manager which may be not equal the default entity manager
98
     *
99
     * @return EntityManager
100
     */
101
    public function getEntityManager()
102
    {
103
        return $this->modelManager->getEntityManager();
104
    }
105
106
    /**
107
     * @return ConfigProvider[]
108
     */
109
    public function getProviders()
110
    {
111
        return $this->providers;
112
    }
113
114
    /**
115
     * @param string $scope
116
     *
117
     * @return ConfigProvider|null
118
     */
119
    public function getProvider($scope)
120
    {
121
        return isset($this->providers[$scope]) ? $this->providers[$scope] : null;
122
    }
123
124
    /**
125
     * @param ConfigProvider $provider
126
     */
127
    public function addProvider(ConfigProvider $provider)
128
    {
129
        $this->providers[$provider->getScope()] = $provider;
130
    }
131
132
    /**
133
     * @return EventDispatcherInterface
134
     */
135
    public function getEventDispatcher()
136
    {
137
        return $this->eventDispatcher;
138
    }
139
140
    /**
141
     * Checks whether a database contains all tables required to work with entity configuration data.
142
     *
143
     * @return bool
144
     */
145
    public function isDatabaseReadyToWork()
146
    {
147
        return $this->modelManager->checkDatabase();
148
    }
149
150
    /**
151
     * @param string $className
152
     *
153
     * @return EntityMetadata|null
154
     */
155
    public function getEntityMetadata($className)
156
    {
157
        return class_exists($className)
158
            ? $this->metadataFactory->getMetadataForClass($className)
159
            : null;
160
    }
161
162
    /**
163
     * @param string $className
164
     * @param string $fieldName
165
     *
166
     * @return FieldMetadata|null
167
     */
168
    public function getFieldMetadata($className, $fieldName)
169
    {
170
        $metadata = $this->getEntityMetadata($className);
171
172
        return $metadata && isset($metadata->propertyMetadata[$fieldName])
173
            ? $metadata->propertyMetadata[$fieldName]
174
            : null;
175
    }
176
177
    /**
178
     * @param string      $className
179
     * @param string|null $fieldName
180
     *
181
     * @return int|null
182
     */
183 View Code Duplication
    public function getConfigModelId($className, $fieldName = null)
184
    {
185
        $item = $fieldName
186
            ? $this->findField($className, $fieldName)
187
            : $this->findEntity($className);
188
189
        return null !== $item
190
            ? $item['i']
191
            : null;
192
    }
193
194
    /**
195
     * @param string      $className
196
     * @param string|null $fieldName
197
     *
198
     * @return bool|null
199
     */
200 View Code Duplication
    public function isHiddenModel($className, $fieldName = null)
201
    {
202
        $item = $fieldName
203
            ? $this->findField($className, $fieldName)
204
            : $this->findEntity($className);
205
206
        return null !== $item
207
            ? $item['h']
208
            : null;
209
    }
210
211
    /**
212
     * @param string      $className
213
     * @param string|null $fieldName
214
     *
215
     * @return bool
216
     */
217
    public function hasConfig($className, $fieldName = null)
218
    {
219
        if (!$this->modelManager->checkDatabase()) {
220
            return false;
221
        }
222
223
        return $fieldName
224
            ? $this->isConfigurableField($className, $fieldName)
225
            : $this->isConfigurableEntity($className);
226
    }
227
228
    /**
229
     * @param ConfigIdInterface $configId
230
     *
231
     * @return ConfigInterface
232
     *
233
     * @throws RuntimeException
234
     */
235
    public function getConfig(ConfigIdInterface $configId)
236
    {
237
        $scope = $configId->getScope();
238
        if ($configId instanceof FieldConfigId) {
239
            return $this->getFieldConfig($scope, $configId->getClassName(), $configId->getFieldName());
240
        }
241
242
        $className = $configId->getClassName();
243
        if ($className) {
244
            return $this->getEntityConfig($scope, $className);
245
        }
246
247
        return $this->createEntityConfig($scope);
248
    }
249
250
    /**
251
     * @param string $scope
252
     *
253
     * @return ConfigInterface
254
     */
255
    public function createEntityConfig($scope)
256
    {
257
        return new Config(
258
            new EntityConfigId($scope),
259
            $this->getEntityDefaultValues($scope)
260
        );
261
    }
262
263
    /**
264
     * @param string $scope
265
     * @param string $className
266
     *
267
     * @return ConfigInterface
268
     *
269
     * @throws RuntimeException
270
     */
271
    public function getEntityConfig($scope, $className)
272
    {
273
        $config = $this->cache->getEntityConfig($scope, $className);
274
        if (!$config) {
275
            if (!$this->modelManager->checkDatabase()) {
276
                throw $this->createDatabaseNotSyncedException();
277
            }
278
279
            if (!$this->isConfigurableEntity($className)) {
280
                throw new RuntimeException(sprintf('Entity "%s" is not configurable', $className));
281
            }
282
283
            $config = new Config(
284
                new EntityConfigId($scope, $className),
285
                $this->modelManager->getEntityModel($className)->toArray($scope)
286
            );
287
288
            // put to a cache
289
            $this->cache->saveConfig($config);
290
        }
291
292
        // for calculate change set
293
        $configKey = $scope . '.' . $className;
294
        if (!isset($this->originalValues[$configKey])) {
295
            $this->originalValues[$configKey] = $config->getValues();
296
        }
297
298
        return $config;
299
    }
300
301
    /**
302
     * @param string $scope
303
     * @param string $className
304
     * @param string $fieldName
305
     *
306
     * @return ConfigInterface
307
     *
308
     * @throws RuntimeException
309
     */
310
    public function getFieldConfig($scope, $className, $fieldName)
311
    {
312
        $config = $this->cache->getFieldConfig($scope, $className, $fieldName);
313
        if (!$config) {
314
            if (!$this->modelManager->checkDatabase()) {
315
                throw $this->createDatabaseNotSyncedException();
316
            }
317
318
            if (!$this->isConfigurableEntity($className)) {
319
                throw new RuntimeException(sprintf('Entity "%s" is not configurable', $className));
320
            }
321
            if (!$this->isConfigurableField($className, $fieldName)) {
322
                throw new RuntimeException(sprintf('Field "%s::%s" is not configurable', $className, $fieldName));
323
            }
324
325
            $model = $this->modelManager->getFieldModel($className, $fieldName);
326
            $config = new Config(
327
                new FieldConfigId($scope, $className, $fieldName, $model->getType()),
328
                $model->toArray($scope)
329
            );
330
331
            // put to a cache
332
            $this->cache->saveConfig($config);
333
        }
334
335
        // for calculate change set
336
        $configKey = $scope . '.' . $className . '.' . $fieldName;
337
        if (!isset($this->originalValues[$configKey])) {
338
            $this->originalValues[$configKey] = $config->getValues();
339
        }
340
341
        return $config;
342
    }
343
344
    /**
345
     * Gets configuration data for all configurable entities (if $className is not specified)
346
     * or all configurable fields of the given $className.
347
     *
348
     * @param string      $scope
349
     * @param string|null $className
350
     * @param bool        $withHidden Set true if you need ids of all configurable entities,
351
     *                                including entities marked as ConfigModel::MODE_HIDDEN
352
     *
353
     * @return ConfigInterface[]
354
     */
355
    public function getConfigs($scope, $className = null, $withHidden = false)
356
    {
357
        if ($className) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $className of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
358
            return $this->mapFields(
359
                function ($scope, $class, $field, $type) {
360
                    return $this->getFieldConfig($scope, $class, $field, $type);
0 ignored issues
show
Unused Code introduced by
The call to ConfigManager::getFieldConfig() has too many arguments starting with $type.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
361
                },
362
                $scope,
363
                $className,
364
                $withHidden
365
            );
366
        } else {
367
            return $this->mapEntities(
368
                function ($scope, $class) {
369
                    return $this->getEntityConfig($scope, $class);
370
                },
371
                $scope,
372
                $withHidden
373
            );
374
        }
375
    }
376
377
    /**
378
     * Gets a list of ids for all configurable entities (if $className is not specified)
379
     * or all configurable fields of the given $className.
380
     *
381
     * @param string $scope
382
     * @param string|null $className
383
     * @param bool $withHidden Set true if you need ids of all configurable entities,
384
     *                                including entities marked as ConfigModel::MODE_HIDDEN
385
     *
386
     * @return ConfigIdInterface[]
387
     */
388
    public function getIds($scope, $className = null, $withHidden = false)
389
    {
390
        if ($className) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $className of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
391
            return $this->mapFields(
392
                function ($scope, $class, $field, $type) {
393
                    return new FieldConfigId($scope, $class, $field, $type);
394
                },
395
                $scope,
396
                $className,
397
                $withHidden
398
            );
399
        } else {
400
            return $this->mapEntities(
401
                function ($scope, $class) {
402
                    return new EntityConfigId($scope, $class);
403
                },
404
                $scope,
405
                $withHidden
406
            );
407
        }
408
    }
409
410
    /**
411
     * @param string      $scope
412
     * @param string      $className
413
     * @param string|null $fieldName
414
     *
415
     * @return ConfigIdInterface
416
     */
417
    public function getId($scope, $className, $fieldName = null)
418
    {
419
        if ($fieldName) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fieldName of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
420
            return $this->getFieldConfig($scope, $className, $fieldName)->getId();
421
        } else {
422
            return new EntityConfigId($scope, $className);
423
        }
424
    }
425
426
    /**
427
     * Discards all unsaved changes and clears all related caches.
428
     */
429
    public function clear()
430
    {
431
        $this->clearCache();
432
        $this->clearConfigurableCache();
433
434
        $this->modelManager->clearCache();
435
        $this->getEntityManager()->clear();
436
    }
437
438
    /**
439
     * Removes all configuration data or for the given object (if $configId is specified) from a cache.
440
     *
441
     * @param ConfigIdInterface|null $configId
442
     */
443
    public function clearCache(ConfigIdInterface $configId = null)
444
    {
445
        if ($configId) {
446
            if ($configId instanceof FieldConfigId) {
447
                $this->cache->deleteFieldConfig($configId->getClassName(), $configId->getFieldName());
448
            } else {
449
                $this->cache->deleteEntityConfig($configId->getClassName());
450
            }
451
        } else {
452
            $this->cache->deleteAllConfigs();
453
        }
454
    }
455
456
    /**
457
     * Clears a cache of configurable entity flags
458
     */
459
    public function clearConfigurableCache()
460
    {
461
        $this->modelManager->clearCheckDatabase();
462
        $this->cache->deleteAllConfigurable();
463
    }
464
465
    /**
466
     * Clears entity config model cache.
467
     */
468
    public function clearModelCache()
469
    {
470
        $this->modelManager->clearCache();
471
        $this->cache->deleteAllConfigurable(true);
472
        $this->cache->deleteAllConfigs(true);
473
    }
474
475
    /**
476
     * Removes all entries from all used caches, completely.
477
     */
478
    public function flushAllCaches()
479
    {
480
        //$this->cache->flushAllConfigurable();
481
        //$this->cache->flushAllConfigs();
482
        // @todo temporary solution. 'flush' methods should be used. should be fixed in BAP-9151
483
        $this->cache->deleteAllConfigurable();
484
        $this->cache->deleteAllConfigs();
485
    }
486
487
    /**
488
     * Makes the given configuration object managed and persistent.
489
     *
490
     * @param ConfigInterface $config
491
     */
492
    public function persist(ConfigInterface $config)
493
    {
494
        $configKey = $this->buildConfigKey($config->getId());
495
496
        $this->persistConfigs[$configKey] = $config;
497
    }
498
499
    /**
500
     * Merges configuration data from the given configuration object with existing
501
     * managed configuration object.
502
     *
503
     * @param ConfigInterface $config
504
     */
505
    public function merge(ConfigInterface $config)
506
    {
507
        $this->mergeConfigValues($config, $this->buildConfigKey($config->getId()));
508
    }
509
510
    /**
511
     * Flushes all changes that have been queued up to now to a database.
512
     */
513
    public function flush()
514
    {
515
        /** @var ConfigModel[] $models */
516
        $models = [];
517
        $this->prepareFlush($models);
518
519
        $em  = $this->getEntityManager();
520
        $now = new \DateTime('now', new \DateTimeZone('UTC'));
521
522
        $logEntry = $this->auditManager->buildEntity($this);
523
524
        foreach ($models as $model) {
525
            if (null === $model->getId()) {
526
                $model->setCreated($now);
527
            }
528
            $model->setUpdated($now);
529
            $em->persist($model);
530
        }
531
532
        if (!empty($models)) {
533
            $em->flush();
534
        }
535
536
        if (null !== $logEntry) {
537
            $this->auditManager->save($logEntry);
538
        }
539
540
        // @todo: Should be removed together with deprecated events
541
        if ($this->hasListeners(Events::POST_FLUSH_CONFIG)) {
0 ignored issues
show
Deprecated Code introduced by
The constant Oro\Bundle\EntityConfigB...ents::POST_FLUSH_CONFIG has been deprecated with message: since 1.9. Use POST_FLUSH instead

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

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

Loading history...
542
            $this->eventDispatcher->dispatch(
543
                Events::POST_FLUSH_CONFIG,
0 ignored issues
show
Deprecated Code introduced by
The constant Oro\Bundle\EntityConfigB...ents::POST_FLUSH_CONFIG has been deprecated with message: since 1.9. Use POST_FLUSH instead

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

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

Loading history...
544
                new Event\FlushConfigEvent($models, $this)
0 ignored issues
show
Deprecated Code introduced by
The class Oro\Bundle\EntityConfigB...\Event\FlushConfigEvent has been deprecated with message: since 1.9. Use PostFlushConfigEvent instead

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

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

Loading history...
545
            );
546
        }
547
        $this->eventDispatcher->dispatch(
548
            Events::POST_FLUSH,
549
            new Event\PostFlushConfigEvent($models, $this)
550
        );
551
552
        if (!empty($models)) {
553
            $this->cache->deleteAllConfigurable();
554
        }
555
556
        $this->persistConfigs   = [];
557
        $this->configChangeSets = [];
558
    }
559
560
    /**
561
     * @param array $models
562
     *
563
     * @SuppressWarnings(PHPMD.NPathComplexity)
564
     */
565
    protected function prepareFlush(&$models)
566
    {
567
        $groupedConfigs = [];
568
        foreach ($this->persistConfigs as $config) {
569
            $this->calculateConfigChangeSet($config);
570
571
            // @todo: Should be removed together with deprecated events
572
            if ($this->hasListeners(Events::PRE_PERSIST_CONFIG)) {
0 ignored issues
show
Deprecated Code introduced by
The constant Oro\Bundle\EntityConfigB...nts::PRE_PERSIST_CONFIG has been deprecated with message: since 1.9. Use PRE_FLUSH instead

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

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

Loading history...
573
                $this->eventDispatcher->dispatch(
574
                    Events::PRE_PERSIST_CONFIG,
0 ignored issues
show
Deprecated Code introduced by
The constant Oro\Bundle\EntityConfigB...nts::PRE_PERSIST_CONFIG has been deprecated with message: since 1.9. Use PRE_FLUSH instead

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

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

Loading history...
575
                    new Event\PersistConfigEvent($config, $this)
0 ignored issues
show
Deprecated Code introduced by
The class Oro\Bundle\EntityConfigB...vent\PersistConfigEvent has been deprecated with message: since 1.9. Use PreFlushConfigEvent instead

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

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

Loading history...
576
                );
577
            }
578
579
            $configId = $config->getId();
580
            $modelKey = $configId instanceof FieldConfigId
581
                ? $configId->getClassName() . '.' . $configId->getFieldName()
582
                : $configId->getClassName();
583
584
            $groupedConfigs[$modelKey][$configId->getScope()] = $config;
585
        }
586
587
        /** @var ConfigInterface[] $configs */
588
        foreach ($groupedConfigs as $modelKey => $configs) {
589
            $this->eventDispatcher->dispatch(
590
                Events::PRE_FLUSH,
591
                new Event\PreFlushConfigEvent($configs, $this)
592
            );
593
            foreach ($configs as $scope => $config) {
594
                $configId  = $config->getId();
595
                $className = $configId->getClassName();
596
                $fieldName = $configId instanceof FieldConfigId
597
                    ? $configId->getFieldName()
598
                    : null;
599
600
                if (isset($models[$modelKey])) {
601
                    $model = $models[$modelKey];
602
                } else {
603
                    $model             = null !== $fieldName
604
                        ? $this->modelManager->getFieldModel($className, $fieldName)
605
                        : $this->modelManager->getEntityModel($className);
606
                    $models[$modelKey] = $model;
607
                }
608
609
                $indexedValues = $this->getPropertyConfig($scope)->getIndexedValues(
610
                    null !== $fieldName ? PropertyConfigContainer::TYPE_FIELD : PropertyConfigContainer::TYPE_ENTITY
611
                );
612
                $model->fromArray($scope, $config->getValues(), $indexedValues);
613
614
                if (null !== $fieldName) {
615
                    $this->cache->deleteFieldConfig($className, $fieldName);
616
                } else {
617
                    $this->cache->deleteEntityConfig($className);
618
                }
619
            }
620
        }
621
622
        if (count($this->persistConfigs) !== count($this->configChangeSets)) {
623
            $this->prepareFlush($models);
624
        }
625
    }
626
627
    /**
628
     * @param ConfigInterface $config
629
     */
630
    public function calculateConfigChangeSet(ConfigInterface $config)
631
    {
632
        $configKey = $this->buildConfigKey($config->getId());
633
634
        $diff = isset($this->originalValues[$configKey])
635
            ? $this->getDiff($config->getValues(), $this->originalValues[$configKey])
636
            : $this->getDiff($config->getValues(), []);
637
        if (!empty($diff)) {
638
            $this->configChangeSets[$configKey] = isset($this->configChangeSets[$configKey])
639
                ? array_merge($this->configChangeSets[$configKey], $diff)
640
                : $diff;
641
        } elseif (!isset($this->configChangeSets[$configKey])) {
642
            $this->configChangeSets[$configKey] = [];
643
        }
644
    }
645
646
    /**
647
     * @return ConfigInterface[]
648
     */
649
    public function getUpdateConfig()
650
    {
651
        return array_values($this->persistConfigs);
652
    }
653
654
    /**
655
     * @param ConfigInterface $config
656
     *
657
     * @return array [old_value, new_value] or empty array
658
     */
659
    public function getConfigChangeSet(ConfigInterface $config)
660
    {
661
        $configKey = $this->buildConfigKey($config->getId());
662
663
        return isset($this->configChangeSets[$configKey])
664
            ? $this->configChangeSets[$configKey]
665
            : [];
666
    }
667
668
    /**
669
     * Checks if the configuration model for the given class exists
670
     *
671
     * @param string $className
672
     *
673
     * @return bool
674
     */
675
    public function hasConfigEntityModel($className)
676
    {
677
        return null !== $this->modelManager->findEntityModel($className);
678
    }
679
680
    /**
681
     * Checks if the configuration model for the given field exist
682
     *
683
     * @param string $className
684
     * @param string $fieldName
685
     *
686
     * @return bool
687
     */
688
    public function hasConfigFieldModel($className, $fieldName)
689
    {
690
        return null !== $this->modelManager->findFieldModel($className, $fieldName);
691
    }
692
693
    /**
694
     * Gets a config model for the given entity
695
     *
696
     * @param string $className
697
     *
698
     * @return EntityConfigModel|null
699
     */
700
    public function getConfigEntityModel($className)
701
    {
702
        return $this->modelManager->findEntityModel($className);
703
    }
704
705
    /**
706
     * Gets a config model for the given entity field
707
     *
708
     * @param string $className
709
     * @param string $fieldName
710
     *
711
     * @return FieldConfigModel|null
712
     */
713
    public function getConfigFieldModel($className, $fieldName)
714
    {
715
        return $this->modelManager->findFieldModel($className, $fieldName);
716
    }
717
718
    /**
719
     * @param string|null $className
720
     * @param string|null $mode
721
     *
722
     * @return EntityConfigModel
723
     */
724
    public function createConfigEntityModel($className = null, $mode = null)
725
    {
726
        if (empty($className)) {
727
            $entityModel = $this->modelManager->createEntityModel(
728
                $className,
729
                $mode ?: ConfigModel::MODE_DEFAULT
730
            );
731
        } else {
732
            $entityModel = $this->modelManager->findEntityModel($className);
733
            if (null === $entityModel) {
734
                $metadata = $this->getEntityMetadata($className);
735
                if (!$mode) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $mode of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
736
                    $mode = $metadata && $metadata->mode
0 ignored issues
show
Bug introduced by
The property mode does not seem to exist in Metadata\ClassMetadata.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
737
                        ? $metadata->mode
738
                        : ConfigModel::MODE_DEFAULT;
739
                }
740
                $entityModel = $this->modelManager->createEntityModel($className, $mode);
741
                foreach ($this->providers as $scope => $provider) {
742
                    $configKey = $scope . '.' . $className;
743
                    $config    = new Config(
744
                        new EntityConfigId($scope, $className),
745
                        $this->getEntityDefaultValues($scope, $className, $metadata)
0 ignored issues
show
Bug introduced by
It seems like $metadata defined by $this->getEntityMetadata($className) on line 734 can also be of type object<Metadata\ClassMetadata>; however, Oro\Bundle\EntityConfigB...etEntityDefaultValues() does only seem to accept object<Oro\Bundle\Entity...ta\EntityMetadata>|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
746
                    );
747
                    $this->mergeConfigValues($config, $configKey);
748
749
                    // put a config to a local cache
750
                    $this->cache->saveConfig($config, true);
751
                    // for calculate change set
752
                    if (!isset($this->originalValues[$configKey])) {
753
                        $this->originalValues[$configKey] = $config->getValues();
754
                    }
755
                }
756
                // put "configurable" flag to a local cache
757
                $this->cache->saveConfigurable(true, $className, null, true);
758
                // if needed, update the list of entities in a local cache
759
                $entities = $this->cache->getEntities(true);
760 View Code Duplication
                if (null !== $entities && !isset($entities[$className])) {
761
                    $entities[$className] = [
762
                        'i' => null,
763
                        'h' => $mode === ConfigModel::MODE_HIDDEN
764
                    ];
765
                    $this->cache->saveEntities($entities, true);
766
                }
767
768
                // @todo: Should be removed together with deprecated events
769
                if ($this->hasListeners(Events::NEW_ENTITY_CONFIG)) {
0 ignored issues
show
Deprecated Code introduced by
The constant Oro\Bundle\EntityConfigB...ents::NEW_ENTITY_CONFIG has been deprecated with message: since 1.9. Use CREATE_ENTITY instead

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

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

Loading history...
770
                    $this->eventDispatcher->dispatch(
771
                        Events::NEW_ENTITY_CONFIG,
0 ignored issues
show
Deprecated Code introduced by
The constant Oro\Bundle\EntityConfigB...ents::NEW_ENTITY_CONFIG has been deprecated with message: since 1.9. Use CREATE_ENTITY instead

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

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

Loading history...
772
                        new Event\EntityConfigEvent($className, $this)
773
                    );
774
                }
775
                $this->eventDispatcher->dispatch(
776
                    Events::CREATE_ENTITY,
777
                    new Event\EntityConfigEvent($className, $this)
778
                );
779
            }
780
        }
781
782
        return $entityModel;
783
    }
784
785
    /**
786
     * @param string      $className
787
     * @param string      $fieldName
788
     * @param string      $fieldType
789
     * @param string|null $mode
790
     *
791
     * @return FieldConfigModel
792
     */
793
    public function createConfigFieldModel($className, $fieldName, $fieldType, $mode = null)
794
    {
795
        $fieldModel = $this->modelManager->findFieldModel($className, $fieldName);
796
        if (null === $fieldModel) {
797
            $metadata = $this->getFieldMetadata($className, $fieldName);
798
            if (!$mode) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $mode of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
799
                $mode = $metadata && $metadata->mode
800
                    ? $metadata->mode
801
                    : ConfigModel::MODE_DEFAULT;
802
            }
803
            $fieldModel = $this->modelManager->createFieldModel($className, $fieldName, $fieldType, $mode);
804
            foreach ($this->providers as $scope => $provider) {
805
                $configKey = $scope . '.' . $className . '.' . $fieldName;
806
                $config    = new Config(
807
                    new FieldConfigId($scope, $className, $fieldName, $fieldType),
808
                    $this->getFieldDefaultValues($scope, $className, $fieldName, $fieldType, $metadata)
809
                );
810
                $this->mergeConfigValues($config, $configKey);
811
812
                // put a config to a local cache
813
                $this->cache->saveConfig($config, true);
814
                // for calculate change set
815
                if (!isset($this->originalValues[$configKey])) {
816
                    $this->originalValues[$configKey] = $config->getValues();
817
                }
818
            }
819
            // put "configurable" flag to a local cache
820
            $this->cache->saveConfigurable(true, $className, $fieldName, true);
821
            // if needed, update the list of fields in a local cache
822
            $fields = $this->cache->getFields($className, true);
823 View Code Duplication
            if (null !== $fields && !isset($fields[$fieldName])) {
824
                $fields[$fieldName] = [
825
                    'i' => null,
826
                    'h' => $mode === ConfigModel::MODE_HIDDEN,
827
                    't' => $fieldType,
828
                ];
829
                $this->cache->saveFields($className, $fields, true);
830
            }
831
832
            // @todo: Should be removed together with deprecated events
833
            if ($this->hasListeners(Events::NEW_FIELD_CONFIG)) {
0 ignored issues
show
Deprecated Code introduced by
The constant Oro\Bundle\EntityConfigB...vents::NEW_FIELD_CONFIG has been deprecated with message: since 1.9. Use CREATE_FIELD instead

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

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

Loading history...
834
                $this->eventDispatcher->dispatch(
835
                    Events::NEW_FIELD_CONFIG,
0 ignored issues
show
Deprecated Code introduced by
The constant Oro\Bundle\EntityConfigB...vents::NEW_FIELD_CONFIG has been deprecated with message: since 1.9. Use CREATE_FIELD instead

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

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

Loading history...
836
                    new Event\FieldConfigEvent($className, $fieldName, $this)
837
                );
838
            }
839
            $this->eventDispatcher->dispatch(
840
                Events::CREATE_FIELD,
841
                new Event\FieldConfigEvent($className, $fieldName, $this)
842
            );
843
        }
844
845
        return $fieldModel;
846
    }
847
848
    /**
849
     * @param string $className
850
     * @param bool   $force - if TRUE overwrite existing value from annotation
851
     *
852
     * @TODO: need handling for removed values
853
     */
854
    public function updateConfigEntityModel($className, $force = false)
855
    {
856
        // existing values for a custom entity must not be overridden
857
        if ($force && $this->isCustom($className)) {
858
            $force = false;
859
        }
860
861
        $metadata = $this->getEntityMetadata($className);
862
        $entityModel = $this->createConfigEntityModel($className, $metadata->mode);
0 ignored issues
show
Bug introduced by
The property mode does not seem to exist in Metadata\ClassMetadata.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
863
        $entityModel->setMode($metadata->mode);
864
        foreach ($this->providers as $scope => $provider) {
865
            $config        = $provider->getConfig($className);
866
            $defaultValues = $this->getEntityDefaultValues($scope, $className, $metadata);
0 ignored issues
show
Documentation introduced by
$metadata is of type object<Metadata\ClassMetadata>, but the function expects a object<Oro\Bundle\Entity...ta\EntityMetadata>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
867
            $hasChanges    = $this->updateConfigValues($config, $defaultValues, $force);
868
            if ($hasChanges) {
869
                $configKey = $scope . '.' . $className;
870
871
                $this->persistConfigs[$configKey] = $config;
872
            }
873
        }
874
875
        // @todo: Should be removed together with deprecated events
876
        if ($this->hasListeners(Events::UPDATE_ENTITY_CONFIG)) {
0 ignored issues
show
Deprecated Code introduced by
The constant Oro\Bundle\EntityConfigB...s::UPDATE_ENTITY_CONFIG has been deprecated with message: since 1.9. Use UPDATE_ENTITY instead

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

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

Loading history...
877
            $this->eventDispatcher->dispatch(
878
                Events::UPDATE_ENTITY_CONFIG,
0 ignored issues
show
Deprecated Code introduced by
The constant Oro\Bundle\EntityConfigB...s::UPDATE_ENTITY_CONFIG has been deprecated with message: since 1.9. Use UPDATE_ENTITY instead

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

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

Loading history...
879
                new Event\EntityConfigEvent($className, $this)
880
            );
881
        }
882
        $this->eventDispatcher->dispatch(
883
            Events::UPDATE_ENTITY,
884
            new Event\EntityConfigEvent($className, $this)
885
        );
886
    }
887
888
    /**
889
     * @param string $className
890
     * @param string $fieldName
891
     * @param bool   $force - if TRUE overwrite existing value from annotation
892
     *
893
     * @TODO: need handling for removed values
894
     */
895
    public function updateConfigFieldModel($className, $fieldName, $force = false)
896
    {
897
        // existing values for a custom field must not be overridden
898
        if ($force && $this->isCustom($className, $fieldName)) {
899
            $force = false;
900
        }
901
902
        $metadata = $this->getFieldMetadata($className, $fieldName);
903
        foreach ($this->providers as $scope => $provider) {
904
            $config = $provider->getConfig($className, $fieldName);
905
            /** @var FieldConfigId $configId */
906
            $configId      = $config->getId();
907
            $defaultValues = $this->getFieldDefaultValues(
908
                $scope,
909
                $className,
910
                $fieldName,
911
                $configId->getFieldType(),
912
                $metadata
913
            );
914
            $hasChanges    = $this->updateConfigValues($config, $defaultValues, $force);
915
            if ($hasChanges) {
916
                $configKey = $scope . '.' . $className . '.' . $fieldName;
917
918
                $this->persistConfigs[$configKey] = $config;
919
            }
920
        }
921
922
        // @todo: Should be removed together with deprecated events
923
        if ($this->hasListeners(Events::UPDATE_FIELD_CONFIG)) {
0 ignored issues
show
Deprecated Code introduced by
The constant Oro\Bundle\EntityConfigB...ts::UPDATE_FIELD_CONFIG has been deprecated with message: since 1.9. Use UPDATE_FIELD instead

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

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

Loading history...
924
            $this->eventDispatcher->dispatch(
925
                Events::UPDATE_FIELD_CONFIG,
0 ignored issues
show
Deprecated Code introduced by
The constant Oro\Bundle\EntityConfigB...ts::UPDATE_FIELD_CONFIG has been deprecated with message: since 1.9. Use UPDATE_FIELD instead

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

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

Loading history...
926
                new Event\FieldConfigEvent($className, $fieldName, $this)
927
            );
928
        }
929
        $this->eventDispatcher->dispatch(
930
            Events::UPDATE_FIELD,
931
            new Event\FieldConfigEvent($className, $fieldName, $this)
932
        );
933
    }
934
935
    /**
936
     * Changes a type of a field
937
     *
938
     * @param string $className
939
     * @param string $fieldName
940
     * @param string $newFieldName
941
     *
942
     * @return bool TRUE if the name was changed; otherwise, FALSE
943
     */
944
    public function changeFieldName($className, $fieldName, $newFieldName)
945
    {
946
        $result = $this->modelManager->changeFieldName($className, $fieldName, $newFieldName);
947
        if ($result) {
948
            // @todo: Should be removed together with deprecated events
949
            if ($this->hasListeners(Events::RENAME_FIELD_OLD)) {
0 ignored issues
show
Deprecated Code introduced by
The constant Oro\Bundle\EntityConfigB...vents::RENAME_FIELD_OLD has been deprecated with message: since 1.9. Use RENAME_FIELD instead

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

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

Loading history...
950
                $this->eventDispatcher->dispatch(
951
                    Events::RENAME_FIELD_OLD,
0 ignored issues
show
Deprecated Code introduced by
The constant Oro\Bundle\EntityConfigB...vents::RENAME_FIELD_OLD has been deprecated with message: since 1.9. Use RENAME_FIELD instead

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

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

Loading history...
952
                    new Event\RenameFieldEvent($className, $fieldName, $newFieldName, $this)
953
                );
954
            }
955
            $this->eventDispatcher->dispatch(
956
                Events::RENAME_FIELD,
957
                new Event\RenameFieldEvent($className, $fieldName, $newFieldName, $this)
958
            );
959
            foreach ($this->providers as $scope => $provider) {
960
                $cachedConfig = $this->cache->getFieldConfig($scope, $className, $fieldName, true);
961
                if ($cachedConfig) {
962
                    $this->cache->saveConfig($this->changeConfigFieldName($cachedConfig, $newFieldName), true);
963
                    $this->cache->deleteFieldConfig($className, $fieldName, true);
964
                }
965
966
                $newConfigKey = $scope . '.' . $className . '.' . $newFieldName;
967
                $configKey    = $scope . '.' . $className . '.' . $fieldName;
968
                if (isset($this->persistConfigs[$configKey])) {
969
                    $this->persistConfigs[$newConfigKey] = $this->changeConfigFieldName(
970
                        $this->persistConfigs[$configKey],
971
                        $newFieldName
972
                    );
973
                    unset($this->persistConfigs[$configKey]);
974
                }
975
                if (isset($this->originalValues[$configKey])) {
976
                    $this->originalValues[$newConfigKey] = $this->originalValues[$configKey];
977
                    unset($this->originalValues[$configKey]);
978
                }
979
                if (isset($this->configChangeSets[$configKey])) {
980
                    $this->configChangeSets[$newConfigKey] = $this->configChangeSets[$configKey];
981
                    unset($this->configChangeSets[$configKey]);
982
                }
983
            }
984
        };
985
986
        return $result;
987
    }
988
989
    /**
990
     * Changes a type of a field
991
     *
992
     * @param string $className
993
     * @param string $fieldName
994
     * @param string $fieldType
995
     *
996
     * @return bool TRUE if the type was changed; otherwise, FALSE
997
     */
998
    public function changeFieldType($className, $fieldName, $fieldType)
999
    {
1000
        return $this->modelManager->changeFieldType($className, $fieldName, $fieldType);
1001
    }
1002
1003
    /**
1004
     * Changes a mode of a field
1005
     *
1006
     * @param string $className
1007
     * @param string $fieldName
1008
     * @param string $mode      Can be the value of one of ConfigModel::MODE_* constants
1009
     *
1010
     * @return bool TRUE if the mode was changed; otherwise, FALSE
1011
     */
1012
    public function changeFieldMode($className, $fieldName, $mode)
1013
    {
1014
        return $this->modelManager->changeFieldMode($className, $fieldName, $mode);
1015
    }
1016
1017
    /**
1018
     * Changes a mode of an entity
1019
     *
1020
     * @param string $className
1021
     * @param string $mode      Can be the value of one of ConfigModel::MODE_* constants
1022
     *
1023
     * @return bool TRUE if the type was changed; otherwise, FALSE
1024
     */
1025
    public function changeEntityMode($className, $mode)
1026
    {
1027
        return $this->modelManager->changeEntityMode($className, $mode);
1028
    }
1029
1030
    /**
1031
     * Gets config id for the given model
1032
     *
1033
     * @param ConfigModel $model
1034
     * @param string      $scope
1035
     *
1036
     * @return ConfigIdInterface
1037
     */
1038
    public function getConfigIdByModel($model, $scope)
1039
    {
1040
        if ($model instanceof FieldConfigModel) {
1041
            return new FieldConfigId(
1042
                $scope,
1043
                $model->getEntity()->getClassName(),
1044
                $model->getFieldName(),
1045
                $model->getType()
1046
            );
1047
        } else {
1048
            return new EntityConfigId($scope, $model->getClassName());
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Oro\Bundle\EntityConfigBundle\Entity\ConfigModel as the method getClassName() does only exist in the following sub-classes of Oro\Bundle\EntityConfigBundle\Entity\ConfigModel: Oro\Bundle\EntityConfigB...ntity\EntityConfigModel. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1049
        }
1050
    }
1051
1052
    /**
1053
     * Gets a model for the given config id
1054
     *
1055
     * @param ConfigIdInterface $configId
1056
     *
1057
     * @return ConfigModel
1058
     */
1059
    protected function getModelByConfigId(ConfigIdInterface $configId)
1060
    {
1061
        return $configId instanceof FieldConfigId
1062
            ? $this->modelManager->getFieldModel($configId->getClassName(), $configId->getFieldName())
1063
            : $this->modelManager->getEntityModel($configId->getClassName());
1064
    }
1065
1066
    /**
1067
     * In case of FieldConfigId replaces OLD field name with given NEW one
1068
     *
1069
     * @param ConfigInterface $config
1070
     * @param string          $newFieldName
1071
     *
1072
     * @return ConfigInterface
1073
     */
1074
    protected function changeConfigFieldName(ConfigInterface $config, $newFieldName)
1075
    {
1076
        $configId = $config->getId();
1077
        if ($configId instanceof FieldConfigId) {
1078
            $newConfigId = new FieldConfigId(
1079
                $configId->getScope(),
1080
                $configId->getClassName(),
1081
                $newFieldName,
1082
                $configId->getFieldType()
1083
            );
1084
1085
            $config = new Config($newConfigId, $config->getValues());
1086
        }
1087
1088
        return $config;
1089
    }
1090
1091
    /**
1092
     * Extracts entity default values from an annotation and config file
1093
     *
1094
     * @param string              $scope
1095
     * @param string|null         $className
1096
     * @param EntityMetadata|null $metadata
1097
     *
1098
     * @return array
1099
     */
1100 View Code Duplication
    protected function getEntityDefaultValues($scope, $className = null, $metadata = null)
1101
    {
1102
        $propertyConfig = $this->getPropertyConfig($scope);
1103
1104
        // try to get default values from an annotation
1105
        if ($metadata && isset($metadata->defaultValues[$scope])) {
1106
            // combine them with default values from a config file
1107
            $defaultValues = array_merge(
1108
                $propertyConfig->getDefaultValues(PropertyConfigContainer::TYPE_ENTITY),
1109
                $metadata->defaultValues[$scope]
1110
            );
1111
        } else {
1112
            $defaultValues = $propertyConfig->getDefaultValues(PropertyConfigContainer::TYPE_ENTITY);
1113
        }
1114
1115
        // process translatable values
1116
        if ($className) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $className of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1117
            $translatablePropertyNames = $propertyConfig->getTranslatableValues(PropertyConfigContainer::TYPE_ENTITY);
1118
            foreach ($translatablePropertyNames as $propertyName) {
1119
                if (empty($defaultValues[$propertyName])) {
1120
                    $defaultValues[$propertyName] =
1121
                        ConfigHelper::getTranslationKey($scope, $propertyName, $className);
1122
                }
1123
            }
1124
        }
1125
1126
        return $defaultValues;
1127
    }
1128
1129
    /**
1130
     * Extracts field default values from an annotation and config file
1131
     *
1132
     * @param string             $scope
1133
     * @param string             $className
1134
     * @param string             $fieldName
1135
     * @param string             $fieldType
1136
     * @param FieldMetadata|null $metadata
1137
     *
1138
     * @return array
1139
     */
1140 View Code Duplication
    protected function getFieldDefaultValues($scope, $className, $fieldName, $fieldType, $metadata = null)
1141
    {
1142
        $propertyConfig = $this->getPropertyConfig($scope);
1143
1144
        // try to get default values from an annotation
1145
        if ($metadata && isset($metadata->defaultValues[$scope])) {
1146
            // combine them with default values from a config file
1147
            $defaultValues = array_merge(
1148
                $propertyConfig->getDefaultValues(PropertyConfigContainer::TYPE_FIELD, $fieldType),
1149
                $metadata->defaultValues[$scope]
1150
            );
1151
        } else {
1152
            $defaultValues = $propertyConfig->getDefaultValues(PropertyConfigContainer::TYPE_FIELD, $fieldType);
1153
        }
1154
1155
        // process translatable values
1156
        $translatablePropertyNames = $propertyConfig->getTranslatableValues(PropertyConfigContainer::TYPE_FIELD);
1157
        foreach ($translatablePropertyNames as $propertyName) {
1158
            if (empty($defaultValues[$propertyName])) {
1159
                $defaultValues[$propertyName] =
1160
                    ConfigHelper::getTranslationKey($scope, $propertyName, $className, $fieldName);
1161
            }
1162
        }
1163
1164
        return $defaultValues;
1165
    }
1166
1167
    /**
1168
     * Updates values of the given config based on the given default values and $force flag
1169
     *
1170
     * @param ConfigInterface $config
1171
     * @param array           $defaultValues
1172
     * @param bool            $force
1173
     *
1174
     * @return bool  TRUE if at least one config value was updated; otherwise, FALSE
1175
     */
1176
    protected function updateConfigValues(ConfigInterface $config, array $defaultValues, $force)
1177
    {
1178
        $hasChanges = false;
1179
        foreach ($defaultValues as $code => $value) {
1180
            if (!$config->has($code) || $force) {
1181
                $config->set($code, $value);
1182
                $hasChanges = true;
1183
            }
1184
        }
1185
1186
        return $hasChanges;
1187
    }
1188
1189
    /**
1190
     * @param ConfigInterface $config
1191
     * @param string          $configKey
1192
     */
1193
    protected function mergeConfigValues(ConfigInterface $config, $configKey)
1194
    {
1195
        if (isset($this->persistConfigs[$configKey])) {
1196
            $existingValues = $this->persistConfigs[$configKey]->getValues();
1197
            if (!empty($existingValues)) {
1198
                $config->setValues(array_merge($existingValues, $config->getValues()));
1199
            }
1200
        }
1201
        $this->persistConfigs[$configKey] = $config;
1202
    }
1203
1204
    /**
1205
     * Computes the difference of current and original config values
1206
     *
1207
     * @param array $values
1208
     * @param array $originalValues
1209
     *
1210
     * @return array
1211
     */
1212
    protected function getDiff($values, $originalValues)
1213
    {
1214
        $diff = [];
1215
        if (empty($originalValues)) {
1216
            foreach ($values as $code => $value) {
1217
                $diff[$code] = [null, $value];
1218
            }
1219
        } else {
1220
            foreach ($originalValues as $code => $originalValue) {
1221
                if (array_key_exists($code, $values)) {
1222
                    $value = $values[$code];
1223
                    if ($originalValue != $value) {
1224
                        $diff[$code] = [$originalValue, $value];
1225
                    }
1226
                }
1227
            }
1228
            foreach ($values as $code => $value) {
1229
                if (!array_key_exists($code, $originalValues)) {
1230
                    $diff[$code] = [null, $value];
1231
                }
1232
            }
1233
        }
1234
1235
        return $diff;
1236
    }
1237
1238
    /**
1239
     * Returns a string unique identifies each config item
1240
     *
1241
     * @param ConfigIdInterface $configId
1242
     *
1243
     * @return string
1244
     */
1245
    protected function buildConfigKey(ConfigIdInterface $configId)
1246
    {
1247
        return $configId instanceof FieldConfigId
1248
            ? $configId->getScope() . '.' . $configId->getClassName() . '.' . $configId->getFieldName()
1249
            : $configId->getScope() . '.' . $configId->getClassName();
1250
    }
1251
1252
    /**
1253
     * @param string $scope
1254
     *
1255
     * @return PropertyConfigContainer
1256
     */
1257
    protected function getPropertyConfig($scope)
1258
    {
1259
        if (isset($this->propertyConfigs[$scope])) {
1260
            return $this->propertyConfigs[$scope];
1261
        }
1262
1263
        $propertyConfig = $this->providers[$scope]->getPropertyConfig();
1264
1265
        $this->propertyConfigs[$scope] = $propertyConfig;
1266
1267
        return $propertyConfig;
1268
    }
1269
1270
    /**
1271
     * @param callable $callback
1272
     * @param string   $scope
1273
     * @param bool     $withHidden
1274
     *
1275
     * @return array
1276
     */
1277
    protected function mapEntities($callback, $scope, $withHidden)
1278
    {
1279
        $result = [];
1280
1281
        $entities = $this->cache->getEntities();
1282
        if (null !== $entities) {
1283
            foreach ($entities as $class => $data) {
1284
                if ($withHidden || !$data['h']) {
1285
                    $result[] = $callback($scope, $class);
1286
                }
1287
            }
1288
        } elseif ($this->modelManager->checkDatabase()) {
1289
            $models   = $this->modelManager->getModels();
1290
            $entities = [];
1291
            /** @var EntityConfigModel $model */
1292
            foreach ($models as $model) {
1293
                $isHidden = $model->isHidden();
1294
                $class    = $model->getClassName();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Oro\Bundle\EntityConfigBundle\Entity\ConfigModel as the method getClassName() does only exist in the following sub-classes of Oro\Bundle\EntityConfigBundle\Entity\ConfigModel: Oro\Bundle\EntityConfigB...ntity\EntityConfigModel. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1295
1296
                $entities[$class] = [
1297
                    'i' => $model->getId(),
1298
                    'h' => $isHidden
1299
                ];
1300
                if ($withHidden || !$isHidden) {
1301
                    $result[] = $callback($scope, $class);
1302
                }
1303
            }
1304
            $this->cache->saveEntities($entities);
1305
        }
1306
1307
        return $result;
1308
    }
1309
1310
    /**
1311
     * @param string $className
1312
     *
1313
     * @return array|null ['i' => entity_model_id, 'h' => is_hidden_model]
1314
     */
1315
    protected function findEntity($className)
1316
    {
1317
        $result = null;
1318
1319
        $entities = $this->cache->getEntities();
1320
        if (null === $entities && $this->modelManager->checkDatabase()) {
1321
            $models   = $this->modelManager->getModels();
1322
            $entities = [];
1323
            /** @var EntityConfigModel $model */
1324
            foreach ($models as $model) {
1325
                $isHidden = $model->isHidden();
1326
                $class    = $model->getClassName();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Oro\Bundle\EntityConfigBundle\Entity\ConfigModel as the method getClassName() does only exist in the following sub-classes of Oro\Bundle\EntityConfigBundle\Entity\ConfigModel: Oro\Bundle\EntityConfigB...ntity\EntityConfigModel. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1327
1328
                $entities[$class] = [
1329
                    'i' => $model->getId(),
1330
                    'h' => $isHidden
1331
                ];
1332
            }
1333
            $this->cache->saveEntities($entities);
1334
        }
1335
        if (null !== $entities && isset($entities[$className])) {
1336
            $result = $entities[$className];
1337
        }
1338
1339
        return $result;
1340
    }
1341
1342
    /**
1343
     * @param callable $callback
1344
     * @param string   $scope
1345
     * @param string   $className
1346
     * @param bool     $withHidden
1347
     *
1348
     * @return array
1349
     */
1350
    protected function mapFields($callback, $scope, $className, $withHidden)
1351
    {
1352
        $result = [];
1353
1354
        $fields = $this->cache->getFields($className);
1355
        if (null !== $fields) {
1356
            foreach ($fields as $field => $data) {
1357
                if ($withHidden || !$data['h']) {
1358
                    $result[] = $callback($scope, $className, $field, $data['t']);
1359
                }
1360
            }
1361
        } elseif ($this->modelManager->checkDatabase()) {
1362
            $models = $this->modelManager->getModels($className);
1363
            $fields = [];
1364
            /** @var FieldConfigModel $model */
1365
            foreach ($models as $model) {
1366
                $isHidden = $model->isHidden();
1367
                $field    = $model->getFieldName();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Oro\Bundle\EntityConfigBundle\Entity\ConfigModel as the method getFieldName() does only exist in the following sub-classes of Oro\Bundle\EntityConfigBundle\Entity\ConfigModel: Oro\Bundle\EntityConfigB...Entity\FieldConfigModel. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1368
                $type     = $model->getType();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Oro\Bundle\EntityConfigBundle\Entity\ConfigModel as the method getType() does only exist in the following sub-classes of Oro\Bundle\EntityConfigBundle\Entity\ConfigModel: Oro\Bundle\EntityConfigB...Entity\FieldConfigModel. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1369
1370
                $fields[$field] = [
1371
                    'i' => $model->getId(),
1372
                    'h' => $isHidden,
1373
                    't' => $type,
1374
                ];
1375
                if ($withHidden || !$isHidden) {
1376
                    $result[] = $callback($scope, $className, $field, $type);
1377
                }
1378
            }
1379
            $this->cache->saveFields($className, $fields);
1380
        }
1381
1382
        return $result;
1383
    }
1384
1385
    /**
1386
     * @param string $className
1387
     * @param string $fieldName
1388
     *
1389
     * @return array|null ['i' => field_model_id, 'h' => is_hidden_model, 't' => field_type]
1390
     */
1391
    protected function findField($className, $fieldName)
1392
    {
1393
        $result = null;
1394
1395
        $fields = $this->cache->getFields($className);
1396
        if (null === $fields && $this->modelManager->checkDatabase()) {
1397
            $models = $this->modelManager->getModels($className);
1398
            $fields = [];
1399
            /** @var FieldConfigModel $model */
1400
            foreach ($models as $model) {
1401
                $isHidden = $model->isHidden();
1402
                $field    = $model->getFieldName();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Oro\Bundle\EntityConfigBundle\Entity\ConfigModel as the method getFieldName() does only exist in the following sub-classes of Oro\Bundle\EntityConfigBundle\Entity\ConfigModel: Oro\Bundle\EntityConfigB...Entity\FieldConfigModel. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1403
                $type     = $model->getType();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Oro\Bundle\EntityConfigBundle\Entity\ConfigModel as the method getType() does only exist in the following sub-classes of Oro\Bundle\EntityConfigBundle\Entity\ConfigModel: Oro\Bundle\EntityConfigB...Entity\FieldConfigModel. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1404
1405
                $fields[$field] = [
1406
                    'i' => $model->getId(),
1407
                    'h' => $isHidden,
1408
                    't' => $type,
1409
                ];
1410
            }
1411
            $this->cache->saveFields($className, $fields);
1412
        }
1413
        if (null !== $fields && isset($fields[$fieldName])) {
1414
            $result = $fields[$fieldName];
1415
        }
1416
1417
        return $result;
1418
    }
1419
1420
    /**
1421
     * Checks whether an entity is configurable.
1422
     *
1423
     * @param string $className
1424
     *
1425
     * @return bool
1426
     */
1427 View Code Duplication
    protected function isConfigurableEntity($className)
1428
    {
1429
        $isConfigurable = $this->cache->getConfigurable($className);
1430
        if (null === $isConfigurable) {
1431
            $isConfigurable = (null !== $this->modelManager->findEntityModel($className));
1432
            $this->cache->saveConfigurable($isConfigurable, $className);
1433
        }
1434
1435
        return $isConfigurable;
1436
    }
1437
1438
    /**
1439
     * Checks whether a field is configurable.
1440
     *
1441
     * @param string $className
1442
     * @param string $fieldName
1443
     *
1444
     * @return bool
1445
     */
1446 View Code Duplication
    protected function isConfigurableField($className, $fieldName)
1447
    {
1448
        $isConfigurable = $this->cache->getConfigurable($className, $fieldName);
1449
        if (null === $isConfigurable) {
1450
            $isConfigurable = (null !== $this->modelManager->findFieldModel($className, $fieldName));
1451
            $this->cache->saveConfigurable($isConfigurable, $className, $fieldName);
1452
        }
1453
1454
        return $isConfigurable;
1455
    }
1456
1457
    /**
1458
     * Checks whether an entity or entity field is custom or system
1459
     * Custom means that "extend::owner" equals "Custom"
1460
     *
1461
     * @param string      $className
1462
     * @param string|null $fieldName
1463
     *
1464
     * @return bool
1465
     */
1466 View Code Duplication
    protected function isCustom($className, $fieldName = null)
1467
    {
1468
        $result         = false;
1469
        $extendProvider = $this->getProvider('extend');
1470
        if ($extendProvider && $extendProvider->hasConfig($className, $fieldName)) {
1471
            $result = $extendProvider->getConfig($className, $fieldName)
1472
                ->is('owner', ExtendScope::OWNER_CUSTOM);
1473
        }
1474
1475
        return $result;
1476
    }
1477
1478
    /**
1479
     * @return LogicException
1480
     */
1481
    protected function createDatabaseNotSyncedException()
1482
    {
1483
        return new LogicException(
1484
            'Database is not synced, if you use ConfigManager, when a db schema may be hasn\'t synced.'
1485
            . ' check it by ConfigManager::modelManager::checkDatabase'
1486
        );
1487
    }
1488
1489
    /**
1490
     * @deprecated since 1.9. Should be removed together with deprecated events
1491
     * @see Oro\Bundle\EntityConfigBundle\Event\Events
1492
     *
1493
     * @param string $eventName
1494
     *
1495
     * @return bool
1496
     */
1497
    private function hasListeners($eventName)
1498
    {
1499
        if (!isset($this->hasListenersCache[$eventName])) {
0 ignored issues
show
Deprecated Code introduced by
The property Oro\Bundle\EntityConfigB...ger::$hasListenersCache has been deprecated with message: since 1.9. Should be removed together with deprecated events

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

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

Loading history...
1500
            $this->hasListenersCache[$eventName] = $this->eventDispatcher->hasListeners($eventName);
0 ignored issues
show
Deprecated Code introduced by
The property Oro\Bundle\EntityConfigB...ger::$hasListenersCache has been deprecated with message: since 1.9. Should be removed together with deprecated events

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

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

Loading history...
1501
        }
1502
1503
        return $this->hasListenersCache[$eventName];
0 ignored issues
show
Deprecated Code introduced by
The property Oro\Bundle\EntityConfigB...ger::$hasListenersCache has been deprecated with message: since 1.9. Should be removed together with deprecated events

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

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

Loading history...
1504
    }
1505
}
1506