我在我的Symfony 2项目中使用DoctrineExtensions,我有一个简单的Entity类,我在属性上使用Sluggable,然后我想根据slug将值设置为另一个属性,但是,即使使用Lifecycle Callbacks
@ORM\PrePersist
,@ORM\PreFlush
,此时slug属性仍然是空的,这意味着还没有生成slug,这是我的班级,为了保持这个简短,我我不打算在这里放置每个属性的get and set
函数,只是该类对于这个例子很重要的部分(请阅读评论)
<?php
namespace My\LearnBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* Banner
*
* @ORM\Table(name="banner")
* @ORM\HasLifecycleCallbacks()
*/
class Banner {
/**
* @var integer
*
* @ORM\Column(name="id", type="bigint", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=128, nullable=false)
* @Assert\NotBlank()
*/
private $name;
/**
* @var string
*
* @ORM\Column(name="slug", type="string", length=256, nullable=false)
* @Gedmo\Slug(fields={"name"})
*/
private $slug;
/**
* @var string
*
* @ORM\Column(name="tracking_url", type="string", length=256, nullable=false)
*/
private $trackingUrl;
/**
* Set slug
*
* @param string $slug
* @return Banner
*/
public function setSlug($slug) {
$this->slug = $slug;
$this->trackingUrl = $slug."/tracking"; //Doesn't work
return $this;
}
/**
* Set trackingUrl value
*
* @ORM\PreUpdate
*/
public function setTrackingUrlValue() {
//the slug is empty. Doesn't work
$this->trackingUrl = $this->slug."/tracking";
return $this;
}
/**
* Set trackingUrl value
*
* @ORM\PreFlush
*/
public function setTrackingUrlValueOnFlush() {
//the slug is empty. Doesn't work
return $this->setTrackingUrlValue();
}
}
我尝试过什么?好吧,使用setSlug
函数,但它不起作用(注意上面的例子注释),似乎没有被调用。使用Lifecycle Callbacks
@ORM\PrePersist
,@ORM\PreFlush
和@ORM\PreUpdate
也不起作用。
现在我在控制器中解决了这个问题,在flush
上调用EntityManager
后,根据slug设置属性值并再次调用flush
,因此,在一个请求中进行2个数据库查询,一个用于插入,一个用于更新。我不想使用Event Listener
,因为此行为仅适用于此特定实体,或者存在将事件侦听器附加到单个实体的方法?
但是现在,我想知道:
为什么我试图用Lifecycle Callbacks
做的事情没有用呢?
为什么使用setSlug
功能不起作用?
一种更清洁的方式来实现我想要的东西?
感谢
答案 0 :(得分:0)
可能发生的事情是,带注释的侦听器的优先级高于创建子弹的优先级(或者它们具有相同的优先级,在这种情况下,带注释的侦听器可能在之前添加)。
我担心你必须抛弃注释,创建一个实际的监听器并将其标记为事件注册编译器传递以获取它。这个令人讨厌的是捆绑似乎使用onFlush
来创建slug(code)。
namespace Acme\DemoBundle\Listener;
use Acme\DemoBundle\Model\TrackingUrlUpdateable;
use Doctrine\ORM\Event\OnFlushEventArgs;
class TrackingUrlUpdater
{
public function onFlush(OnFlushEventArgs $eventArgs)
{
$em = $eventArgs->getEntityManager();
$uof = $em->getUnitOfWork();
// Let's process both types of entities in a single loop.
$entities = array_merge(
$uof->getScheduledEntityInsertions(),
$uof->getScheduledEntityUpdates()
);
foreach ($entities as $entity) {
// Using a fictional interface (e.g. for making testing easier).
if (!($entity instanceof TrackingUrlUpdateable)) {
continue;
}
// `Banner::updateTrackingUrl()` would internally change the
// tracking url to the correct one.
$entity->updateTrackingUrl();
// The change-set must be recomputed as its fields were modified
// in the previous step.
$uof->recomputeSingleEntityChangeSet(
$em->getClassMetadata(get_class($entity)),
$entity
);
}
}
}
现在剩下的就是以低于优先级的方式注册侦听器。
Sluggable
哦,别忘了测试!
答案 1 :(得分:0)
假设您正在使用StofDoctrineExtensionsBundle
来集成库,另一种方法是增加sluggable
侦听器的优先级,以便在sluggable
侦听器之后调用带注释的回调函数
compiler pass可能会成功。
namespace Acme\DemoBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class SluggableListenerPriorityChangingPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$id = 'stof_doctrine_extensions.listener.sluggable';
$tag = 'doctrine.event_subscriber';
$definition = $container->getDefintion($id);
$attributes = $definition->getTag($tag);
if (!$attributes) {
throw new \LogicException("The listener (`$id`) must have a `$tag` tag.");
}
$attributes['priority'] = 10;
$definition
->clearTag($tag);
->addTag($tag, $attributes)
;
}
}
sluggable
侦听器service itself已注册here,如果已从配置中启用。