Forms Symfony2中的表单后处理
我是Symfony的新手,我正在尝试创建一个绑定到实体用户的表单 此实体的一个字段的类型为ArrayCollection。它实际上是与另一个类的对象的单域关系。 所以,为了更清楚,我们需要一点代码Forms Symfony2中的表单后处理,forms,symfony,doctrine-orm,formbuilder,Forms,Symfony,Doctrine Orm,Formbuilder,我是Symfony的新手,我正在尝试创建一个绑定到实体用户的表单 此实体的一个字段的类型为ArrayCollection。它实际上是与另一个类的对象的单域关系。 所以,为了更清楚,我们需要一点代码 class User { \\... /** * @ORM\OneToMany(targetEntity="UserGoods", mappedBy="users") * @ORM\JoinColumn(name="goods", referencedColumnN
class User
{
\\...
/**
* @ORM\OneToMany(targetEntity="UserGoods", mappedBy="users")
* @ORM\JoinColumn(name="goods", referencedColumnName="id")
*/
private $goods;
public function __construct()
{
$this->goods = new ArrayCollection();
}
\\...
}
以及相关的类
class UserGoods
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var \DateTime
*
* @ORM\Column(name="inserted_at", type="datetime")
*/
private $insertedAt;
/**
* @var float
*
* @ORM\Column(name="value", type="float")
*/
private $value;
/**
* @ORM\ManyToOne(targetEntity="User", inversedBy="goods")
*/
protected $users;
}
现在,我想创建一个FormBuilder,它可以做一些非常简单的事情,但我自己却不知道如何去做。
我只需要一个number类型的字段,如果存在具有当前日期的Goods类型的对象,请修改它,否则将向集合添加一个新对象
这可以在控制器内轻松完成,但我有很多这种表单的实例,这将使我的程序无法维护
是否有办法在表单生成器中添加一些提交数据的后处理?
我已经尝试过使用DataTransformers,但这些还不够,因为它们最多只能将一个数字转换为一个UserGoods对象,而原始的ArrayCollection将不会被保留(关于条令关联呢?)。
此外,如果我将字段类型声明为数字类型的集合,则在呈现表单时将显示ArrayCollection中的所有项,而不仅仅是最后一项
你知道怎么摆脱这一切吗?
提前感谢您的帮助。按照建议,使用表单事件。在事件中,您将检查具有提交日期的商品是否已经存在(从数据库加载),并使用post数据对其进行修改。如果它们不存在,你将创造新的。您还可以在实体中创建另一个方法getLastItemsInCollection(),在该方法中,您可以使用条件仅从数据库中加载最后一个(推荐),或从原始ArrayCollection中获取最后一个项。如上所述,您可以使字段未映射,并在FormEvent中手动映射商品。我希望这会有所帮助,我希望我理解正确。我遵循了Cerad和tomazahlin的建议,并提出了解决方案 我确信每年全世界至少有两个人都会遇到我同样的问题,所以我会花一些时间公布我的结果。
请随意纠正、批评或添加我,最终我是Symfony的新手 首先,我最终是如何定义我的两个类的
class User
{
//...
/**
* @ORM\ManyToMany(targetEntity="UserGoods", inversedBy="users", cascade={"persist", "remove"})
* @ORM\JoinColumn(name="goods", referencedColumnName="id")
*/
// Should have been a OneToMany relationship, but Doctrine requires the
// owner side to be on the Many side, and I need it on the One side.
// A ManyToMany relationship compensate this.
private $goods;
public function __construct()
{
$this->goods = new ArrayCollection();
}
//...
}
以及连接类
/**
* @ORM\HasLifecycleCallbacks()
**/
class UserGoods
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var \DateTime
*
* @ORM\Column(name="inserted_at", type="datetime")
*/
private $insertedAt;
/**
* @var float
*
* @ORM\Column(name="value", type="float", nullable=true)
*/
// I do not want this field to be null, but in this way when
// persisting I can look for null elements and remove them
private $value;
/**
* @ORM\ManyToMany(targetEntity="User", inversedBy="goods")
*/
protected $users;
/**
* @ORM\PrePersist()
* @ORM\PreUpdate()
*/
// This automatically sets InsertedAt value when inserting or
// updating an element.
public function setInsertedAtValue()
{
$date = new \DateTime();
$this->setInsertedAt( $date );
}
}
正如我所说,我需要一个FormBuilder来处理我的数组集合。用于此目的的最佳表单类型是。。。集合类型。
这需要将子窗体定义为其类型
<?php
namespace MyBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use MyBundle\Entity\UserGoods;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('goods', 'collection', array(
'type' => new GoodsdataWithDateType(),
'required' => false,
)
);
\\ ...
这可以很好地工作,但是当在细枝文件中使用类似{form(form)}的东西进行渲染时,显示非常糟糕。
为了使它更方便用户,我定制了表单的显示方式,以便删除一些垃圾并只包含必要的标签。
所以在我的小树枝上:
{{ form_start(form) }}
{{ form_errors(form) }}
<div>
{{ form_label(form.goods) }}
{{ form_errors(form.goods) }}
<br>
{% for field in form.goods %}
{{ form_widget(field) }}
{% endfor %}
</div>
{{ form_end(form) }}
并在我的config.yml中将其注册为
mybundle.emptyvalues_listener:
class: MyBundle\EventListener\EmptyValueListener
tags:
- { name: doctrine.event_listener, event: onFlush }
你看过表单事件吗?是的,我看了收据。他们看起来很有希望,但我不知道这些与我的情况有什么关系。欢迎提示!我不理解你的用例。也许其他人可以帮忙。
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$thisuser = $builder->getData();
// I added the following function inside the User class.
// I use a for loop to scroll all the associated Goods to get the
// latest one.
$mygoods = $thisuser->getLatestGoods();
if ( $mygoods && null !== $mygoods->getId() ) {
// The Array contains already some elements
$datetime1 = $mygoods->getInsertedAt();
$datetime2 = new \DateTime();
$datetime2->setTime(0, 0, 0);
// Check when was the last one inserted
if ($datetime1 < $datetime2) // Nice way to compare dates
{
// If it is older than today, add a new element to the array
$newgoods = new UserGoods();
$thisuser->addGoods($newgoods);
}
} else {
// The array is empty and I need to create the firs element
$newgoods = new UserGoods();
$thisuser->addGoods($newgoods);
}
$builder->add('goods', 'collection', array(
'type' => new GoodsdataWithDateType(),
'required' => false,
'allow_add' => true, // this enables the array to be
// populated with new elements
)
);
namespace MyBundle\EventListener;
use Doctrine\ORM\Event\OnFlushEventArgs;
use MyBundle\Entity\UserGoods;
class EmptyValueListener
{
public function onFlush(OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
$entities = array_merge(
$uow->getScheduledEntityInsertions(),
$uow->getScheduledEntityUpdates()
);
foreach ($entities as $entity) {
if ($entity instanceof UserGoods) {
if ($entity && null !== $entity )
{
if ( empty($entity->getValue()) )
{
$users = $entity->getUsers();
foreach ($users as $curruser)
{
$curruser->removeGoods($entity);
$em->remove($entity);
$md = $em->getClassMetadata('MyBundle\Entity\UserGoods');
$uow->computeChangeSet($md, $entity);
$em->persist($curruser);
$md = $em->getClassMetadata('MyBundle\Entity\User');
$uow->computeChangeSet($md, $curruser);
}
}
}
}
}
}
}
mybundle.emptyvalues_listener:
class: MyBundle\EventListener\EmptyValueListener
tags:
- { name: doctrine.event_listener, event: onFlush }