• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tito10047 / type-safe-id-bundle / 23204991048

17 Mar 2026 07:38AM UTC coverage: 91.88% (+13.3%) from 78.621%
23204991048

push

github

web-flow
Merge pull request #2 from tito10047/v2

Add custom maker command
add tests
more refactoring

139 of 154 new or added lines in 4 files covered. (90.26%)

1 existing line in 1 file now uncovered.

215 of 234 relevant lines covered (91.88%)

31.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.02
/src/EntityIdTypeRegisterCompilerPass.php
1
<?php
2

3
namespace Tito10047\TypeSafeIdBundle;
4

5
use Symfony\Bridge\Doctrine\Types\AbstractUidType;
6
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
7
use Symfony\Component\DependencyInjection\ContainerBuilder;
8
use Symfony\Component\DependencyInjection\Definition;
9
use Symfony\Component\DependencyInjection\Reference;
10
use Symfony\Component\Uid\Ulid;
11
use Symfony\Component\Uid\Uuid;
12
use League\ConstructFinder\ConstructFinder;
13
use Tito10047\TypeSafeIdBundle\IdGenerator\UniversalTypeIdGenerator;
14
use Tito10047\TypeSafeIdBundle\Util\PathUtil;
15

16
class EntityIdTypeRegisterCompilerPass implements CompilerPassInterface {
17

18
        private const CONTAINER_TYPES_PARAMETER = 'doctrine.dbal.connection_factory.types';
19

20
        public function __construct(
21
                private string $projectDir,
22
        ) {
23
        }
54✔
24

25
        public function process(ContainerBuilder $container): void
26
        {
27
                // throw new \Exception('CompilerPass is running');
28
                if (!$container->hasParameter('type_safe_id.entity_namespace')) {
48✔
29
                        return;
6✔
30
                }
31

32
                /** @var array<string, array{class: class-string}> $typeDefinition */
33
                $typeDefinition = $container->getParameter(self::CONTAINER_TYPES_PARAMETER);
42✔
34

35
                $entityNamespace = $container->getParameter('type_safe_id.entity_namespace');
42✔
36
                $typeIdNamespace = $container->getParameter('type_safe_id.type_id_namespace');
42✔
37

38
                $types = $this->generateTypes($container, PathUtil::namespaceToPath($typeIdNamespace));
42✔
39

40
                // Build entity -> ID class mapping for universal generator
41
                $entityToIdClassMap = [];
42✔
42

43
                /** @var array{namespace: string, name: string, id_class: string} $type */
44
                foreach ($types as $type) {
42✔
45
                        $name      = $type['name'];
42✔
46
                        $namespace = $type['namespace'];
42✔
47
                        $idClass   = $type['id_class'];
42✔
48

49
                        if (array_key_exists($name, $typeDefinition)) {
42✔
UNCOV
50
                                continue;
×
51
                        }
52

53
                        $typeDefinition[$name] = ['class' => $namespace];
42✔
54

55
                        // Build mapping: Entity class -> ID class
56
                        if ($idClass) {
42✔
57
                                // Convert App\Domain\ValueObject\ProductId to App\Domain\Entity\Product
58
                                $entityClass = str_replace($typeIdNamespace, $entityNamespace, $idClass);
42✔
59
                                $entityClass = preg_replace('/Id$/', '', $entityClass);
42✔
60

61
                                $entityToIdClassMap[$entityClass] = $idClass;
42✔
62
                        }
63
                }
64

65
                // Register single universal ID generator service with the mapping
66
                if (!empty($entityToIdClassMap)) {
42✔
67
                        $generatorDefinition = new Definition(UniversalTypeIdGenerator::class, [$entityToIdClassMap]);
42✔
68
                        $generatorDefinition->setPublic(false);
42✔
69
                        $generatorDefinition->addTag('doctrine.id_generator');
42✔
70
                        $container->setDefinition('doctrine.id_generator.universal', $generatorDefinition);
42✔
71
                }
72

73
                $container->setParameter(self::CONTAINER_TYPES_PARAMETER, $typeDefinition);
42✔
74
        }
75

76
        /** @return iterable<int, array{namespace: class-string, name: string, id_class: string}> */
77
        private function generateTypes(ContainerBuilder $container, string $typeIdPath): iterable
78
        {
79
                $searchPath = $typeIdPath;
42✔
80
                if (!str_starts_with($searchPath, '/') && !str_contains($searchPath, ':')) {
42✔
NEW
81
                        $searchPath = sprintf('%s/%s', $this->projectDir, $typeIdPath);
×
82
                }
83

84
                if (!is_dir($searchPath)) {
42✔
NEW
85
                        return;
×
86
                }
87

88
                $classNames = ConstructFinder::locatedIn($searchPath)->findClassNames();
42✔
89

90
                foreach ($classNames as $className) {
42✔
91
                        $reflection = new \ReflectionClass($className);
42✔
92

93
                        if (! $reflection->isSubclassOf(AbstractUidType::class) && !$reflection->isSubclassOf(AbstractIntIdType::class) ) {
42✔
94
                                continue;
42✔
95
                        }
96

97
                        // Find the corresponding entity ID class
98
                        $idClass = null;
42✔
99
                        if (str_ends_with($className, 'Type')) {
42✔
100
                                $idClass = substr($className, 0, -4); // Remove 'Type' suffix
42✔
101
                        }
102

103
                        yield [
42✔
104
                                'namespace' => $reflection->getName(),
42✔
105
                                'name'      => $reflection->getName(),
42✔
106
                                'id_class'  => $idClass,
42✔
107
                        ];
42✔
108
                }
109
        }
110

111
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc