Skip to content

Commit 27e943c

Browse files
committed
Se agrega FormLoader con compiler pass que detecta los bundles con rutas de formularios.
1 parent 0f8dd9d commit 27e943c

6 files changed

Lines changed: 124 additions & 1 deletion

File tree

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
"php": "^8.5",
2929
"symfony/framework-bundle": "^8.0",
3030
"symfony/twig-bundle": "^8.0",
31-
"derafu/form": "*"
31+
"derafu/form": "*",
32+
"derafu/data-processor": "*"
3233
},
3334
"require-dev": {
3435
"ext-xdebug": "*",

config/services.yaml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,37 @@ services:
4646
arguments:
4747
$typeResolver: '@Derafu\Form\Type\TypeResolver'
4848

49+
# ── Data Processor ────────────────────────────────────────────────
50+
# Created via static factory with all default rules (cast, transform,
51+
# sanitize, validate). Apps can swap this for a custom-configured
52+
# processor by overriding the alias.
53+
54+
Derafu\DataProcessor\Contract\ProcessorInterface:
55+
factory: ['Derafu\DataProcessor\ProcessorFactory', 'createDefault']
56+
57+
# ── Form Data Processor ───────────────────────────────────────────
58+
# Bridges derafu/form schemas with derafu/data-processor rules.
59+
# SchemaToRulesMapper converts JSON Schema constraints (minLength,
60+
# format, required, etc.) into data-processor rule strings.
61+
62+
Derafu\Form\Processor\SchemaToRulesMapper: ~
63+
64+
Derafu\Form\Processor\FormDataProcessor:
65+
arguments:
66+
$mapper: '@Derafu\Form\Processor\SchemaToRulesMapper'
67+
$processor: '@Derafu\DataProcessor\Contract\ProcessorInterface'
68+
69+
# ── Form Loader ───────────────────────────────────────────────────
70+
# Uses PhpFormLoader from derafu/form. Files are .form.php scripts
71+
# that return a Closure (dynamic, receives context) or a plain array.
72+
# Paths are registered by FormPathsPass compiler pass:
73+
# 1. Bundle resources/forms/ directories (auto-discovered)
74+
# 2. App forms_path (highest priority, overrides bundles)
75+
76+
Derafu\Form\Loader\PhpFormLoader:
77+
arguments:
78+
$formFactory: '@Derafu\Form\Contract\Factory\FormFactoryInterface'
79+
4980
# ── Interface Aliases ─────────────────────────────────────────────
5081

5182
Derafu\Form\Contract\Factory\FormFactoryInterface:
@@ -56,3 +87,12 @@ services:
5687

5788
Derafu\Form\Contract\Type\TypeResolverInterface:
5889
alias: Derafu\Form\Type\TypeResolver
90+
91+
Derafu\Form\Contract\Processor\FormDataProcessorInterface:
92+
alias: Derafu\Form\Processor\FormDataProcessor
93+
94+
Derafu\Form\Contract\Processor\SchemaToRulesMapperInterface:
95+
alias: Derafu\Form\Processor\SchemaToRulesMapper
96+
97+
Derafu\Form\Contract\Loader\FormLoaderInterface:
98+
alias: Derafu\Form\Loader\PhpFormLoader
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Derafu: Symfony Form Bundle - Integrates derafu/form with Symfony.
7+
*
8+
* Copyright (c) 2026 Esteban De La Fuente Rubio / Derafu <https://www.derafu.dev>
9+
* Licensed under the MIT License.
10+
* See LICENSE file for more details.
11+
*/
12+
13+
namespace Derafu\FormBundle\DependencyInjection\Compiler;
14+
15+
use Derafu\Form\Loader\PhpFormLoader;
16+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
17+
use Symfony\Component\DependencyInjection\ContainerBuilder;
18+
19+
/**
20+
* Discovers form directories in registered bundles and wires them into
21+
* the {@see PhpFormLoader}.
22+
*
23+
* Convention: any bundle that has a `resources/forms/` directory gets
24+
* its forms auto-registered — zero configuration required.
25+
*
26+
* Priority (last `addPath()` wins):
27+
* 1. Bundle paths (in bundle registration order)
28+
* 2. App path (`derafu_form.forms_path`) — always highest priority
29+
*
30+
* This means the app can override any bundle-provided form by placing
31+
* a file with the same name in its own `resources/forms/` directory.
32+
*/
33+
final class FormPathsPass implements CompilerPassInterface
34+
{
35+
private const string FORMS_DIR = 'resources/forms';
36+
37+
public function process(ContainerBuilder $container): void
38+
{
39+
if (!$container->hasDefinition(PhpFormLoader::class)) {
40+
return;
41+
}
42+
43+
$loaderDefinition = $container->getDefinition(PhpFormLoader::class);
44+
45+
// 1. Add bundle paths (in registration order → lower priority).
46+
// kernel.bundles_metadata 'path' points to the namespace root
47+
// (e.g. src/), but resources/ is typically one level up at the
48+
// package root. Check both locations.
49+
$bundlesMetadata = $container->getParameter('kernel.bundles_metadata');
50+
foreach ($bundlesMetadata as $bundleMeta) {
51+
$basePath = $bundleMeta['path'];
52+
foreach ([$basePath, dirname($basePath)] as $candidate) {
53+
$formsDir = $candidate . '/' . self::FORMS_DIR;
54+
if (is_dir($formsDir)) {
55+
$loaderDefinition->addMethodCall('addPath', [$formsDir]);
56+
break;
57+
}
58+
}
59+
}
60+
61+
// 2. Add app path last (highest priority → overrides bundles).
62+
$appFormsPath = $container->getParameter('derafu_form.forms_path');
63+
$loaderDefinition->addMethodCall('addPath', [$appFormsPath]);
64+
}
65+
}

src/DependencyInjection/Configuration.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ public function getConfigTreeBuilder(): TreeBuilder
3131
->defaultValue('derafu_')
3232
->cannotBeEmpty()
3333
->end()
34+
->scalarNode('forms_path')
35+
->info('Directory for app-level form definition files (highest priority).')
36+
->defaultValue('%kernel.project_dir%/resources/forms')
37+
->end()
3438
->end()
3539
;
3640

src/DependencyInjection/FormExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public function load(array $configs, ContainerBuilder $container): void
2828
$config = $this->processConfiguration($configuration, $configs);
2929

3030
$container->setParameter('derafu_form.twig_prefix', $config['twig_prefix']);
31+
$container->setParameter('derafu_form.forms_path', $config['forms_path']);
3132

3233
$loader = new YamlFileLoader(
3334
$container,

src/FormBundle.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212

1313
namespace Derafu\FormBundle;
1414

15+
use Derafu\FormBundle\DependencyInjection\Compiler\FormPathsPass;
1516
use Derafu\FormBundle\DependencyInjection\FormExtension;
17+
use Symfony\Component\DependencyInjection\ContainerBuilder;
1618
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
1719
use Symfony\Component\HttpKernel\Bundle\Bundle;
1820

@@ -27,9 +29,19 @@
2729
* derafu/renderer) for form templates. A separate `FormTwigExtension`
2830
* is registered in Symfony's Twig with a prefix so page templates can
2931
* call `{{ derafu_form(form) }}` etc.
32+
*
33+
* A compiler pass auto-discovers `resources/forms/` directories in all
34+
* registered bundles and wires them into the PhpFormLoader. The app's
35+
* own forms directory has the highest priority (override pattern).
3036
*/
3137
final class FormBundle extends Bundle
3238
{
39+
public function build(ContainerBuilder $container): void
40+
{
41+
parent::build($container);
42+
$container->addCompilerPass(new FormPathsPass());
43+
}
44+
3345
public function getContainerExtension(): ?ExtensionInterface
3446
{
3547
if ($this->extension === null) {

0 commit comments

Comments
 (0)