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

tito10047 / type-safe-id-bundle / 23656383151

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

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%)

26.22 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
    }
45✔
30

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

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

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

51
    public function configureDependencies(DependencyBuilder $dependencies)
52
    {
53
        // No extra dependencies needed beyond MakerBundle and Doctrine
54
    }
30✔
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');
30✔
67
        
68
        $idType = EntityIdTypeEnum::INT;
30✔
69
        if ($input->getOption('with-ulid')) {
30✔
70
            $idType = EntityIdTypeEnum::ULID;
10✔
71
        } elseif ($input->getOption('with-uuid')) {
20✔
72
            $idType = EntityIdTypeEnum::UUID;
10✔
73
        }
74

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

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

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

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

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

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

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

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

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

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

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

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