ceadreak
ceadreak

Reputation: 1684

Doctrine2 / querying discriminator value

For a project, I need to manage "cards". There are 2 card types : "Client card" and "Member card". Cards can be ordered by a user of this application.

To achieve that, I created an abstract Card entity using a Type discriminator.

/* @ORM\Entity
 * @ORM\Table(name="cards")
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="type", type="string")
 * @ORM\DiscriminatorMap({"member"="MemberCard", "client"="ClientCard"})
 */
abstract class Card
{
    const MEMBER = 'member';
    const CLIENT = 'client';

    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * @ORM\Column(type="integer")
     */
    protected $id;

    /**
     * @ORM\ManyToOne(targetEntity="CardsOrder", inversedBy="cards")
     * @ORM\JoinColumn(name="order_id", referencedColumnName="id", onDelete="CASCADE")
     */
    protected $order;

    /**
     * Return the card type
     *
     * @return string
     */
    abstract public function getType();

    /**
     * @return mixed
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @param CardsOrder $order
     *
     * @return $this
     */
    public function setOrder(CardsOrder $order)
    {
        $this->order = $order;

        return $this;
    }

    /**
     * @return CardsOrder
     */
    public function getOrder()
    {
        return $this->order;
    }
}

The MemberCard entity

/**
 * @ORM\Entity
 */
class MemberCard extends Card
{

    /**
     * @ORM\ManyToOne(targetEntity="Member", inversedBy="cards")
     * @ORM\JoinColumn(name="member_id", referencedColumnName="id", onDelete="CASCADE")
     */
    protected $member;

    /**
     * @param Member $member
     *
     * @return $this
     */
    public function setMember(Member $member)
    {
        $this->member = $member;

        return $this;
    }

    /**
     * @return Member
     */
    public function getMember()
    {
        return $this->member;
    }

    /**
     * @return string
     */
    public function getType()
    {
        return self::MEMBER;
    }
}

The ClientCard entity

/**
 * @ORM\Entity
 */
class ClientCard extends Card
{

    /**
     * ClientCard - client n-1 relation
     *
     * @ORM\ManyToOne(targetEntity="Client", inversedBy="cards")
     * @ORM\JoinColumn(name="client_id", referencedColumnName="id", onDelete="CASCADE")
     */
    protected $client;

    /**
     * @param \Admin\Crm\Entity\Client\Client $client
     *
     * @return $this
     */
    public function setClient(Client $client)
    {
        $this->client = $client;

        return $this;
    }

    /**
     * @return Client
     */
    public function getClient()
    {
        return $this->client;
    }

    /**
     * @return string
     */
    public function getType()
    {
        return self::CLIENT;
    }

}

Now, I want to know if a member or a client has a pending ordered card (status is a binary status flag) :

public function findPendingCard($clientOrMember)
{
    // $this->getRepository is the Card repository, injected in a factory
    $query = $this->getRepository()->createQueryBuilder('c')
                      ->join('c.order', 'o', 'WITH', 'BIT_AND(o.status, :oStatus) > 0')
                      ->where('c INSTANCE OF :memberCardEntity AND c.member = :clientOrMember')
                      ->orWhere('c INSTANCE OF :clientCardEntity AND c.client = :clientOrMember')
                      ->setParameters([
                          'oStatus'        => CardsOrder::OPEN,
                          'clientOrMember' => $clientOrMember,
                          'memberCardEntity'   => MemberCard::class,
                          'clientCardEntity'   => ClientCard::class,
                      ])
                      ->getQuery();

        return $query->getOneOrNullResult();
    }

But I got this error :

[Semantical Error] line 0, col 148 near 'member = :clientOrMember)': Error: Class Card has no field or association named member

Any idea what I'm doing wrong?

Upvotes: 0

Views: 148

Answers (1)

geoB
geoB

Reputation: 4704

It's not so much that you're doing something wrong, but there is a method to work around the issue. You can make it possible to return the card type with the following.

In class Card add:

public function getCardType()
{
    return $this->discr;
}

in class MemberCard add:

protected $discr = 'member';

in class ClientCard add:

protected $discr = 'client';

Upvotes: 2

Related Questions