Skip to content

Commit f077ccd

Browse files
committed
Progress
1 parent 9845ac7 commit f077ccd

File tree

6 files changed

+104
-26
lines changed

6 files changed

+104
-26
lines changed

.devcontainer/Dockerfile

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
FROM php:8.4-cli-trixie
22

33
RUN apt-get update && apt-get install -y --no-install-recommends \
4-
file \
5-
git \
4+
file \
5+
git \
66
openssh-client \
7-
&& rm -rf /var/lib/apt/lists/*
7+
&& rm -rf /var/lib/apt/lists/*
88

99
ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
1010

1111
RUN set -eux; \
12-
install-php-extensions \
13-
@composer \
14-
apcu \
15-
opcache \
16-
xdebug \
17-
;
12+
install-php-extensions \
13+
@composer \
14+
apcu \
15+
opcache \
16+
xdebug \
17+
;
1818

1919
RUN useradd -m vscode
2020

src/A2lixAutoFormBundle.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@
1212
namespace A2lix\AutoFormBundle;
1313

1414
use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
15+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1516
use Symfony\Component\DependencyInjection\ContainerBuilder;
1617
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
1718
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
1819

19-
final class A2lixAutoFormBundle extends AbstractBundle
20+
final class A2lixAutoFormBundle extends AbstractBundle implements CompilerPassInterface
2021
{
2122
#[\Override]
2223
public function configure(DefinitionConfigurator $definition): void
@@ -42,4 +43,18 @@ public function loadExtension(array $config, ContainerConfigurator $container, C
4243
->arg('$globalExcludedChildren', $config['children_excluded'])
4344
;
4445
}
46+
47+
public function build(ContainerBuilder $container): void
48+
{
49+
$container->addCompilerPass($this);
50+
}
51+
52+
public function process(ContainerBuilder $container): void
53+
{
54+
if ($container->hasExtension('a2lix_translation_form')) {
55+
$container->getDefinition('a2lix_auto_form.form.type.auto_type')
56+
->setArgument('$globalTranslatedChildren', true)
57+
;
58+
}
59+
}
4560
}

src/Form/Attribute/AutoTypeCustom.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public function __construct(
3030
private ?string $name = null,
3131
private ?bool $excluded = null,
3232
private ?bool $embedded = null,
33+
private ?bool $translated = null,
3334
private ?array $groups = null,
3435
) {}
3536

@@ -45,6 +46,7 @@ public function getOptions(): array
4546
...(null !== $this->name ? ['child_name' => $this->name] : []),
4647
...(null !== $this->excluded ? ['child_excluded' => $this->excluded] : []),
4748
...(null !== $this->embedded ? ['child_embedded' => $this->embedded] : []),
49+
...(null !== $this->embedded ? ['child_translated' => $this->translated] : []),
4850
...(null !== $this->groups ? ['child_groups' => $this->groups] : []),
4951
];
5052
}

src/Form/Builder/AutoTypeBuilder.php

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use A2lix\AutoFormBundle\Form\Attribute\AutoTypeCustom;
1515
use A2lix\AutoFormBundle\Form\Type\AutoType;
16+
use Doctrine\Common\Collections\ArrayCollection;
1617
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
1718
use Symfony\Component\Form\FormBuilderInterface;
1819
use Symfony\Component\Form\FormInterface;
@@ -26,6 +27,7 @@
2627
* child_name?: string,
2728
* child_excluded?: bool,
2829
* child_embedded?: bool,
30+
* child_translated?: bool,
2931
* child_groups?: list<string>,
3032
* ...
3133
* }
@@ -35,6 +37,7 @@
3537
* children: array<string, ChildOptions|ChildBuilderCallable>,
3638
* children_excluded: list<string>|"*",
3739
* children_embedded: list<string>|"*",
40+
* children_translated: bool,
3841
* children_groups: list<string>|null,
3942
* builder: FormBuilderCallable|null,
4043
* }
@@ -52,6 +55,7 @@ public function __construct(
5255
public function buildChildren(FormBuilderInterface $builder, array $formOptions): void
5356
{
5457
$dataClass = $this->getDataClass($form = $builder->getForm());
58+
dump($dataClass);
5559

5660
if (null === $classProperties = $this->propertyInfoExtractor->getProperties($dataClass)) {
5761
throw new \RuntimeException(\sprintf('Unable to extract properties of "%s".', $dataClass));
@@ -61,7 +65,7 @@ public function buildChildren(FormBuilderInterface $builder, array $formOptions)
6165
$allChildrenExcluded = '*' === $formOptions['children_excluded'];
6266
$allChildrenEmbedded = '*' === $formOptions['children_embedded'];
6367
$childrenGroups = $formOptions['children_groups'] ?? ['Default'];
64-
$formLevel = $this->getFormLevel($form);
68+
$formDepth = $this->getFormDepth($form);
6569

6670
/** @var list<string> $classProperties */
6771
foreach ($classProperties as $classProperty) {
@@ -116,12 +120,17 @@ public function buildChildren(FormBuilderInterface $builder, array $formOptions)
116120
// PropertyInfo? Enrich childOptions
117121
if (null !== $propTypeInfo = $this->propertyInfoExtractor->getType($dataClass, $classProperty)) {
118122
// @phpstan-ignore argument.type
123+
$formChildTranslated = ($formOptions['children_translated'] || ($childOptions['child_translated'] ?? false))
124+
&& ('translations' === $classProperty);
125+
// @phpstan-ignore argument.type
119126
$formChildEmbedded = $allChildrenEmbedded || \in_array($classProperty, $formOptions['children_embedded'], true)
120127
|| ($childOptions['child_embedded'] ?? false);
121128

122-
if ($formChildEmbedded) {
123-
$childOptions = $this->updateChildOptions($childOptions, $propTypeInfo, $formLevel);
124-
}
129+
$childOptions = match (true) {
130+
$formChildTranslated => $this->updateTranslatedChildOptions($childOptions, $propTypeInfo, $refProperty),
131+
$formChildEmbedded => $this->updateEmbeddedChildOptions($childOptions, $propTypeInfo, $refProperty, $formDepth),
132+
default => $childOptions,
133+
};
125134
}
126135

127136
$this->addChild($builder, $classProperty, $childOptions);
@@ -171,6 +180,7 @@ private function addChild(FormBuilderInterface $builder, string|FormBuilderInter
171180
$options['child_type'],
172181
$options['child_excluded'],
173182
$options['child_embedded'],
183+
$options['child_translated'],
174184
$options['child_groups'],
175185
);
176186

@@ -193,14 +203,41 @@ private function getDataClass(FormInterface $form): string
193203

194204
throw new \RuntimeException('Unable to get dataClass');
195205
}
206+
/**
207+
* @param ChildOptions $baseChildOptions
208+
*
209+
* @return ChildOptions
210+
*/
211+
private function updateTranslatedChildOptions(
212+
array $baseChildOptions,
213+
TypeInfo $propTypeInfo,
214+
\ReflectionProperty $refProperty,
215+
): array {
216+
if (!$propTypeInfo instanceof TypeInfo\CollectionType) {
217+
return [];
218+
}
219+
220+
dump($refProperty);
221+
222+
return [
223+
'child_type' => 'A2lix\TranslationFormBundle\Form\Type\TranslationsType',
224+
'translation_class' => $propTypeInfo->getCollectionValueType()->getClassName(),
225+
'required' => $propTypeInfo->isNullable(),
226+
...$baseChildOptions,
227+
];
228+
}
196229

