GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#137)
by joseph
60:53 queued 35:20
created

AbstractTest.php$0 ➔ getSchema()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\Tests\Assets;
4
5
use Composer\Autoload\ClassLoader;
6
use Doctrine\Common\Cache\CacheProvider;
7
use Doctrine\ORM\EntityManagerInterface;
8
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\CodeHelper;
9
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Command\AbstractCommand;
10
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\AbstractGenerator;
11
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Embeddable\ArchetypeEmbeddableGenerator;
12
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Embeddable\EntityEmbeddableSetter;
13
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\EntityGenerator;
14
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Field\EntityFieldSetter;
15
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Field\FieldGenerator;
16
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\FindAndReplaceHelper;
17
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\RelationsGenerator;
18
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\NamespaceHelper;
19
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\PathHelper;
20
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\UnusedRelationsRemover;
21
use EdmondsCommerce\DoctrineStaticMeta\Config;
22
use EdmondsCommerce\DoctrineStaticMeta\ConfigInterface;
23
use EdmondsCommerce\DoctrineStaticMeta\Container;
24
use EdmondsCommerce\DoctrineStaticMeta\Entity\Factory\EntityFactory;
25
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\EntityInterface;
26
use EdmondsCommerce\DoctrineStaticMeta\Entity\Repositories\RepositoryFactory;
27
use EdmondsCommerce\DoctrineStaticMeta\Entity\Testing\EntityDebugDumper;
28
use EdmondsCommerce\DoctrineStaticMeta\Entity\Testing\EntityGenerator\TestEntityGeneratorFactory;
29
use EdmondsCommerce\DoctrineStaticMeta\Schema\Schema;
30
use EdmondsCommerce\DoctrineStaticMeta\SimpleEnv;
31
use EdmondsCommerce\PHPQA\Constants;
32
use PHPUnit\Framework\TestCase;
33
use Symfony\Component\Filesystem\Filesystem;
34
35
/**
36
 * Class AbstractTest
37
 *
38
 * @package EdmondsCommerce\DoctrineStaticMeta
39
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
40
 * @SuppressWarnings(PHPMD.NumberOfChildren)
41
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
42
 */
