Skip to content

Commit fb404cc

Browse files
authored
feature #1589 [make:validator] generate final classes
* [make:validator] generate final classes - adds generated file comparison to test suite * simplify class generation * match expectations created in #1590
1 parent e7aeeb2 commit fb404cc

File tree

8 files changed

+116
-22
lines changed

8 files changed

+116
-22
lines changed

src/Generator.php

+30
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,36 @@ public function generateClass(string $className, string $templateName, array $va
7676
return $targetPath;
7777
}
7878

79+
/**
80+
* Future replacement for generateClass().
81+
*
82+
* @internal
83+
*
84+
* @param string $templateName Template name in Resources/skeleton to use
85+
* @param array $variables Array of variables to pass to the template
86+
*
87+
* @return string The path where the file will be created
88+
*
89+
* @throws \Exception
90+
*/
91+
final public function generateClassFromClassData(ClassData $classData, string $templateName, array $variables = []): string
92+
{
93+
$classData = $this->templateComponentGenerator->configureClass($classData);
94+
$targetPath = $this->fileManager->getRelativePathForFutureClass($classData->getFullClassName());
95+
96+
if (null === $targetPath) {
97+
throw new \LogicException(\sprintf('Could not determine where to locate the new class "%s", maybe try with a full namespace like "\\My\\Full\\Namespace\\%s"', $classData->getFullClassName(), $classData->getClassName()));
98+
}
99+
100+
$variables = array_merge($variables, [
101+
'class_data' => $classData,
102+
]);
103+
104+
$this->addOperation($targetPath, $templateName, $variables);
105+
106+
return $targetPath;
107+
}
108+
79109
/**
80110
* Generate a normal file from a template.
81111
*

src/Maker/MakeValidator.php

+19-11
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616
use Symfony\Bundle\MakerBundle\Generator;
1717
use Symfony\Bundle\MakerBundle\InputConfiguration;
1818
use Symfony\Bundle\MakerBundle\Str;
19+
use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassData;
1920
use Symfony\Component\Console\Command\Command;
2021
use Symfony\Component\Console\Input\InputArgument;
2122
use Symfony\Component\Console\Input\InputInterface;
23+
use Symfony\Component\Validator\Constraint;
24+
use Symfony\Component\Validator\ConstraintValidator;
2225
use Symfony\Component\Validator\Validation;
2326

2427
/**
@@ -49,26 +52,31 @@ public function configureCommand(Command $command, InputConfiguration $inputConf
4952
/** @return void */
5053
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
5154
{
52-
$validatorClassNameDetails = $generator->createClassNameDetails(
53-
$input->getArgument('name'),
54-
'Validator\\',
55-
'Validator'
55+
$validatorClassData = ClassData::create(
56+
class: \sprintf('Validator\\%s', $input->getArgument('name')),
57+
suffix: 'Validator',
58+
extendsClass: ConstraintValidator::class,
59+
useStatements: [
60+
Constraint::class,
61+
],
5662
);
5763

58-
$constraintFullClassName = Str::removeSuffix($validatorClassNameDetails->getFullName(), 'Validator');
64+
$constraintDataClass = ClassData::create(
65+
class: \sprintf('Validator\\%s', Str::removeSuffix($validatorClassData->getClassName(), 'Validator')),
66+
extendsClass: Constraint::class,
67+
);
5968

60-
$generator->generateClass(
61-
$validatorClassNameDetails->getFullName(),
69+
$generator->generateClassFromClassData(
70+
$validatorClassData,
6271
'validator/Validator.tpl.php',
6372
[
64-
'constraint_class_name' => Str::getShortClassName($constraintFullClassName),
73+
'constraint_class_name' => $constraintDataClass->getClassName(),
6574
]
6675
);
6776

68-
$generator->generateClass(
69-
$constraintFullClassName,
77+
$generator->generateClassFromClassData(
78+
$constraintDataClass,
7079
'validator/Constraint.tpl.php',
71-
[]
7280
);
7381

7482
$generator->writeChanges();

src/Maker/MakeVoter.php

+2-3
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,9 @@ class: \sprintf('Security\Voter\%s', $input->getArgument('name')),
6060
]
6161
);
6262

63-
$generator->generateClass(
64-
$voterClassData->getFullClassName(),
63+
$generator->generateClassFromClassData(
64+
$voterClassData,
6565
'security/Voter.tpl.php',
66-
['class_data' => $voterClassData]
6766
);
6867

6968
$generator->writeChanges();

src/Resources/skeleton/validator/Constraint.tpl.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<?= "<?php\n" ?>
22

3-
namespace <?= $namespace; ?>;
3+
namespace <?= $class_data->getNamespace(); ?>;
44

5-
use Symfony\Component\Validator\Constraint;
5+
<?= $class_data->getUseStatements(); ?>
66

77
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
8-
class <?= $class_name ?> extends Constraint
8+
<?= $class_data->getClassDeclaration(); ?>
99
{
1010
public string $message = 'The string "{{ string }}" contains an illegal character: it can only contain letters or numbers.';
1111

Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
<?= "<?php\n" ?>
22

3-
namespace <?= $namespace; ?>;
3+
namespace <?= $class_data->getNamespace(); ?>;
44

5-
use Symfony\Component\Validator\Constraint;
6-
use Symfony\Component\Validator\ConstraintValidator;
5+
<?= $class_data->getUseStatements(); ?>
76

8-
class <?= $class_name ?> extends ConstraintValidator
7+
<?= $class_data->getClassDeclaration(); ?>
98
{
109
public function validate(mixed $value, Constraint $constraint): void
1110
{
@@ -18,6 +17,7 @@ public function validate(mixed $value, Constraint $constraint): void
1817
// TODO: implement the validation here
1918
$this->context->buildViolation($constraint->message)
2019
->setParameter('{{ value }}', $value)
21-
->addViolation();
20+
->addViolation()
21+
;
2222
}
2323
}

tests/Maker/MakeValidatorTest.php

+12
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ public function getTestDetails(): \Generator
3232
'FooBar',
3333
]
3434
);
35+
36+
// Validator
37+
$expectedVoterPath = \dirname(__DIR__).'/fixtures/make-validator/expected/FooBarValidator.php';
38+
$generatedVoter = $runner->getPath('src/Validator/FooBarValidator.php');
39+
40+
self::assertSame(file_get_contents($expectedVoterPath), file_get_contents($generatedVoter));
41+
42+
// Constraint
43+
$expectedVoterPath = \dirname(__DIR__).'/fixtures/make-validator/expected/FooBar.php';
44+
$generatedVoter = $runner->getPath('src/Validator/FooBar.php');
45+
46+
self::assertSame(file_get_contents($expectedVoterPath), file_get_contents($generatedVoter));
3547
}),
3648
];
3749
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace App\Validator;
4+
5+
use Symfony\Component\Validator\Constraint;
6+
7+
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
8+
final class FooBar extends Constraint
9+
{
10+
public string $message = 'The string "{{ string }}" contains an illegal character: it can only contain letters or numbers.';
11+
12+
// You can use #[HasNamedArguments] to make some constraint options required.
13+
// All configurable options must be passed to the constructor.
14+
public function __construct(
15+
public string $mode = 'strict',
16+
?array $groups = null,
17+
mixed $payload = null
18+
) {
19+
parent::__construct([], $groups, $payload);
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace App\Validator;
4+
5+
use Symfony\Component\Validator\Constraint;
6+
use Symfony\Component\Validator\ConstraintValidator;
7+
8+
final class FooBarValidator extends ConstraintValidator
9+
{
10+
public function validate(mixed $value, Constraint $constraint): void
11+
{
12+
/* @var FooBar $constraint */
13+
14+
if (null === $value || '' === $value) {
15+
return;
16+
}
17+
18+
// TODO: implement the validation here
19+
$this->context->buildViolation($constraint->message)
20+
->setParameter('{{ value }}', $value)
21+
->addViolation()
22+
;
23+
}
24+
}

0 commit comments

Comments
 (0)