Doctrine ORM Conditional Association

2019-07-06 16:19发布

i'm building a Q&A site and my questions, answers and comments are on the same posts table. But their postType is different. I can get answers for a question and comments for an answer with this association:

/**
 * @OneToMany(targetEntity="Cms\Entity\Post", mappedBy="parent")
 */
private $answers;

/**
 * @OneToMany(targetEntity="Cms\Entity\Post", mappedBy="parent")
 */
private $comments;

But i think this is not the correct way to do this because if i fetch a question both answers and comments are filling with just answers. I have to set a condition for relation like postType = 1

How can i do this?

2条回答
不美不萌又怎样
2楼-- · 2019-07-06 16:22

Use Doctrine's Filtering Collections Criteria class. You can even filter the collection first before the sql query:

If the collection has not been loaded from the database yet, the filtering API can work on the SQL level to make optimized access to large collections.

use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;

...

    /** @var Collection */
    protected $posts;

    /**
     * @return Post[]
     */
    public function getAnswers()
    {
        $criteria = Criteria::create()
            ->where(Criteria::expr()->eq('postType', 'answer'))
        ;

        return $this->posts->matching($criteria);
    }

    /**
     * @return Post[]
     */
    public function getComments()
    {
        $criteria = Criteria::create()
            ->where(Criteria::expr()->eq('postType', 'comment'))
        ;

        return $this->posts->matching($criteria);
    }
查看更多
等我变得足够好
3楼-- · 2019-07-06 16:37

Your schema is invalid. You schould have two different objects for answers and comments as they are two different things, even if they share a common interface.

You should create two entities, Answer and Comment and create assocations to them. As they are almost the same thing you could create an abstract class, AbstractContent, that defines all required fields and accessor methods. Doctrine supports inheritance so the final database schema will be exactly the same, but your OO model will be correct.

/** 
 * @MappedSuperclass 
 * @InheritanceType("SINGLE_TABLE")
 * @DiscriminatorColumn(type = "string", name = "discriminator")
 * @DiscriminatorMap({ "answer" = "Answer", "comment" = "Comment" })
 */
abstract class AbstractContent {
    /** @Column(type = "integer") @Id @GeneratedValue("AUTO") */
    protected $id;

    /** @Column(type="text") */
    protected $content;

    /** @Column(type = "datetime", name = "created_at") */
    protected $createdAt;

    public function __construct() {
        $this->createdAt = new \DateTime();
    }
}

/** @Entity */
class Answer extends AbstractContent { }

/** @Entity */
class Comment extends AbstractContent { }

/**
 * @OneToMany(targetEntity="Cms\Entity\Answer", mappedBy="parent")
 */
private $answers;

/**
 * @OneToMany(targetEntity="Cms\Entity\Comment", mappedBy="parent")
 */
private $comments;

You can read more about inheritance in Doctrine on its documentation pages: Inheritance Mapping

查看更多
登录 后发表回答