43
abstract class AbstractTest extends TestCase
44
{
45
    public const TEST_TYPE_SMALL             = 'Small';
46
    public const TEST_TYPE_MEDIUM            = 'Medium';
47
    public const TEST_TYPE_LARGE             = 'Large';
48
    public const VAR_PATH                    = __DIR__ . '/../../var/testOutput/';
49
    public const WORK_DIR                    = 'override me';
50
    public const TEST_PROJECT_ROOT_NAMESPACE = 'My\\Test\\Project';
51
    protected static $buildOnce = false;
52
    protected static $built     = false;
53
    /**
54
     * @var Container
55
     */
56
    protected static $containerStaticRef;
57
    /**
58
     * The absolute path to the Entities folder, eg:
59
     * /var/www/vhosts/doctrine-static-meta/var/{testWorkDir}/Entities
60
     *
61
     * @var string
62
     */
63
    protected $entitiesPath = '';
64
    /**
65
     * The absolute path to the EntityRelations folder, eg:
66
     * /var/www/vhosts/doctrine-static-meta/var/{testWorkDir}/Entity/Relations
67
     *
68
     * @var string
69
     */
70
    protected $entityRelationsPath = '';
71
    /**
72
     * @var Container
73
     */
74
    protected $container;
75
    /**
76
     * @var Filesystem
77
     */
78
    protected $filesystem;
79
    /**
80
     * @var string|null
81
     */
82
    protected $copiedWorkDir;
83
    /**
84
     * @var string|null
85
     */
86
    protected $copiedRootNamespace;
87
88
    /**
89
     * Ensure built steps are set to false when test class is instantiated
90
     */
91
    public static function setUpBeforeClass()
92
    {
93
        self::$built   = false;
94
        static::$built = false;
95
    }
96
97
    public static function tearDownAfterClass()
98
    {
99
        if (!(self::$containerStaticRef instanceof Container)) {
0 ignored issues
show
introduced by
self::containerStaticRef is always a sub-type of EdmondsCommerce\DoctrineStaticMeta\Container. If self::containerStaticRef can have other possible types, add them to tests/Assets/AbstractTest.php:54.
Loading history...
100
            return;
101
        }
102
        $entityManager = static::$containerStaticRef->get(EntityManagerInterface::class);
103
        $connection    = $entityManager->getConnection();
104
105
        $entityManager->close();
106
        $connection->close();
107
        static::$containerStaticRef = null;
108
    }
109
110
    /**
111
     * Prepare working directory, ensure its empty, create entities folder and set up env variables
112
     *
113
     * The order of these actions is critical
114
     */
115
    public function setUp()
116
    {
117
        if (false !== stripos(static::WORK_DIR, self::WORK_DIR)) {
118
            throw new \RuntimeException(
119
                "You must set a `public const WORK_DIR=AbstractTest::VAR_PATH.'/'"
120
                . ".self::TEST_TYPE.'/folderName/';` in your test class"
121
            );
122
        }
123
        if (false === strpos(static::WORK_DIR, '/' . static::TEST_TYPE_SMALL)
124
            && false === strpos(static::WORK_DIR, '/' . static::TEST_TYPE_MEDIUM)
125
            && false === strpos(static::WORK_DIR, '/' . static::TEST_TYPE_LARGE)
126
        ) {
127
            throw new \RuntimeException(
128
                'Your WORK_DIR is missing the test type, should look like: '
129
                . "`public const WORK_DIR=AbstractTest::VAR_PATH.'/'"
130
                . ".self::TEST_TYPE_(SMALL|MEDIUM|LARGE).'/folderName/';` in your test class"
131
            );
132
        }
133
        $this->copiedWorkDir       = null;
134
        $this->copiedRootNamespace = null;
135
        $this->entitiesPath        = static::WORK_DIR
136
                                     . '/' . AbstractCommand::DEFAULT_SRC_SUBFOLDER
137
                                     . '/' . AbstractGenerator::ENTITIES_FOLDER_NAME;
138
        $this->entityRelationsPath = static::WORK_DIR
139
                                     . '/' . AbstractCommand::DEFAULT_SRC_SUBFOLDER
140
                                     . '/' . AbstractGenerator::ENTITY_RELATIONS_FOLDER_NAME;
141
        $this->clearWorkDir();
142
        $this->setupContainer($this->entitiesPath);
143
        $this->clearCache();
144
        $this->extendAutoloader(
145
            static::TEST_PROJECT_ROOT_NAMESPACE . '\\',
146
            static::WORK_DIR
147
        );
148
    }
149
150
    protected function clearWorkDir(): void
151
    {
152
        if (true === static::$buildOnce && true === static::$built) {
153
            $this->entitiesPath = $this->getRealPath($this->entitiesPath);
154
155
            return;
156
        }
157
        $this->getFileSystem()->mkdir(static::WORK_DIR);
158
        $this->emptyDirectory(static::WORK_DIR);
159
        $this->getFileSystem()->mkdir($this->entitiesPath);
160
        $this->entitiesPath = $this->getRealPath($this->entitiesPath);
161
162
        $this->getFileSystem()->mkdir($this->entityRelationsPath);
163
        $this->entityRelationsPath = realpath($this->entityRelationsPath);
164
    }
165
166
    protected function getRealPath(string $path)
167
    {
168
        $realpath = realpath($path);
169
        if (false === $realpath) {
170
            throw new \RuntimeException('Failed getting realpath for path: ' . $path);
171
        }
172
173
        return $realpath;
174
    }
175
176
    protected function getFileSystem(): Filesystem
177
    {
178
        if (null === $this->filesystem) {
179
            $this->filesystem = new Filesystem();
180
        }
181
182
        return $this->filesystem;
183
    }
184
185
    protected function emptyDirectory(string $path): void
186
    {
187
        $fileSystem = $this->getFileSystem();
188
        $fileSystem->remove($path);
189
        $fileSystem->mkdir($path);
190
    }
191
192
    /**
193
     * @param string $entitiesPath
194
     *
195
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\ConfigException
196
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
197
     * @SuppressWarnings(PHPMD.Superglobals)
198
     * @SuppressWarnings(PHPMD.StaticAccess)
199
     */
200
    protected function setupContainer(string $entitiesPath): void
201
    {
202
        SimpleEnv::setEnv(Config::getProjectRootDirectory() . '/.env');
203
        $testConfig                                               = $_SERVER;
204
        $testConfig[ConfigInterface::PARAM_ENTITIES_PATH]         = $entitiesPath;
205
        $testConfig[ConfigInterface::PARAM_DB_NAME]               .= '_test';
206
        $testConfig[ConfigInterface::PARAM_DEVMODE]               = true;
207
        $testConfig[ConfigInterface::PARAM_FILESYSTEM_CACHE_PATH] = static::WORK_DIR . '/cache/dsm';
208
        $this->container                                          = new Container();
209
        $this->container->buildSymfonyContainer($testConfig);
210
        static::$containerStaticRef = $this->container;
211
    }
212
213
    /**
214
     * Clear the Doctrine Cache
215
     *
216
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
217
     */
218
    protected function clearCache(): void
219
    {
220
        $cache = $this->getEntityManager()
221
                      ->getConfiguration()
222
                      ->getMetadataCacheImpl();
223
        if ($cache instanceof CacheProvider) {
224
            $cache->deleteAll();
225
        }
226
    }
227
228
    /**
229
     * @return EntityManagerInterface
230
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
231
     */
232
    protected function getEntityManager(): EntityManagerInterface
233
    {
234
        return $this->container->get(EntityManagerInterface::class);
235
    }
236
237
    /**
238
     * Accesses the standard Composer autoloader
239
     *
240
     * Extends the class and also providing an entry point for xdebugging
241
     *
242
     * Extends this with a PSR4 root for our WORK_DIR
243
     *
244
     * @param string $namespace
245
     * @param string $path
246
     *
247
     * @throws \ReflectionException
248
     */
249
    protected function extendAutoloader(string $namespace, string $path): void
250
    {
251
        //Unregister any previously set extension first
252
        $registered = \spl_autoload_functions();
253
        foreach ($registered as $loader) {
254
            if ((new  \ts\Reflection\ReflectionClass(\get_class($loader[0])))->isAnonymous()) {
255
                \spl_autoload_unregister($loader);
256
            }
257
        }
258
        //Then build a new extension and register it
259
        $namespace  = rtrim($namespace, '\\') . '\\';
260
        $testLoader = new class($namespace) extends ClassLoader
261
        {
262
            /**
263
             * @var string
264
             */
265
            protected $namespace;
266
267
            public function __construct(string $namespace)
268
            {
269
                $this->namespace = $namespace;
270
            }
271
272
            public function loadClass($class)
273
            {
274
                if (false === strpos($class, $this->namespace)) {
275
                    return false;
276
                }
277
                $found = parent::loadClass($class);
278
                if (false === $found || null === $found) {
0 ignored issues
show
introduced by
The condition null === $found is always false.
Loading history...
279
                    //good point to set a breakpoint
280
                    return $found;
281
                }
282
283
                return $found;
284
            }
285
        };
286
        $testLoader->addPsr4($namespace, $path . '/src', true);
287
        $testLoader->addPsr4($namespace, $path . '/tests', true);
288
        $testLoader->register();
289
    }
290
291
    /**
292
     * Run QA tools against the generated code
293
     *
294
     * Can specify a custom namespace root if required
295
     *
296
     * Will run:
297
     *
298
     * - PHP linting
299
     * - PHPStan
300
     *
301
     * @SuppressWarnings(PHPMD.Superglobals)
302
     * @param null|string $namespaceRoot
303
     *
304
     * @return bool
305
     */
306
    public function qaGeneratedCode(?string $namespaceRoot = null): bool
307
    {
308
        if ($this->isQuickTests()) {
309
            return true;
310
        }
311
        $workDir       = static::WORK_DIR;
312
        $namespaceRoot = trim($namespaceRoot ?? static::TEST_PROJECT_ROOT_NAMESPACE, '\\');
313
        if (null !== $this->copiedRootNamespace) {
314
            $workDir       = $this->copiedWorkDir;
315
            $namespaceRoot = trim($this->copiedRootNamespace, '\\');
316
        }
317
        static $codeValidator;
318
        if (null === $codeValidator) {
319
            $codeValidator = new CodeValidator();
320
        }
321
        $errors = $codeValidator($workDir, $namespaceRoot);
322
        self::assertNull($errors);
323
324
        return true;
325
    }
326
327
    /**
328
     * @return bool
329
     * @SuppressWarnings(PHPMD.Superglobals)
330
     */
331
    protected function isQuickTests(): bool
332
    {
333
        if (isset($_SERVER[Constants::QA_QUICK_TESTS_KEY])
334
            && (int)$_SERVER[Constants::QA_QUICK_TESTS_KEY] === Constants::QA_QUICK_TESTS_ENABLED
335
        ) {
336
            return true;
337
        }
338
339
        return false;
340
    }
341
342
343
    protected function tearDown()
344
    {
345
        $entityManager = $this->getEntityManager();
346
        $connection    = $entityManager->getConnection();
347
348
        $entityManager->close();
349
        $connection->close();
350
    }
351
352
    protected function getRepositoryFactory(): RepositoryFactory
353
    {
354
        return $this->container->get(RepositoryFactory::class);
355
    }
356
357
    protected function getNamespaceHelper(): NamespaceHelper
358
    {
359
        return $this->container->get(NamespaceHelper::class);
360
    }
361
362
    protected function dump(EntityInterface $entity): string
363
    {
364
        return (new EntityDebugDumper())->dump($entity, $this->getEntityManager());
365
    }
366
367
    /**
368
     * If PHP loads any files whilst generating, then subsequent changes to those files will not have any effect
369
     *
370
     * To resolve this, we need to clone the copied code into a new namespace before running it
371
     *
372
     * We only allow copying to a new work dir once per test run, different extras must be used
373
     *
374
     * @return string $copiedWorkDir
375
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
376
     * @throws \ReflectionException
377
     */
378
    protected function setupCopiedWorkDir(): string
379
    {
380
        $copiedNamespaceRoot       = $this->getCopiedNamespaceRoot();
381
        $this->copiedWorkDir       = rtrim(static::WORK_DIR, '/') . 'Copies/' . $copiedNamespaceRoot . '/';
382
        $this->copiedRootNamespace = $copiedNamespaceRoot;
383
        if (is_dir($this->copiedWorkDir)) {
384
            throw new \RuntimeException(
385
                'The Copied WorkDir ' . $this->copiedWorkDir . ' Already Exists'
386
            );
387
        }
388
        if (is_dir($this->copiedWorkDir)) {
389
            $this->getFileSystem()->remove($this->copiedWorkDir);
390
        }
391
        $this->getFileSystem()->mkdir($this->copiedWorkDir);
392
        $this->getFileSystem()->mirror(static::WORK_DIR, $this->copiedWorkDir);
393
        $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->copiedWorkDir));
