无法使用Doctrine重新创建SQL查询

时间:2016-07-26 05:36:07

标签: php doctrine-orm

我在尝试重新创建以下查询时遇到问题:

select *
from acme_opties_relaties as r
inner join acme_options as o on o.id = r.id_a
inner join acme_option_subgroup as osg on osg.option_id = o.id
where osg.subgroup_id in (309, 303, 306, 300)
and o.id <> 47086
and r.soort = 2
and o.project_id = 140018

我得到的是以下查询:

SELECT t0_.id_a AS id_a_17, t0_.id_b AS id_b_18, 
t0_.soort AS soort_0, t1_.id AS id_1, t1_.external_id AS external_id_2, t1_.external_ref AS external_ref_3, t1_.`name`
AS name_4, t1_.name_sales AS name_sales_5, t1_.price AS price_6, t1_.warranty AS warranty_7, t1_.material AS material_8, t1_.no_option
AS no_option_9, t1_.print AS print_10, t1_.text_warranty AS text_warranty_11, t1_.text_technical AS text_technical_12, t1_.text_sales AS text_sales_13, t1_.`group`
AS group_14, t1_.created AS created_15, t1_.updated AS updated_16, t1_.project_id AS project_id_19
FROM acme_opties_relaties t0_
INNER JOIN acme_options t1_ ON
(t1_.id <> 47086 AND t0_.soort = 2 AND EXISTS
(SELECT 1 FROM acme_option_subgroup t2_ INNER JOIN acme_subgroep t3_ ON t2_.subgroup_id = t3_.id WHERE t2_.option_id = t1_.id AND t3_.id IN (309, 303, 306, 300))
AND t1_.project_id = 140018)

任何指针/方向都会受到赞赏。

<?php
declare(strict_types = 1);

namespace Acme\Domain\Entity\Option;


use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Acme\Domain\Entity\Element\SubElement;
use Acme\Domain\Value\Option\Relation\Type;

/**
 * Class RelationRepository
 * Relations are interpreted as following.
 * id_a is depending on id_b
 * id_a is selected by id_b
 * id_a is excluded by id_b
 *
 * @package Acme\Domain\Entity\Option
 */
class RelationRepository extends EntityRepository
{
    /**
     * @var array
     */
    private $directions = ['left', 'right'];

    /**
     * getLeft returns the left side of the relations.
     *
     * @param Option $option
     * @param SubElement $subElement
     * @param Type $type
     * @return array
     */
    private function getLeft(Option $option, SubElement $subElement, Type $type, bool $inversed = false): Query
    {
        return $this->getQuery($option, $subElement, $type, 'left', $inversed);
    }

    /**
     * getRight returns the right side of the relations.
     *
     * @param Option $option
     * @param SubElement $subElement
     * @param Type $type
     * @return array
     */
    private function getRight(Option $option, SubElement $subElement, Type $type, bool $inversed = false): Query
    {
        return $this->getQuery($option, $subElement, $type, 'right', $inversed);
    }

    /**
     * getQuery returns the full Query needed to get a Collection of Relations.
     *
     * @param Option $option
     * @param SubElement $subElement
     * @param Type $type
     * @param string $direction
     * @return Query
     */
    private function getQuery(Option $option, SubElement $subElement, Type $type, string $direction, bool $inversed): Query
    {
        $direction = strtolower($direction);

        $this->validateDirection($direction);

        $qb = $this->_em->createQueryBuilder()
            ->select(['r', 'option'])
            ->from('Acme\Domain\Entity\Option\Relation', 'r')
            ->join('Acme\Domain\Entity\Option\Option', 'option')
        ;

        $subGroups = iterator_to_array($subElement->getElement()->getSubGroups());

        return $this->addDirectionWhere($qb, $direction, $inversed)
            ->andWhere('r.type=:type')
            ->andWhere(':subGroup MEMBER OF option.subGroups')
            ->andWhere('option.project=:project')
            ->setParameter(':type', $type->getValue())
            ->setParameter(':opt', $option->getId())
            ->setParameter(':project', $option->getProject())
            ->setParameter(':subGroup', $subGroups)
            ->getQuery();
    }

    /**
     * validateDirection checks if the direction given is valid.
     *
     * @param string $direction
     */
    private function validateDirection(string $direction)
    {
        if (!in_array($direction, $this->directions, true)) {
            throw new \InvalidArgumentException(sprintf('Unexpected direction given [%s]. Expected on of: [%s]',
                $direction,
                implode(', ', $direction)
            ));
        }
    }

    /**
     * addDirectionWhere returns a prepared QueryBuilder with a WHERE on the side needed.
     *
     * @param $direction
     * @return \Doctrine\ORM\QueryBuilder
     */
    private function addDirectionWhere(QueryBuilder $qb, $direction, $inversed = false): QueryBuilder
    {
        switch ($direction) {
            case 'right':
                $where = $inversed ? 'r.relation<>:opt' : 'r.relation=:opt';
                break;
            default:
                $where = $inversed ? 'r.option<>:opt' : 'r.option=:opt';
                break;
        }

        $qb->where($where);

        return $qb;
    }