197230
/**
198231
* @param ChildOptions $baseChildOptions
199232
*
200233
* @return ChildOptions
201234
*/
202-
private function updateChildOptions(array $baseChildOptions, TypeInfo $propTypeInfo, int $formLevel): array
203-
{
235+
private function updateEmbeddedChildOptions(
236+
array $baseChildOptions,
237+
TypeInfo $propTypeInfo,
238+
\ReflectionProperty $refProperty,
239+
int $formDepth
240+
): array {
204241
// TypeInfo matching native FormType? Abort, guessers are enough
205242
if (self::isTypeInfoWithMatchingNativeFormType($propTypeInfo)) {
206243
return $baseChildOptions;
@@ -214,7 +251,7 @@ private function updateChildOptions(array $baseChildOptions, TypeInfo $propTypeI
214251
'allow_delete' => true,
215252
'delete_empty' => true,
216253
'by_reference' => false,
217-
'prototype_name' => '__name'.$formLevel.'__',
254+
'prototype_name' => '__name'.$formDepth.'__',
218255
...$baseChildOptions,
219256
];
220257

@@ -279,18 +316,18 @@ private static function isTypeInfoWithMatchingNativeFormType(TypeInfo $propTypeI
279316
/**
280317
* @param FormInterface<mixed> $form
281318
*/
282-
private function getFormLevel(FormInterface $form): int
319+
private function getFormDepth(FormInterface $form): int
283320
{
284321
if ($form->isRoot()) {
285322
return 0;
286323
}
287324

288-
$level = 0;
325+
$depth = 0;
289326
while (null !== $formParent = $form->getParent()) {
290327
$form = $formParent;
291-
++$level;
328+
++$depth;
292329
}
293330

294-
return $level;
331+
return $depth;
295332
}
296333
}

src/Form/Type/AutoType.php

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,13 @@ final class AutoType extends AbstractType
2626
{
2727
/**
2828
* @param list<string> $globalExcludedChildren
29+
* @param list<string> $globalEmbeddedChildren
2930
*/
3031
public function __construct(
3132
private readonly AutoTypeBuilder $autoTypeBuilder,
3233
private readonly array $globalExcludedChildren = [],
34+
private readonly array $globalEmbeddedChildren = [],
35+
private readonly bool $globalTranslatedChildren = false,
3336
) {}
3437

3538
#[\Override]
@@ -45,25 +48,45 @@ public function configureOptions(OptionsResolver $resolver): void
4548
$resolver->setDefaults([
4649
'children' => [],
4750
'children_excluded' => $this->globalExcludedChildren,
48-
'children_embedded' => [],
51+
'children_embedded' => $this->globalEmbeddedChildren,
52+
'children_translated' => $this->globalTranslatedChildren,
4953
'children_groups' => null,
5054
'builder' => null,
5155
]);
5256

53-
$resolver->setAllowedTypes('children_excluded', 'string[]|string');
54-
$resolver->setAllowedTypes('children_embedded', 'string[]|string');
57+
$resolver->setAllowedTypes('children_excluded', 'string[]|string|callable');
58+
$resolver->setInfo('children_excluded', 'An array of properties, the * wildcard, or a callable (mixed $previousValue): mixed');
59+
$resolver->addNormalizer('children_excluded', static function (Options $options, mixed $value): mixed {
60+
if (is_callable($value)) {
61+
return ($value)($options['children_excluded']);
62+
}
63+
64+
return $value;
65+
});
66+
67+
$resolver->setAllowedTypes('children_embedded', 'string[]|string|callable');
68+
$resolver->setInfo('children_embedded', 'An array of properties, the * wildcard, or a callable (mixed $previousValue): mixed');
69+
$resolver->addNormalizer('children_embedded', static function (Options $options, mixed $value): mixed {
70+
if (is_callable($value)) {
71+
return ($value)($options['children_embedded']);
72+
}
73+
74+
return $value;
75+
});
76+
77+
$resolver->setAllowedTypes('children_translated', 'bool');
5578
$resolver->setAllowedTypes('children_groups', 'string[]|null');
5679
$resolver->setAllowedTypes('builder', 'callable|null');
57-
$resolver->setInfo('builder', 'A callable that accepts two arguments (FormBuilderInterface $builder, string[] $classProperties). It should not return anything.');
80+
$resolver->setInfo('builder', 'A callable (FormBuilderInterface $builder, string[] $classProperties): void');
5881

82+
// Others defaults FormType:class options
5983
$resolver->setNormalizer('data_class', static function (Options $options, ?string $value): string {
6084
if (null === $value) {
6185
throw new \RuntimeException('Missing "data_class" option of "AutoType".');
6286
}
6387

6488
return $value;
6589
});
66-
6790
$resolver->setDefault('validation_groups', static function (Options $options): ?array {
6891
/** @var list<string>|null */
6992
return $options['children_groups'];

src/Form/TypeGuesser/TypeInfoTypeGuesser.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public function guessType(string $class, string $property): ?TypeGuess
3737

3838
// FormTypes handling 'multiple' option
3939
if ($typeInfo->isIdentifiedBy(TypeIdentifier::ARRAY)) {
40+
dump($typeInfo);
4041
/** @var TypeInfo\CollectionType $typeInfo */
4142
// @phpstan-ignore missingType.generics
4243
$collValueType = $typeInfo->getCollectionValueType();

0 commit comments

Comments
 (0)