使用 Symfony 2.8 生成表单会抛出 Twig_Error_Runtime
Generating forms with Symfony 2.8 throws a Twig_Error_Runtime
自从几天前(2015 年 11 月 30 日)发布了 Symfony 的最后一个 LTS 版本后,我就开始使用它了。不幸的是,我无法使用在 Symfony 2.7.7 中运行良好的相同代码生成具有写入操作的 CRUD。
首先,我使用 Linux Mint 17.2 下的 bash
创建一个新的 Symfony 项目:
symfony new tasks lts
创建新目录 tasks
,其中包含一个新的 Symfony 2.8.0 项目。
在 app/config/parameters.yml
中调整数据库凭据后,我创建了数据库:
app/console doctrine:database:create
并生成一个新包:
app/console generate:bundle --namespace=Acme/TasksBundle --format=yml
然后我创建一个新目录 src/Acme/TasksBundle/Resources/config/doctrine
并在其中放置两个我的模型文件。它们是:
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
数据库模式应该是这样的:
+----------------+ +--------------+
| task | | task_tag | +---------+
+----------------+ +--------------+ | tag |
| id |<--->| task_id | +---------+
| description | | tag_id |<--->| id |
+----------------+ +--------------+ | name |
+---------+
现在我可以生成实体了:
app/console generate:doctrine:entities AcmeTasksBundle
这工作正常,因此可以更新数据库:
app/console doctrine:schema:update --force
到目前为止一切正常。这些表在数据库中。现在我想用写操作生成 CRUD:
app/console generate:doctrine:crud --entity=AcmeTasksBundle:Task --with-write --format=yml
确认几个问题后,它生成 CRUD 并打印出来:
Generating the CRUD code: OK
然后抛出这个错误:
[Twig_Error_Runtime]
Key "tags" for array with keys "id, description" does not exist in "form/FormType.php.twig" at line 29
创建了控制器,但没有创建表单。
生成不带写入选项的 CRUD 工作正常。完全相同的代码可以完美地与 Symfony 2.7.7 一起使用。
我检查了文件 form/FormType.php.twig
中版本之间的差异,以下是相关部分:
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 %}
据我所知,for循环中的if条件就是错误发生的地方。 (我假设表达式 fields_mapping[field]['type']
导致问题,因为多对多字段 (tag
) 没有属性 type
。)
我做错了什么?我怎么解决这个问题?非常感谢您的帮助。
编辑:
Symfony 3.0.0 也会出现同样的问题。文件 form/FormType.php.twig
自版本 2.8 以来已更改。
看起来像是生成器包中 datetime fix 之后的回归。
一个快速的解决方案是在您的 composer.json
:
中恢复到 v2.*
"sensio/generator-bundle": "^2.5",
最好的解决方案是分叉存储库、修复错误并创建拉取请求以回馈社区。
由于您已经完成了隔离错误的所有工作,因此修复很简单:检查 type
是否存在于 Resources/skeleton/form/FormType.php.twig
中。像
{%- if fields_mapping[field]['type'] is defined and fields_mapping[field]['type'] in ['date', 'time', 'datetime'] %}
除非该错误基于相同的假设掩盖了更多隐藏的错误。
我稍微研究了一下并尝试调试错误。
正如我上面提到的,文件 form/FormType.php.twig
自版本 2.8.0 以来已更改。
显然,Symfony 的开发者想要增强表单并自动解析类型 date
、time
和 datetime
。这发生在行中:
{%- if fields_mapping[field]['type'] in ['date', 'time', 'datetime'] %}
这应该借助数组fields_mapping
.
来实现
通过一些快速而肮脏的解决方法,我试图找出 fields_mapping
中隐藏的内容。这是我的模型的结果:
任务
{
id => {
id => 1,
fieldName => id,
type => integer,
columnName => id
},
description => {
fieldName => description,
type => text,
columnName => description
}
}
当遍历 Task 的字段时,在最后一步它会遍历字段 tags
。 if 子句中的表达式如下所示:
fields_mapping['tags']['type']
正如我们在前面的例子中看到的,fields_mapping
中没有键 tags
for Task,只有 id
和 description
。由于密钥 tags
不存在,因此抛出错误。
我将文件 form/FormType.php.twig
中的相关行更改为如下所示:
{%- if fields_mapping[field] is defined and fields_mapping[field]['type'] in ['date', 'time', 'datetime'] %}
现在我们可以使用新功能,我们通过检查数组中是否存在键来防止错误。
我不知道这是一个错误还是我的特定情况有问题。现在距离 2.8.0 和 3.0.0 版本发布已经一周了,所以可能有成千上万的用户在使用它们。我简直不敢相信,如果这是一个错误,没有人会注意到这一点。
编辑:
我在 GitHub 上发布了一个问题:
https://github.com/sensiolabs/SensioGeneratorBundle/issues/443
这是一个错误,已经以同样的方式解决了,正如我在上面所想和写的:
https://github.com/Maff-/SensioGeneratorBundle/commit/205f64e96a94759f795271cb00fc86fb03b1fd4a
即使更新固定包后问题仍然存在,有时解决问题的最简单方法是删除 vendor
目录,然后更新 composer。
自从几天前(2015 年 11 月 30 日)发布了 Symfony 的最后一个 LTS 版本后,我就开始使用它了。不幸的是,我无法使用在 Symfony 2.7.7 中运行良好的相同代码生成具有写入操作的 CRUD。
首先,我使用 Linux Mint 17.2 下的 bash
创建一个新的 Symfony 项目:
symfony new tasks lts
创建新目录 tasks
,其中包含一个新的 Symfony 2.8.0 项目。
在 app/config/parameters.yml
中调整数据库凭据后,我创建了数据库:
app/console doctrine:database:create
并生成一个新包:
app/console generate:bundle --namespace=Acme/TasksBundle --format=yml
然后我创建一个新目录 src/Acme/TasksBundle/Resources/config/doctrine
并在其中放置两个我的模型文件。它们是:
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
数据库模式应该是这样的:
+----------------+ +--------------+
| task | | task_tag | +---------+
+----------------+ +--------------+ | tag |
| id |<--->| task_id | +---------+
| description | | tag_id |<--->| id |
+----------------+ +--------------+ | name |
+---------+
现在我可以生成实体了:
app/console generate:doctrine:entities AcmeTasksBundle
这工作正常,因此可以更新数据库:
app/console doctrine:schema:update --force
到目前为止一切正常。这些表在数据库中。现在我想用写操作生成 CRUD:
app/console generate:doctrine:crud --entity=AcmeTasksBundle:Task --with-write --format=yml
确认几个问题后,它生成 CRUD 并打印出来:
Generating the CRUD code: OK
然后抛出这个错误:
[Twig_Error_Runtime]
Key "tags" for array with keys "id, description" does not exist in "form/FormType.php.twig" at line 29
创建了控制器,但没有创建表单。
生成不带写入选项的 CRUD 工作正常。完全相同的代码可以完美地与 Symfony 2.7.7 一起使用。
我检查了文件 form/FormType.php.twig
中版本之间的差异,以下是相关部分:
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 %}
据我所知,for循环中的if条件就是错误发生的地方。 (我假设表达式 fields_mapping[field]['type']
导致问题,因为多对多字段 (tag
) 没有属性 type
。)
我做错了什么?我怎么解决这个问题?非常感谢您的帮助。
编辑:
Symfony 3.0.0 也会出现同样的问题。文件 form/FormType.php.twig
自版本 2.8 以来已更改。
看起来像是生成器包中 datetime fix 之后的回归。
一个快速的解决方案是在您的 composer.json
:
"sensio/generator-bundle": "^2.5",
最好的解决方案是分叉存储库、修复错误并创建拉取请求以回馈社区。
由于您已经完成了隔离错误的所有工作,因此修复很简单:检查 type
是否存在于 Resources/skeleton/form/FormType.php.twig
中。像
{%- if fields_mapping[field]['type'] is defined and fields_mapping[field]['type'] in ['date', 'time', 'datetime'] %}
除非该错误基于相同的假设掩盖了更多隐藏的错误。
我稍微研究了一下并尝试调试错误。
正如我上面提到的,文件 form/FormType.php.twig
自版本 2.8.0 以来已更改。
显然,Symfony 的开发者想要增强表单并自动解析类型 date
、time
和 datetime
。这发生在行中:
{%- if fields_mapping[field]['type'] in ['date', 'time', 'datetime'] %}
这应该借助数组fields_mapping
.
通过一些快速而肮脏的解决方法,我试图找出 fields_mapping
中隐藏的内容。这是我的模型的结果:
任务
{
id => {
id => 1,
fieldName => id,
type => integer,
columnName => id
},
description => {
fieldName => description,
type => text,
columnName => description
}
}
当遍历 Task 的字段时,在最后一步它会遍历字段 tags
。 if 子句中的表达式如下所示:
fields_mapping['tags']['type']
正如我们在前面的例子中看到的,fields_mapping
中没有键 tags
for Task,只有 id
和 description
。由于密钥 tags
不存在,因此抛出错误。
我将文件 form/FormType.php.twig
中的相关行更改为如下所示:
{%- if fields_mapping[field] is defined and fields_mapping[field]['type'] in ['date', 'time', 'datetime'] %}
现在我们可以使用新功能,我们通过检查数组中是否存在键来防止错误。
我不知道这是一个错误还是我的特定情况有问题。现在距离 2.8.0 和 3.0.0 版本发布已经一周了,所以可能有成千上万的用户在使用它们。我简直不敢相信,如果这是一个错误,没有人会注意到这一点。
编辑:
我在 GitHub 上发布了一个问题:
https://github.com/sensiolabs/SensioGeneratorBundle/issues/443
这是一个错误,已经以同样的方式解决了,正如我在上面所想和写的:
https://github.com/Maff-/SensioGeneratorBundle/commit/205f64e96a94759f795271cb00fc86fb03b1fd4a
即使更新固定包后问题仍然存在,有时解决问题的最简单方法是删除 vendor
目录,然后更新 composer。