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

tito10047 / type-safe-id-bundle / 23183550196

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

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

97.2
/src/Maker/MakeTypeSafeEntity.php
1
<?php
2

3
namespace Tito10047\TypeSafeIdBundle\Maker;
4

5
use Symfony\Bundle\MakerBundle\ConsoleStyle;
6
use Symfony\Bundle\MakerBundle\DependencyBuilder;
7
use Symfony\Bundle\MakerBundle\Generator;
8
use Symfony\Bundle\MakerBundle\InputConfiguration;
9
use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
10
use Symfony\Bundle\MakerBundle\Maker\Common\EntityIdTypeEnum;
11
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
12
use Symfony\Component\Console\Command\Command;
13
use Symfony\Component\Console\Input\InputArgument;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Input\InputOption;
16
use Symfony\Component\Uid\Ulid;
17
use Symfony\Component\Uid\UuidV7;
18
use Symfony\Bridge\Doctrine\Types\AbstractUidType;
19
use Tito10047\TypeSafeIdBundle\AbstractIntIdType;
20
use Tito10047\TypeSafeIdBundle\Util\PathUtil;
21

22
class MakeTypeSafeEntity extends AbstractMaker
23
{
24
    public function __construct(
25
        private readonly string $entityNamespace,
26
        private readonly string $typeIdNamespace,
27
        private readonly string $repositoryNamespace,
28
    ) {
29
    }
54✔
30

31
    public static function getCommandName(): string
32
    {
33
        return 'make:entity:type';
42✔
34
    }
35

36
    public static function getCommandDescription(): string
37
    {
38
        return 'Creates a new Doctrine entity class with type-safe ID';
42✔
39
    }
40

41
    public function configureCommand(Command $command, InputConfiguration $inputConfig)
42
    {
43
        $command
42✔
44
            ->addArgument('name', InputArgument::OPTIONAL, 'The name of the entity class (e.g. <alternate>Foo</alternate>)')
42✔
45
            ->addOption('with-ulid', null, InputOption::VALUE_NONE, 'Use ULID as ID')
42✔
46
            ->addOption('with-uuid', null, InputOption::VALUE_NONE, 'Use UUID as ID')
42✔
47
            ->setHelp(file_get_contents(__DIR__ . '/../../docs/make_entity_typesafe.txt') ?: 'Creates a new Doctrine entity class with type-safe ID')
42✔
48
        ;
42✔
49
    }
50

51
    public function configureDependencies(DependencyBuilder $dependencies)
52
    {
53
        // No extra dependencies needed beyond MakerBundle and Doctrine
54
    }
36✔
55

56
    public function interact(InputInterface $input, ConsoleStyle $io, Command $command)
57
    {
NEW
58
        if (null === $input->getArgument('name')) {
×
NEW
59
            $argument = $command->getDefinition()->getArgument('name');
×
NEW
60
            $input->setArgument('name', $io->ask($argument->getDescription()));
×
61
        }
62
    }
63

64
    public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
65
    {
66
        $className = $input->getArgument('name');
36✔
67
        
68
        $idType = EntityIdTypeEnum::INT;
36✔
69
        if ($input->getOption('with-ulid')) {
36✔
70
            $idType = EntityIdTypeEnum::ULID;
12✔
71
        } elseif ($input->getOption('with-uuid')) {
24✔
72
            $idType = EntityIdTypeEnum::UUID;
12✔
73
        }
74

75
        $rootNamespace = $generator->getRootNamespace();
36✔
76
                $rootNamespace = trim($rootNamespace, '\\');
36✔
77
        
78
        $entityNamespace = trim($this->entityNamespace, '\\');
36✔
79
        if (str_starts_with($entityNamespace, $rootNamespace . '\\')) {
36✔
80
            $entityNamespace = substr($entityNamespace, strlen($rootNamespace) + 1);
36✔
81
        }
82

83
        $typeIdNamespace = trim($this->typeIdNamespace, '\\');
36✔
84
        if (str_starts_with($typeIdNamespace, $rootNamespace . '\\')) {
36✔
85
            $typeIdNamespace = substr($typeIdNamespace, strlen($rootNamespace) + 1);
36✔
86
        }
87

88
        $repositoryNamespace = trim($this->repositoryNamespace, '\\');
36✔
89
        if (str_starts_with($repositoryNamespace, $rootNamespace . '\\')) {
36✔
90
            $repositoryNamespace = substr($repositoryNamespace, strlen($rootNamespace) + 1);
36✔
91
        }
92

93
        $entityClassDetails = $generator->createClassNameDetails($className, $entityNamespace);
36✔
94
        $idClassDetails = $generator->createClassNameDetails($className . 'Id', $typeIdNamespace);
36✔
95
        $idTypeClassDetails = $generator->createClassNameDetails($className . 'IdType', $typeIdNamespace);
36✔
96
        $repositoryClassDetails = $generator->createClassNameDetails($className, $repositoryNamespace, 'Repository');
36✔
97

98
        // 1. Generate ID class
99
        $useStatements = new UseStatementGenerator([]);
36✔
100
        if ($idType === EntityIdTypeEnum::UUID) {
36✔
101
            $useStatements->addUseStatement(UuidV7::class);
12✔
102
        } elseif ($idType === EntityIdTypeEnum::ULID) {
24✔
103
            $useStatements->addUseStatement(Ulid::class);
12✔
104
        }
105
        
106
        $generator->generateClass(
36✔
107
            $idClassDetails->getFullName(),
36✔
108
            __DIR__ . '/../../templates/extension/maker/TypeId.tpl.php',
36✔
109
            [
36✔
110
                'id_type' => $idType,
36✔
111
                'use_statements' => $useStatements,
36✔
112
            ]
36✔
113
        );
36✔
114

115
        // 2. Generate ID Type class
116
        $useStatements = new UseStatementGenerator([]);
36✔
117
        $useStatements->addUseStatement($idClassDetails->getFullName());
36✔
118
        if ($idType === EntityIdTypeEnum::UUID || $idType === EntityIdTypeEnum::ULID) {
36✔
119
            $useStatements->addUseStatement(AbstractUidType::class);
24✔
120
        } else {
121
            $useStatements->addUseStatement(AbstractIntIdType::class);
12✔
122
        }
123
        
124
        $generator->generateClass(
36✔
125
            $idTypeClassDetails->getFullName(),
36✔
126
            __DIR__ . '/../../templates/extension/maker/TypeIdType.tpl.php',
36✔
127
            [
36✔
128
                'id_type' => $idType,
36✔
129
                'id_class' => $idClassDetails->getShortName(),
36✔
130
                'use_statements' => $useStatements,
36✔
131
            ]
36✔
132
        );
36✔
133

134
        // 3. Generate Repository
135
        $useStatements = new UseStatementGenerator([]);
36✔
136
        $useStatements->addUseStatement(\Doctrine\ORM\EntityRepository::class); // Fallback or use ServiceEntityRepository
36✔
137
        $useStatements->addUseStatement(\Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository::class);
36✔
138
        $useStatements->addUseStatement(\Doctrine\Persistence\ManagerRegistry::class);
36✔
139
        $useStatements->addUseStatement($entityClassDetails->getFullName());
36✔
140
        $useStatements->addUseStatement($idClassDetails->getFullName());
36✔
141
        $useStatements->addUseStatement(\Doctrine\ORM\QueryBuilder::class);
36✔
142

143
        $generator->generateClass(
36✔
144
            $repositoryClassDetails->getFullName(),
36✔
145
            __DIR__ . '/../../templates/extension/maker/Repository.tpl.php',
36✔
146
            [
36✔
147
                'entity_full_class_name' => $entityClassDetails->getFullName(),
36✔
148
                'entity_class_name' => $entityClassDetails->getShortName(),
36✔
149
                'id_class' => $idClassDetails->getShortName(),
36✔
150
                'id_type' => $idType,
36✔
151
                'use_statements' => $useStatements,
36✔
152
                'include_example_comments' => false,
36✔
153
                'with_password_upgrade' => false,
36✔
154
                'entity_alias' => strtolower($entityClassDetails->getShortName()[0]),
36✔
155
            ]
36✔
156
        );
36✔
157

158
        // 4. Generate Entity
159
        $useStatements = new UseStatementGenerator([]);
36✔
160
        $useStatements->addUseStatement([[\Doctrine\ORM\Mapping::class => 'ORM']]);
36✔
161
        $useStatements->addUseStatement($idClassDetails->getFullName());
36✔
162
        $useStatements->addUseStatement($idTypeClassDetails->getFullName());
36✔
163

164
        $generator->generateClass(
36✔
165
            $entityClassDetails->getFullName(),
36✔
166
            __DIR__ . '/../../templates/extension/maker/Entity.tpl.php',
36✔
167
            [
36✔
168
                'repository_class_name' => $repositoryClassDetails->getShortName(),
36✔
169
                'id_type' => $idType,
36✔
170
                'id_generator_service' => 'doctrine.id_generator.universal',
36✔
171
                'use_statements' => $useStatements,
36✔
172
                'should_escape_table_name' => false,
36✔
173
                'api_resource' => false,
36✔
174
                'broadcast' => false,
36✔
175
                'table_name' => null, // Will be handled by Doctrine naming strategy
36✔
176
            ]
36✔
177
        );
36✔
178

179
        $generator->writeChanges();
36✔
180

181
        $this->writeSuccessMessage($io);
36✔
182
        
183
        $io->text([
36✔
184
            'Next: You can now add fields to your entity using the official make:entity command:',
36✔
185
            sprintf(' <fg=yellow>php bin/console make:entity %s</>', $className),
36✔
186
            '',
36✔
187
        ]);
36✔
188
    }
189

190
}
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