什么是以及如何在Symfony2中创建数据转换器

与它的前身(Symfony 1.4)不同的是, 使用Doctrine在数据库中插入实体时, 如果你的实体具有一对多关系, 则可以使用几行代码轻松创建它:
给定角色和用户之间的一对多关系:

$user  =  new  User();   $user-> setName('Joe');   $user-> setAge(28);   $user-> setRoleId(1);   $user-> save();

但是从symfony 2开始, 如果你尝试这样做:
$em  =  $this-> getDoctrine()-> getManager();   $user  =  new  User();   $user-> setName('Joe');   $user-> setAge(28);   $user-> setRole(1);   $em-> persist($user);   $em-> flush();

因为你的setRole方法试图仅保存一个数字而不是一个Role Entity, 所以将引发错误, 你需要在用户实体中插入一个原始Role对象
$em  =  $this-> getDoctrine()-> getManager();   $roleEntity  =  $em-> getRepository('ourcodeworldBundle:Roles')-> find(1);   $user  =  new  User();   $user-> setName('Joe');   $user-> setAge(28);   $user-> setRole($roleEntity);   $em-> persist($user);   $em-> flush();

如此容易, 对象将成功持久。
使用数据转换器
现在, 使用数据转换器将实体转换为表单小部件, 考虑具有一对多关系的实体产品, 如果创建CRUD, 则会创建一个选择小部件以选择表单中产品的角色。
但是, 如果选择的类别超过200个, 该怎么办?不舒服的选择方式不是吗?建议你设计一种简单的方法来选择带有用户知道的简单数字的类别。
这是当数据转换器执行操作时, 它们会将数字转换为实体, 然后将该实体自动转换为数字。
如果不使用数据转换器, 则从具有一对多关系的现有数据库创建CRUD时, 很可能会看到以下错误:
Catchable Fatal Error: Argument 1 passed to xxxxxxxxxxxxx::setRole() mustbe an instance of xxxxxxxxxxxxxxx\Role, string given, called in xxxxxxxxx on line xxxx and defined in xxxxxx line The form's view data is expected to be an instance of class xxxxxxxxxxxxxbut is a(n) string. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms a(n) string to an instance of xxxxxxxxxxx

1)在名为DataTransformer的新文件夹中将转换器创建到捆绑软件的Form文件夹中
在实体的form文件夹中创建一个php文件, 并插入以下代码
// src/AppBundle/Form/DataTransformer/IssueToNumberTransformer.phpnamespace AppBundle\Form\DataTransformer; use AppBundle\Entity\Issue; use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\Exception\TransformationFailedException; class IssueToNumberTransformer implements DataTransformerInterface{private $manager; public function __construct(ObjectManager $manager){$this-> manager = $manager; }/*** Transforms an object (issue) to a string (number).** @paramIssue|null $issue* @return string*/public function transform($issue){if (null === $issue) {return ''; }return $issue-> getId(); }/*** Transforms a string (number) to an object (issue).** @paramstring $issueNumber* @return Issue|null* @throws TransformationFailedException if object (issue) is not found.*/public function reverseTransform($issueNumber){// no issue number? It's optional, so that's okif (!$issueNumber) {return; }$issue = $this-> manager-> getRepository('AppBundle:Issue')// query for the issue with this id-> find($issueNumber); if (null === $issue) {// causes a validation error// this message is not shown to the user// see the invalid_message optionthrow new TransformationFailedException(sprintf('An issue with number "%s" does not exist!', $issueNumber)); }return $issue; }}

更改需要替换的显而易见的参数, 例如捆绑软件, 命名空间和每次执行转换器时都需要替换的属性。
2)将datatransformer添加到我们实体的” 表单” 构建器
【什么是以及如何在Symfony2中创建数据转换器】我们需要在表单构建器中应用先前的datatransformer(位于我们创建的文件夹之外)
// src/AppBundle/Form/TaskType.phpnamespace AppBundle\Form\Type; use AppBundle\Form\DataTransformer\IssueToNumberTransformer; // We include the datatransformer created previouslyuse Doctrine\Common\Persistence\EntityManager; // ...class TaskType extends AbstractType{private $entityManager; public function __construct(EntityManager $entityManager) // Create the constructor if not exist and add the entity manager as first parameter (we will add it later){$this-> entityManager = $entityManager; }public function buildForm(FormBuilderInterface $builder, array $options){$builder-> add('description', 'textarea')-> add('issue', 'text', array(// validation message if the data transformer fails'invalid_message' => 'That is not a valid issue number', )); // ...$builder-> get('issue')-> addModelTransformer(new IssueToNumberTransformer($this-> entityManager)); // finally we apply the transformer}// ...}

3)我们将控制器中所需的EntityManager $ entityManager发送到taskType
必须在datatransformer中提供实体管理器(理论管理器), 该实体管理器将作为我们在控制器中的formType声明(newAction, editAction)中的第一个参数传递
// e.g. in a controller somewhere$manager = $this-> getDoctrine()-> getManager(); $form = $this-> createForm(new TaskType($manager), $task);

这样, 我们具有200多个选项的长期选择将被文本输入替换, 在文本输入中, 用户将仅键入角色的ID(或你的自定义标识符), 而不是加载数据库的200行。

    推荐阅读