    /**
     * getRequiredOptions returns a Collection of Relations which are required by the given Option.
     *
     * @param Option $option
     * @param SubElement $subElement
     * @return array
     */
    public function getRequiredOptions(Option $option, SubElement $subElement): array
    {
        return $this->getRight($option, $subElement, Type::get(Type::DEPEND))->getResult();
    }

    /**
     * getDependingOptions returns a Collection of Relations which depend on the given Option.
     *
     * @param Option $option
     * @param SubElement $subElement
     * @return mixed
     */
    public function getDependingOptions(Option $option, SubElement $subElement): array
    {
        return $this->getLeft($option, $subElement, Type::get(Type::DEPEND))->getResult();
    }

    /**
     * getRequiringOptionsExceptCurrent returns a Collection of Relations which require a Option, except the given Option.
     *
     * @param Option $option
     * @param SubElement $subElement
     * @return array
     */
    public function getRequiringOptionsExceptCurrent(Option $option, SubElement $subElement): array
    {
        return $this->getLeft($option, $subElement, Type::get(Type::DEPEND), true)->getResult();
    }

    /**
     * getExceludedOptions returns a Collection of Relations which are excluded by the given Option.
     *
     * @param Option $option
     * @param SubElement $subElement
     * @return array
     */
    public function getExceludedOptions(Option $option, SubElement $subElement): array
    {
        return $this->getLeft($option, $subElement, Type::get(Type::EXCLUDE))->getResult();
    }

    /**
     * getExcludedByOptions returns a Collection of Relations which exclude the given Option.
     *
     * @param Option $option
     * @param SubElement $subElement
     * @return array
     */
    public function getExcludedByOptions(Option $option, SubElement $subElement): array
    {
        return $this->getRight($option, $subElement, Type::get(Type::EXCLUDE))->getResult();
    }

    /**
     * getSelectedOptions returns a Collection of Relations which are selected by the given Option.
     *
     * @param Option $option
     * @param SubElement $subElement
     * @return array
     */
    public function getSelectedOptions(Option $option, SubElement $subElement): array
    {
        return $this->getLeft($option, $subElement, Type::get(Type::SELECT))->getResult();
    }

    /**
     * getSelectedByOptions returns a Collection of Relations which select the given Option.
     *
     * @param Option $option
     * @param SubElement $subElement
     * @return array
     */
    public function getSelectedByOptions(Option $option, SubElement $subElement): array
    {
        return $this->getRight($option, $subElement, Type::get(Type::SELECT))->getResult();
    }
}
/**
 * @ORM\Entity(repositoryClass="Acme\Domain\Entity\Option\RelationRepository")
 * @ORM\Table(name="acme_opties_relaties")
 */
class Relation
{
    /**
     * @ORM\ManyToOne(targetEntity="Acme\Domain\Entity\Option\Option", inversedBy="relations")
     * @ORM\JoinColumn(name="id_a", referencedColumnName="id")
     * @ORM\Id()
     */
    private $option;

    /**
    * @ORM\OneToOne(targetEntity="Acme\Domain\Entity\Option\Option", fetch="EAGER")
    * @ORM\JoinColumn(name="id_b", referencedColumnName="id")
    * @ORM\Id()
    */
    private $relation;
/**
 * Class Option
 * @package Acme\Domain\Entity\Option
 * @ORM\Entity(repositoryClass="OptionRepository")
 * @ORM\Table(name="acme_options")
 */
class Option
{    
    /**
     * @var Collection
     * @ORM\OneToMany(targetEntity="Acme\Domain\Entity\Option\Relation", mappedBy="option")
     */
    private $relations;

问题是关系和选项之间的连接不在r.option.id_a = option.id上,而是在r.option.id_a&lt;&gt;上。 X,它应该加入id并在应用&lt;&gt;的地方加入X

1 个答案:

答案 0 :(得分:0)

哦,我们仍然可以使用大部分SQL。所以改成了:

private function getQuery(Option $option, SubElement $subElement, Type $type, string $direction, bool $inversed): Query
{
    $direction = strtolower($direction);

    $this->validateDirection($direction);

    $query = 'select r, option, relation
                from Acme\Domain\Entity\Option\Relation r
                join r.option option
                join r.relation relation
                join option.subGroups osg
                where osg in (:subGroup)
                and option.project = :project
                and r.type = :type
                ';

    $query = $this->addDirectionWhere($query, $direction, $inversed);

    $subGroups = iterator_to_array($subElement->getElement()->getSubGroups());

    $q = $this->_em->createQuery($query);
    $q->setParameter(':type', $type->getValue())
        ->setParameter(':option', $option->getId())
        ->setParameter(':project', $option->getProject())
        ->setParameter(':subGroup', $subGroups);

    return $q;
}

我仍然非常愿意知道QueryBuilder中的等价物。因为这有我的偏好。