394
395
        foreach ($iterator as $info) {
396
            /**
397
             * @var \SplFileInfo $info
398
             */
399
            if (false === $info->isFile()) {
400
                continue;
401
            }
402
            $contents = file_get_contents($info->getPathname());
403
404
            $updated = \preg_replace(
405
                '%(use|namespace)\s+?'
406
                . $this->container->get(FindAndReplaceHelper::class)
407
                                  ->escapeSlashesForRegex(static::TEST_PROJECT_ROOT_NAMESPACE)
408
                . '\\\\%',
409
                '$1 ' . $copiedNamespaceRoot . '\\',
410
                $contents
411
            );
412
            if ('AbstractEntityTest.php' === $info->getBasename()) {
413
                $updated = str_replace(
414
                    "'" . static::TEST_PROJECT_ROOT_NAMESPACE,
415
                    "'" . $copiedNamespaceRoot,
416
                    $updated
417
                );
418
            }
419
            file_put_contents($info->getPathname(), $updated);
420
        }
421
        $this->extendAutoloader(
422
            $this->copiedRootNamespace . '\\',
423
            $this->copiedWorkDir
424
        );
425
        $this->clearCache();
426
427
        return $this->copiedWorkDir;
428
    }
429
430
    /**
431
     * Get the namespace root to use in a copied work dir
432
     *
433
     * @return string
434
     * @throws \ReflectionException
435
     */
