Generating forms with Symfony 2.8 throws a Twig_Er

2019-02-16 04:37发布

问题:

Since the last LTS version of Symfony was released few days ago (30.11.2015) I started playing with it. Unfortunately I can't generate a CRUD with write actions with the same code that works fine in Symfony 2.7.7.

First I create a new Symfony project using the bash under Linux Mint 17.2:

symfony new tasks lts

The new directory tasks gets created with a new Symfony 2.8.0 project inside.

After adapting the database credentials in app/config/parameters.yml I create the database:

app/console doctrine:database:create

and generate a new bundle:

app/console generate:bundle --namespace=Acme/TasksBundle --format=yml

Then I create a new directory src/Acme/TasksBundle/Resources/config/doctrine and place two files for my models inside. These are:

Task.orm.yml

Acme\TasksBundle\Entity\Task:
    type: entity
    repositoryClass: Acme\TasksBundle\Repository\TaskRepository
    table: task
    id:
        id:
            type: integer
            generator: { strategy : AUTO }
    fields:
        description:
            type: text
    manyToMany:
        tags:
            targetEntity: Tag
            inversedBy: tasks
            cascade: [ "persist" ]
            joinTable:
                name: task_tag
                joinColumns:
                    task_id:
                        referencedColumnName: id
                inverseJoinColumns:
                    tag_id:
                        referencedColumnName: id

Tag.orm.yml

Acme\TasksBundle\Entity\Tag:
    type: entity
    repositoryClass: Acme\TasksBundle\Repository\TagRepository
    table: tag
    id:
        id:
            type: integer
            generator: { strategy : AUTO }
    fields:
        name:
            type: string
            length: 50
    manyToMany:
        tasks:
            targetEntity: Task
            mappedBy: tags

The database schema should like this:

+----------------+     +--------------+
| task           |     | task_tag     |     +---------+
+----------------+     +--------------+     | tag     |
|   id           |<--->|   task_id    |     +---------+
|   description  |     |   tag_id     |<--->|   id    |
+----------------+     +--------------+     |   name  |
                                            +---------+

Now I can generate the entities:

app/console generate:doctrine:entities AcmeTasksBundle

This works fine, so the database can be updated:

app/console doctrine:schema:update --force

Everything ok till now. The tables are in the database. Now I want to generate CRUD with write actions:

app/console generate:doctrine:crud --entity=AcmeTasksBundle:Task --with-write --format=yml

After confirming few questions it generates the CRUD and prints out:

Generating the CRUD code: OK

and afterwards throws this error:

[Twig_Error_Runtime]                                                                                    
Key "tags" for array with keys "id, description" does not exist in "form/FormType.php.twig" at line 29

The controller gets created, but not the form.

Generating the CRUD without write options works fine. The very same code works flawlessly with Symfony 2.7.7.

I checked the differences in the file form/FormType.php.twig between the versions and here are the relevant parts:

Symfony 2.7.7
vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/form/FormType.php.twig

{%- if fields|length > 0 %}
/**
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
    {%- for field in fields %}

        ->add('{{ field }}')
    {%- endfor %}

    ;
}
{% endif %}

Symfony 2.8.0
vendor/sensio/generator-bundle/Resources/skeleton/form/FormType.php.twig

{%- if fields|length > 0 %}
/**
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder

    {%- for field in fields -%}
        {%- if fields_mapping[field]['type'] in ['date', 'time', 'datetime'] %}

        ->add('{{ field }}', '{{ fields_mapping[field]['type'] }}')

        {%- else %}

        ->add('{{ field }}')

        {%- endif -%}
    {%- endfor %}

    ;
}
{% endif %}

As I see the if condition in the for loop is the place where the error occurs. (I assume that the expression fields_mapping[field]['type'] causes the problem since the many to many field (tag) has no attribute type.)

What I am doing wrong? How can I solve this problem? Thank you very much for your help.

EDIT: The same problem occurs with Symfony 3.0.0. The file form/FormType.php.twig has been changed since version 2.8.

回答1:

Looks like a regression after datetime fix in the generator bundle.

A quick solution is to revert to v2.* in your composer.json:

"sensio/generator-bundle": "^2.5",

The best solution is to fork the repo, fix the bug and create a pull request to contribute back to the community.

Since you already did all the job to isolate the bug, the fix is trivial: check if type exists in Resources/skeleton/form/FormType.php.twig. Something like

{%- if fields_mapping[field]['type'] is defined and fields_mapping[field]['type'] in ['date', 'time', 'datetime'] %}

unless the bug masks more hidden errors based on the same assumption.



回答2:

I was researching a little bit and tried to debug the error.

As I mentioned above, the file form/FormType.php.twig has been changed since the version 2.8.0.

Obviously the Symfony makers wanted to enhance the forms and automatically resolve the types date, time and datetime. This happens in the line:

{%- if fields_mapping[field]['type'] in ['date', 'time', 'datetime'] %}

This should be achieved with the help of the array fields_mapping.

With some quick-and-dirty workarounds I tried to find out what is hidden inside of fields_mapping. This is the result for my model:

Task

{
    id => {
        id => 1,
        fieldName => id,
        type => integer,
        columnName => id
    },
    description => {
        fieldName => description,
        type => text,
        columnName => description
    }
}

When iterating through the fields of Task, in the last step it goes through the field tags. The expression in the if clause looks like this:

fields_mapping['tags']['type']

As we see in the previous example, there is no key tags in the fields_mapping for Task, only id and description. Since the key tags doesn't exist, the error is thrown.

I changed the concerned line in the file form/FormType.php.twig to look like this:

{%- if fields_mapping[field] is defined and fields_mapping[field]['type'] in ['date', 'time', 'datetime'] %}

Now we can use the new feature and we prevent an error by checking if the key exists in the array.

I don't know if this is a bug or there is something wrong in my particular case. Now it is already one week since the release of the versions 2.8.0 and 3.0.0, so probably many thousands users have been playing around with them. I couldn't believe that, if it is a bug, nobody would have noticed this.

EDIT:

I posted an issue on GitHub:

https://github.com/sensiolabs/SensioGeneratorBundle/issues/443

This was a bug, that has been solved in the same way, as I thought and wrote above:

https://github.com/Maff-/SensioGeneratorBundle/commit/205f64e96a94759f795271cb00fc86fb03b1fd4a



回答3:

Even if after updating fixed bundle the issue is still existing, sometimes the easiest way to solve the problem is to delete the vendor catalog, and update composer.