How does Symfony 2 find custom form types?

2020-03-31 08:45发布

I was reading up of Data Transformations I modified it somewhat in my code. I am getting

Could not load type "csv2tags"

I have a custom form type

class CSV2TagsType extends AbstractType {
    public function buildForm(FormBuilder $builder, array $opts) {
        $builder->appendClientTransformer(new CSV2TagsTransformer());
    }
    public function getParent() {
        return 'text';
    }
    public function getName() {
        return 'csv2tags';
    }
}

Using the transformer:

class CSV2TagsTransformer implements DataTransformerInterface {
    /**
     * @var EntityManager
     */
    protected $em;
    public function __construct(EntityManager $em) {
        $this->em = $em;
    }
    // ... 
}

In services.yml

services:
    jm.blog.csv2tagsTransformer:
        class: JM\BlogBundle\Form\DataTransformer\CSV2TagsTransformer
        arguments: ["@doctrine.orm.entity_manager"]

A few questions:

  • I moved EntityManager to the transformer instead of the form type, is that OK?
  • I didn't declare an alias for the transformer service like in the docs. I thought the alias for the form type was from the AbstractType::getName() function? Do I need to declare my custom form type as a service too
  • I moved the EntityManager to the transformer. Did I do the right thing when I did new CSV2TagsTransformer() without the EntityManager?

2条回答
相关推荐>>
2楼-- · 2020-03-31 09:06

Regarding your main problem, in your services.yml file, you are supposed to tag your service as form.type, like this:

services:
    jm.blog.csv2tagsTransformer:
        class: JM\BlogBundle\Form\DataTransformer\CSV2TagsTransformer
        arguments: ["@doctrine.orm.entity_manager"]
        tags:
          - { name: form.type, alias: csv2tags }

For the other problems, I have no idea.

查看更多
Ridiculous、
3楼-- · 2020-03-31 09:17

I will just complete the answer given by @greg0ire which is almost right.

First, the service you defined should be your form type (CSV2TagsType) and not your transformer (CSV2TagsTransformer) because it is the form type that is used by the form builder. Since the form builder expect an AbstractType, it will not work with your service definition since the data transformer is not an AbstractType. As @greg0ire said, you must tag your custom form type with the form.type tag.

services:
  jm.blog.csv2tagsType:
    class: JM\BlogBundle\Form\Type\CSV2TagsType
    arguments: ["@doctrine.orm.entity_manager"]
    tags:
      - { name: form.type, alias: csv2tags }

The value returned by the function getName in your custom form type must match the alias you gave (cvs2tags) or the service id you defined (jm.blog.csv2tagsType). This is the way Symfony can find your custom type. It tries to find the service id given by the getName() method.

For you data transformer, there is a litte problem. You are doing new CSV2TagsTransformer() but you are not passing the entity manager. This will result in an error. You cannot do it this way because there is no way to inject the data transformer in a service way.

What you need to do is to inject the entity manager in you custom form type and then pass it to the data transformer when you instantiate it.

class CSV2TagsType extends AbstractType 
{
    protected $em;

    public function __construct(EntityManager $em) {
        $this->em = $em;
    }

    public function buildForm(FormBuilder $builder, array $opts) {
        $builder->appendClientTransformer(new CSV2TagsTransformer($this->em));
    }

    // Rest of class
}

This way, you inject the entity manager in your form type, which is possible because the form type is defined as a service. And in the form type, you instantiate the data transformer and pass to the construct the entity manager that have been injected into the form type.

Another way to go would be to also declare you data transformer as a service and then inject it in the form type. This way, you do not instantiate a data transformer yourself but you use the one injected in the constructor of the form type. So, if you don't need the entity manager in the form type, you can omit to inject it. Here an example of this alternative:

services:
  jm.blog.csv2tagsTransformer:
    class: JM\BlogBundle\Form\DataTransformer\CSV2TagsTransformer
    arguments: ["@doctrine.orm.entity_manager"]

  jm.blog.csv2tagsType:
    class: JM\BlogBundle\Form\Type\CSV2TagsType
    arguments: ["@jm.blog.csv2tagsTransformer"]
    tags:
      - { name: form.type, alias: csv2tags }

class CSV2TagsType extends AbstractType 
{
    protected $transformer;

    public function __construct(CSV2TagsTransformer $transformer) {
        $this->transformer= $transformer;
    }

    public function buildForm(FormBuilder $builder, array $opts) {
        $builder->appendClientTransformer($this->transformer);
    }

    // Rest of class
}

Here a small resume of the answers to your question:

  1. It is correct.
  2. No alias is needed for the transformer service. But as @greg0ire said, you need to define a service for you form type. The getName() function must returned the id that should be used. It can be the alias defined, or the service id directly. Usually, people use the alias as the value returned by getName.
  3. It is not correct to do new CSV2TagsTransformer() because this way, you a not sending the entity manager to your class and since it can't accept null value, it will fail in PHP.

Hope this helps.

Regards,
Matt

查看更多
登录 后发表回答