在学说中加载相关实体的子集

时间:2014-09-19 23:21:02

标签: php symfony doctrine-orm

我不确定我想做的事情是否可行,但我无法找到任何文件。希望我只是在错误的地方寻找。

我想要做的是加载一个始终加载相关对象子集的对象。一些背景 - 我想保留用户删除的联系人的历史记录,因此当删除联系人时,我将db中的isDeleted列设置为true。我很少想加载已删除的联系人,所以我稍后会担心。但是,当我加载用户,然后加载联系人(在我的情况下通过序列化整个user对象),所有联系人都被加载,包括已删除的联系人。所以我想只加载尚未删除的联系人。这就是我现在所拥有的,而且我不知道为什么它不起作用。

use Doctrine\Common\Collections\Criteria,
    Doctrine\Common\Collections\ArrayCollection;

class User{
/**
 * @ORM\OneToMany(targetEntity="Contacts", mappedBy="user", cascade={"persist"}, fetch="EXTRA_LAZY")
 * @ORM\JoinColumn(name="contact_id", referencedColumnName="contact_id")
 */
private $contacts;

public function __construct(){
    $this->contacts = new ArrayCollection();
}

/**
 * Get contacts
 *
 * @return \Doctrine\Common\Collections\ArrayCollection
 */
public function getContacts()
{
    // This is where I am filtering the contacts
    $criteria = Criteria::create()
    ->where(Criteria::expr()->eq("isDeleted", false));
    return $this->contacts->matching($criteria);
}

然后我在控制器中使用JMS Serializer和FOSRest加载和序列化用户。

public function getUserAction($slug){
    $data = $this->getDoctrine()->getRepository('MyCoreBundle:User')->find($slug);
    $view = $this->view($data, 200);

    return $this->handleView($view);
}

结果是以下JSON:

{
userId: 7,
creationDate: "2014-07-28T22:05:43+0000",
isActive: true,
username: "myUser",
contacts: [
    {
        contactId: 23,
        firstName: "Jim",
        lastName: "Bin",
        email: "zzz@zzz.com",
        phone: "1231231231",
        isDeleted: true
    }
]
}

我不确定为什么还在加载已删除的用户。如果我在我的控制器而不是Entity类中进行过滤,则过滤工作。但是我不希望在我的应用程序中随处可以加载 。如果我能在Entity类中获得某些功能,那将是理想的选择。有没有人做过类似的工作呢?

我正在使用Symfony 2.3

2 个答案:

答案 0 :(得分:2)

我认为您的问题是当您从其存储库中获取用户时,getContacts实际上没有被调用,这是使用标准的Doctrine查询完成的,并且因为用户和联系人是相关的无论联系人是否被删除,他们都会遇到。一种解决方案是使用findUserWithActiveContacts()方法创建自己的UserRepository,该方法执行更合适的查询,并使用该方法。

例如:

UserRepository.php

namespace Demo\MyBundle\Entity;

use Doctrine\ORM\EntityRepository;

class UserRepository extends EntityRepository
{
    public function findWithActiveContacts($slug)
    {
        $result= $this->createQueryBuilder('u')
            ->join('u.contacts', 'c')
            ->select('u,c')
            ->where('c.isDeleted = false')
            ->andWhere('u.slug = :slug')
            ->setParameter('slug', $slug)
            ->getQuery()
            ->getResult();

        return $result;
    }
}

将实体连接到新存储库:

user.php的

class User{
/**
 * @ORM\OneToMany(targetEntity="Contacts", mappedBy="user", cascade={"persist"}, fetch="EXTRA_LAZY")
 * @ORM\JoinColumn(name="contact_id", referencedColumnName="contact_id")
 * @ORM\Entity(repositoryClass="Demo\MyBundle\Entity\UserRepository")
 */

然后在控制器中:

public function getUserAction($slug){
    $data = $this->getDoctrine()->getRepository('MyCoreBundle:User')->findWithActiveContacts($slug);
    $view = $this->view($data, 200);

    return $this->handleView($view);
}

因此,只要您使用findWithActiveContacts代替find,就应该始终只获取未删除的联系人,而无需更改任何其他内容。

我认为如果你愿意的话,你可能会在你的客户存储库中覆盖find,但对你而言,这只会改变你所调用的方法的名称,而且我会担心其他代码(例如,为表单提取实体的东西)会开始使用它,这可能是你想要的,但也可能产生不可预测的后果。

以这种方式调整所有查询的官方Doctrine方法是使用Filters,它实际上在一个额外的where条款中滑入了通过Doctrine的所有内容。这就是SoftDeleteable扩展的功能。

答案 1 :(得分:1)

我强烈推荐使用Soft-Deleteable扩展名。您必须使用deletedAt字段,该字段在未删除对象时为null,如果已删除,则为timestamp

它是这样的:

/**
 * @ORM\Entity
 * @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false)
 */
class User
{
    /**
     * @ORM\Column(name="deletedAt", type="datetime", nullable=true)
     */
    private $deletedAt;

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

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

}

在此处查看此扩展程序:Soft-Deleteable Extension