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

tito10047 / type-safe-id-bundle / 26300601897

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

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

15.73 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
    }
27✔
30

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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