学说2,查询实体内部

时间:2011-08-15 11:45:24

标签: php filter doctrine-orm doctrine

如何在实体中执行查询?

namespace Entities\Members;

/**
 * @Entity(repositoryClass="\Entities\Member\MembersRepository")
 * @Table(name="Members")
 * @HasLifecycleCallbacks
 */
class Members extends \Entities\AbstractEntity
{
    /**
     * @Id @Column(name="id", type="bigint",length=15)
     * @GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /** 
     * @Column(name="userid", type="bigint", length=26, nullable=true) 
     */
    protected $userid;

    /** 
     * @Column(name="fname", type="string", length=255,nullable=true) 
     */
    protected $fname;

    /**
     *  @OneToMany(targetEntity="\Entities\Users\Wall", mappedBy="entry", cascade={"persist"}) 
     */
    protected $commententries;

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

示例我希望在此实体中有一个名为filter()的函数 我希望能够过滤commententries集合。它应该返回一个具有特定条件id=1的集合。基本上它应该过滤从连接查询收到的数据。

这样的事情:

$this->commententries->findBy(array('id' => 1));

但显然这不起作用。

5 个答案:

答案 0 :(得分:21)

你的ArrayCollection已经实现了一个filter()方法,你需要传递一个Closure来使它工作你的实体(这里是commentEntries)。

$idsToFilter = array(1,2,3,4);

$member->getComments()->filter(
    function($entry) use ($idsToFilter) {
       if (in_array($entry->getId(), $idsToFilter)) {
           return true;
       }

       return false;
    }
); 

(未经测试)

请注意,此类方法将对您的所有评论进行迭代和急切加载,因此,如果用户有很多,则可能是一个很大的瓶颈;

在大多数情况下,您希望使用自定义存储库,您可以在其中放置此类逻辑。

正如timdev建议的那样,您可以创建一个MemberService,它将通过了解EntityManager来包装此类调用。

将实体与Peristance Layer分开是对Doctrine 1的重大改进,你不应该违反这条规则。

答案 1 :(得分:12)

一般来说,你不应该这样做。

根据经验,实体不应该直接了解实体管理者(或通过某些中间对象)。

这样做的原因主要是可测试性,但根据我的经验,它有助于以其他方式保持组织。

我会通过设计一个处理查找的服务类来实现它。你的控制器(或其他)会像这样驱动它:

<?php
// create a new service, injecting the entitymanager.  if you later wanted 
// to start caching some things, you might inject a cache driver as well.
$member = $em->find('Member',$member_id); //get a member, some how.
$svc = new MemberService($em);

$favoriteCommentaries = $svc->getFavoriteCommentaries($member);

正如我在评论中提示的那样,如果您稍后决定要添加缓存(例如,通过memcached)以避免频繁查找,那么您可以在此服务类附近或此服务中执行此操作。这使您的实体保持简洁,易于测试。由于您在构造时将实体管理器注入服务,因此可以根据需要进行模拟。

getFavoriteCommentaries()可以使用各种实现。一个微不足道的是将它代理到Member :: getFavoriteCommentaries(),它实际上会加载所有内容,然后过滤掉“最喜欢的”。这可能不会特别好地扩展,因此您可以通过使用EM来获取所需的数据来改进它。

答案 2 :(得分:5)

使用自定义存储库进行查询

您不应该在您的实体中编写查询,但是您应该使用存储库。这也在学说文档 7.8.8 Custom Repositories 中进行了解释。它允许您在中心位置构建自定义查询,并保持实体定义清洁。

使用条件过滤集合:

但是,如果您想使用get方法在集合内部进行过滤,则可以使用Criteria。您可以在Doctrine文档 8.8 Filtering collections 中了解如何使用Criteria。像你想要的那样过滤会看起来像这样:

在实体类的顶部声明Criteria

use Doctrine\Common\Collections\Criteria

在您的getCommentEntries方法中,使用该类进行过滤:

public function getCommentEntries()
{
    $criteria = Criteria::create()
        ->where(Criteria::expr()->eq('id', 1));

    $filteredCommentEntries = $this->commententries->matching($criteria);

    return $filteredCommentEntries;
}

答案 3 :(得分:1)

您的问题很难理解,请尝试着解您将来如何构建问题。例如,你说“返回相同的结果”,但“过滤”,这可能意味着什么。你想使用相同的结果集(为什么你会选择这样做),只是使用array_filter或array_walk来过滤结果,或者你真的想要使用条件连接吗?这是非常模糊的。

无论如何......回答(4次阅读你的问题后)。

$q = $qb->select ( "m","w" )
            ->from ( "Members", "m" )
            ->leftJoin( "m.commententries","w",'WITH', 'w.id = :id')
            ->setParameter ( "id", $id )
            ->getQuery ();

答案 4 :(得分:1)

我同意“timdev”。您不应该在实体类中定义查询。我定义服务类的方式支持实体是存储库类。例如:User(entity - YourBundle / Entity / User.php)将具有UserRepository(服务类 - YourBundle / Repository / UserRepository.php)。你的“过滤器”方法应该在这里。您只需要在实体类中映射此服务类。在您的控制器中,您始终可以通过其存储库访问“过滤器”。它在Symfony2 book

中详细记录