436
    protected function getCopiedNamespaceRoot(): string
437
    {
438
        return (new  \ts\Reflection\ReflectionClass(static::class))->getShortName() . '_' . $this->getName() . '_';
439
    }
440
441
    /**
442
     * When working with a copied work dir, use this function to translate the FQN of any Entities etc
443
     *
444
     * This will replace both the raw TestCodeGenerator root namespace and the test level root namespace
445
     *
446
     * @param string $fqn
447
     *
448
     * @return string
449
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
450
     * @throws \ReflectionException
451
     */
452
    protected function getCopiedFqn(string $fqn): string
453
    {
454
        $copiedNamespaceRoot = $this->getCopiedNamespaceRoot();
455
456
        return $this->container
457
            ->get(NamespaceHelper::class)
458
            ->tidy('\\' . $copiedNamespaceRoot . '\\'
459
                   . ltrim(
460
                       \str_replace(
461
                           [
462
                               static::TEST_PROJECT_ROOT_NAMESPACE,
463
                               TestCodeGenerator::TEST_PROJECT_ROOT_NAMESPACE,
464
                           ],
465
                           '',
466
                           $fqn
467
                       ),
468
                       '\\'
469
                   ));
470
    }
471
472
    /**
473
     * @return bool
474
     * @SuppressWarnings(PHPMD.Superglobals)
475
     */
