大家好,我有一个关于软删除内部关系的问题。
对于我正在使用的软删除
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
仅检索未删除的图像?
谢谢
附注我不想在模板中插入
if condition
,我想在后端解决它
Doctrine 有 2 种实体操作方法:
EntityPersister
或 DQL
至 QueryBuilder
。对于像 findAll
这样的简单查询,Doctrine 将选择 BasicEntityPersister
来执行查询。
现在的问题是 Gedmo 不扫描已执行实体的关联,它只关注当前查询。在第一个
EntityPersister
模式中,由于缺乏关联意识,它永远不会添加所需的条件。您可以使用第二种模式来欺骗它,使用 QueryBuilder 和 innerJoin
以及 Images
实体 - 这对您来说将是最快的解决方案。
我为
BasicEntityPersister
提供了一个复杂的解决方案,并且它有效。步骤如下:
EntityManagerDecorator
并装饰服务: entity_manager_proxy:
public: false
class: App\Doctrine\EntityManagerProxy
decorates: doctrine.orm.default_entity_manager
arguments: [ "@entity_manager_proxy.inner" ]
public function getUnitOfWork()
{
return new UnitOfWorkProxy($this);
}
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
);
}
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操作方法,您需要以类似的方式重写
EntityRepository
和QueryBuilder
,然后基本上您可以根据需要围绕getQuery()
方法进行操作,使用SqlWalkers等的提示。如果需要可以提供解决方案
希望有帮助!