I work with Symfony 2.8.3
I've read these resources:
-
How to Create a Custom Form Field Type
-
Symfony2: Overriding a built-in field type with a custom field type having the same name
and some other ones wich were mentioned in them.
It's necessary to create such 'date' field type wich would satisfy the customer in many parameters including format. And this field type (created by me) must be used everywhere in the project even in other bundles.
I create a class:
class DateType extends \Symfony\Component\Form\Extension\Core\Type\DateType {
// Here I overload the necessary methods
}
Register it in services:
services:
#...
form.type.date:
class: Application\Symfony\Component\Form\Extension\Core\Type\DateType
tags:
- { name: form.type, alias: date }
#...
and use it in a form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('birthday', 'date', array(
'widget' => 'single_text',
'label' => 'form.label.birthday',
'translation_domain' => $this->translation_domain,
'required' => true,
'invalid_message' => 'form.error.birthday',
'attr' => array(
'class' => 'datepicker without-error-text',
),
'format' => 'dd-MM-yyyy',
));
}
However this code throws an exception:
The field type "Symfony\Component\Form\Extension\Core\Type\DateType" is not registered with the service container.
What does it mean, 'not registered'?!! Ah, yeah, not registered )). That’s because I registered my field type instead of the built-in one.
One of the resources that I've shown above has an advice regarding how to register customer’s type forcedly.
Ok, let’s do it.
I create a self compiler:
namespace Acme\DemoBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class OverrideServiceCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$definition = $container->getDefinition('form.type.date');
$definition->setClass('Acme\DemoBundle\Form\Type\DateType');
}
}
Use it while building the main bundle:
// src/Acme/DemoBundle/AcmeDemoBundle.php
namespace Acme\DemoBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Acme\DemoBundle\DependencyInjection\Compiler\OverrideServiceCompilerPass;
class AcmeDemoBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new OverrideServiceCompilerPass());
}
}
But it’s still unsuccessful. That code throws an exception again but this time it was different:
The type name specified for the service "form.type.date" does not match the actual name. Expected "Symfony\Component\Form\Extension\Core\Type\DateType", given "Application\Symfony\Component\Form\Extension\Core\Type\DateType"
After researching the code I find out that the Sonata Project throws the exception from its different bundles. In my projetct I use Sonata Admin and Sonata Blocks. These bundles can't create custom date type field in their forms. Because they recieve differentt names of classes.
//vendor\sonata-project\core-bundle\Form\Extension\DependencyInjectionExtension.php
// this block: if ($name !== get_class($type) && (method_exists($type, 'getName') && $type->getName() !== $name)) {...}
/**
* {@inheritdoc}
*/
public function getType($name)
{
// resolve code to FQCN
$name = self::findClass($this->mappingTypes, $name);
if (!isset($this->typeServiceIds[$name])) {
throw new InvalidArgumentException(sprintf('The field type "%s" is not registered with the service container.', $name));
}
$type = $this->container->get($this->typeServiceIds[$name]);
if ($name !== get_class($type) && (method_exists($type, 'getName') && $type->getName() !== $name)) {
throw new InvalidArgumentException(
sprintf('The type name specified for the service "%s" does not match the actual name. Expected "%s", given "%s"',
$this->typeServiceIds[$name],
$name,
get_class($type)
));
}
return $type;
}
And now tell me please what I do wrong.
Thank you very much.