476
    protected function isTravis(): bool
477
    {
478
        return isset($_SERVER['TRAVIS']);
479
    }
480
481
    protected function getEntityEmbeddableSetter(): EntityEmbeddableSetter
482
    {
483
        return $this->container->get(EntityEmbeddableSetter::class);
484
    }
485
486
    /**
487
     * @return ArchetypeEmbeddableGenerator
488
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
489
     */
490
    protected function getArchetypeEmbeddableGenerator(): ArchetypeEmbeddableGenerator
491
    {
492
        /**
493
         * @var ArchetypeEmbeddableGenerator $generator
494
         */
495
        $generator = $this->container->get(ArchetypeEmbeddableGenerator::class);
496
        $generator->setProjectRootNamespace(static::TEST_PROJECT_ROOT_NAMESPACE)
497
                  ->setPathToProjectRoot(static::WORK_DIR);
498
499
        return $generator;
500
    }
501
502
    protected function assertNoMissedReplacements(string $createdFile, array $checkFor = []): void
503
    {
504
        $createdFile = $this->getPathHelper()->resolvePath($createdFile);
505
        self::assertFileExists($createdFile);
506
        $contents   = file_get_contents($createdFile);
507
        $checkFor[] = 'template';
508
        foreach ($checkFor as $check) {
509
            self::assertNotRegExp(
510
                '%[^a-z]' . $check . '[^a-z]%i',
511
                $contents,
512
                'Found the word "' . $check . '" (case insensitive) in the created file ' . $createdFile
513
            );
514
        }
515
    }
