使用DoctrineExtensions更新slug上的另一个字段

时间:2013-11-18 19:09:33

标签: symfony orm doctrine-orm

我在我的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功能不起作用? 一种更清洁的方式来实现我想要的东西?

感谢

2 个答案:

答案 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,如果已从配置中启用。