symfony 软删除主义

gopyfrb3  于 2023-10-24  发布在  其他
关注(0)|答案(1)|浏览(138)

大家好,我有一个关于软删除内部关系的问题。
对于softdelete,我使用Gedmo\SoftDeleteable,这是我的实体(我已经删除了很多东西)

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use JMS\Serializer\Annotation as JMS;

/**
 * Item
 *
 * @ORM\Table(name="item")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\ItemRepository")
 * @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false)
 */
class Item implements EntityInterface
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

   /**
     * One Item has Many Images.
     * @ORM\OneToMany(targetEntity="Image", mappedBy="item", cascade="remove")
     * @MaxDepth(1)
     * @JMS\Groups({"images"})
     */
    private $images;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="deleted_at", type="datetime", nullable=true)
     */
    protected $deletedAt;

    public function getId()
    {
        return $this->id;
    }

    public function __construct()
    {
        $this->images = new \Doctrine\Common\Collections\ArrayCollection();
    }

    public function addImage(\AppBundle\Entity\Image $image)
    {
        $this->images[] = $image;

        return $this;
    }

    public function removeImage(\AppBundle\Entity\Image $image)
    {
        $this->images->removeElement($image);
    }

    public function getImages()
    {
        return $this->images;
    }

    public function getImagePreview()
    {
        foreach($this->getImages() as $image){
            if ($image->getPreview()) {
                return substr($image->getPath(), strpos($image->getPath(), "/images/") + 8);
            }
        }

        return '';
    }

    public function setDeletedAt($deletedAt)
    {
        $this->deletedAt = $deletedAt;

        return $this;
    }

    public function getDeletedAt()
    {
        return $this->deletedAt;
    }
}

我有一些图像被删除,并在我的数据库中,当图像被删除字段deleted_at编译,所以如果我做了一个findAll与学说自动记录不返回
但是,如果我从我的模板调用getImages我检索所有图像也删除图像,但我不想要它。
这是我在循环中从模板调用getImages的方式:

foreach($lot->getImages() as $image) { 
    //code
}

如何使用函数getImages只检索未删除的图像?
谢谢
P.S.我不想在模板中插入if condition,我想在后端解决它

z9zf31ra

z9zf31ra1#

Doctrine对实体有两种操作方法:EntityPersisterDQLQueryBuilder。对于像findAll这样的简单查询,Doctrine将选择BasicEntityPersister来执行查询。
现在,问题是Gedmo不会扫描已执行实体的关联,它只关注当前查询。在第一种EntityPersister模式下,由于缺乏关联感知,它永远不会添加所需的条件。您可以使用第二种模式,使用QueryBuilder和innerJoinImages实体-这将是最快的解决方案。
我为BasicEntityPersister提供了一个复杂的解决方案,它可以工作。以下是步骤:
1.创建一个新的扩展EntityManagerDecorator的multityManager并装饰服务:

entity_manager_proxy:
        public: false
        class: App\Doctrine\EntityManagerProxy
        decorates: doctrine.orm.default_entity_manager
        arguments: [ "@entity_manager_proxy.inner" ]

1.为UOW创建一个装饰器,并在新的EM中调用它:

public function getUnitOfWork()
    {
        return new UnitOfWorkProxy($this);
    }

1.在你的UOW代理中,你现在可以随心所欲地覆盖EntityPersister了。我是这样做的:

public function getEntityPersister($entityName)
    {
        $persister = parent::getEntityPersister($entityName);

        // we are only hooking up the common persister
        if (!$persister instanceof BasicEntityPersister) {
            return $persister;
        }

        $class = $this->em->getClassMetadata($entityName);

        return new SoftDeleteableBasicEntityPersister(
            $this->em,
            $class
        );
    }

1.下面是实际的代码:扫描关联并添加所需的条件。幸运的是,我不必覆盖整个getSelectColumnsSQL方法:

protected function getSelectColumnsSQL()
    {
        // luckily we don't need to modify the SQL created, but a property in the context
        $sql = parent::getSelectColumnsSQL();

        $aliasCounter = 0;

        // we'll be going through all associations and find those with the soft-deleted trait
        foreach ($this->class->associationMappings as $assocField => $mapping) {
            $targetEntityClass = $mapping['targetEntity'];

            if (!$this->isSoftDeleted($targetEntityClass)) {
                continue;
            }

            if (!$mapping['isOwningSide']) {
                continue;
            }

            $targetEntityClassMetadata = $this->em->getClassMetadata($targetEntityClass);
            if ($mapping['mappedBy'] !== null) {
                $association = $targetEntityClassMetadata->getAssociationMapping($mapping['mappedBy']);
            } else {
                $association = $mapping;
            }
            $assocAlias = 's' . ($aliasCounter++);

            $joinTableAlias = $this->getSQLTableAlias($targetEntityClassMetadata->name, $assocAlias);
            $joinTableName  = $this->quoteStrategy->getTableName($targetEntityClassMetadata, $this->platform);

            $tableAlias = $this->getSQLTableAlias($association['targetEntity'], $assocAlias);

            // if we're sure our entity has a relation with a soft-deleted entity,
            // let's add an INNER JOIN to prevent from listing those which were deleted
            $this->currentPersisterContext->selectJoinSql .= ' INNER JOIN';

            foreach ($association['joinColumns'] as $joinColumn) {
                $sourceCol       = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);
                $targetCol       = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform);
                $joinCondition[] = $this->getSQLTableAlias($association['sourceEntity'])
                    . '.' . $sourceCol . ' = ' . $tableAlias . '.' . $targetCol;
            }

            // Add filter SQL
            $filterSql = $this->generateFilterConditionSQL($targetEntityClassMetadata, $tableAlias);
            if ($filterSql) {
                $joinCondition[] = $filterSql;
            }

            // the result of our operation is stored inside the context
            // luckily this will be pulled into the main SQL later
            $this->currentPersisterContext->selectJoinSql .= ' ' . $joinTableName . ' ' . $joinTableAlias . ' ON ';
            $this->currentPersisterContext->selectJoinSql .= implode(' AND ', $joinCondition);
        }

        return $sql;
    }

对于Doctrine 2.4,它可以工作,但可能会改变/打破未来版本的Doctrine,如果他们允许以更一致的方式修改查询。
至于DQL/QueryBuilder操作方法,您需要以类似的方式覆盖EntityRepositoryQueryBuilder,然后基本上您可以根据自己的意愿操作getQuery()方法,使用SqlWalkers等的提示。如果需要,我可以提供解决方案
希望能帮上忙!

相关问题