516
517
    protected function getPathHelper(): PathHelper
518
    {
519
        return $this->container->get(PathHelper::class);
520
    }
521
522
    protected function getTestEntityGeneratorFactory(): TestEntityGeneratorFactory
523
    {
524
        return $this->container->get(TestEntityGeneratorFactory::class);
525
    }
526
527
    protected function assertFileContains(string $createdFile, string $needle): void
528
    {
529
        $createdFile = $this->getPathHelper()->resolvePath($createdFile);
530
        self::assertFileExists($createdFile);
531
        $contents = file_get_contents($createdFile);
532
        self::assertContains(
533
            $needle,
534
            $contents,
535
            "Missing '$needle' in file '$createdFile'"
536
        );
537
    }
538
539
    protected function getEntityGenerator(): EntityGenerator
540
    {
541
        /**
542
         * @var EntityGenerator $entityGenerator
543
         */
544
        $entityGenerator = $this->container->get(EntityGenerator::class);
545
        $entityGenerator->setPathToProjectRoot($this->copiedWorkDir ?? static::WORK_DIR)
546
                        ->setProjectRootNamespace($this->copiedRootNamespace ?? static::TEST_PROJECT_ROOT_NAMESPACE);
547
548
        return $entityGenerator;
549
    }
550
551
    protected function getRelationsGenerator(): RelationsGenerator
552
    {
553
        /**
554
         * @var RelationsGenerator $relationsGenerator
555
         */
556
        $relationsGenerator = $this->container->get(RelationsGenerator::class);
557
        $relationsGenerator->setPathToProjectRoot($this->copiedWorkDir ?? static::WORK_DIR)
558
                           ->setProjectRootNamespace($this->copiedRootNamespace ?? static::TEST_PROJECT_ROOT_NAMESPACE);
559
560
        return $relationsGenerator;
561
    }
562
563
    protected function getFieldGenerator(): FieldGenerator
564
    {
565
        /**
566
         * @var \EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Field\FieldGenerator $fieldGenerator
567
         */
568
        $fieldGenerator = $this->container->get(FieldGenerator::class);
569
        $fieldGenerator->setPathToProjectRoot($this->copiedWorkDir ?? static::WORK_DIR)
570
                       ->setProjectRootNamespace($this->copiedRootNamespace ?? static::TEST_PROJECT_ROOT_NAMESPACE);
571
572
        return $fieldGenerator;
573
    }
574
575
    protected function getTestCodeGenerator(): TestCodeGenerator
576
    {
577
        return $this->container->get(TestCodeGenerator::class);
578
    }
579
580
    protected function getFieldSetter(): EntityFieldSetter
581
    {
582
        $fieldSetter = $this->container->get(EntityFieldSetter::class);
583
        $fieldSetter->setPathToProjectRoot($this->copiedWorkDir ?? static::WORK_DIR)
584
                    ->setProjectRootNamespace($this->copiedRootNamespace ?? static::TEST_PROJECT_ROOT_NAMESPACE);
585
586
        return $fieldSetter;
587
    }
588
589
    protected function getSchema(): Schema
590
    {
591
        return $this->container->get(Schema::class);
592
    }
593
594
    protected function getCodeHelper(): CodeHelper
595
    {
596
        return $this->container->get(CodeHelper::class);
597
    }
598
599
    protected function getUnusedRelationsRemover(): UnusedRelationsRemover
600
    {
601
        return $this->container->get(UnusedRelationsRemover::class);
602
    }
603
604
    protected function createEntity(string $entityFqn): EntityInterface
605
    {
606
        return $this->getEntityFactory()->create($entityFqn);
607
    }
608
609
    protected function getEntityFactory(): EntityFactory
610
    {
611
        $factory = $this->container->get(EntityFactory::class);
612
        $factory->setEntityManager($this->getEntityManager());
613
614
        return $factory;
615
    }
616
}
617