2016-09-17 204 views
11

我正在应用下面的查询使用querybuilder,但不知何故many2many关系不能按预期工作。多对多的关系querybuilder doctrine和symfony2扩展查询

$shopData = $sm->createQueryBuilder()      
       ->select('v') 
       ->from('AdminBundle:Voucher','v') 
       ->innerJoin('v.shop', 's') 
       ->leftJoin('AdminBundle:VoucherProgram', 'vp', \Doctrine\ORM\Query\Expr\Join::ON, 'vp.id = v.program_id') 
       ->leftJoin('AdminBundle:shopHistory', 'sh', \Doctrine\ORM\Query\Expr\Join::ON, 'sh.shop = s.id') 
       ->where('s.shopStatus = :shopStatus') 
       ->setParameter('shopStatus', Shop::SHOP_ACTIVATED) 
       ->andWhere('s.highlightedHome = :highlightedHome') 
       ->setParameter('highlightedHome', Shop::SHOP_HIGHLIGHTED_HOME) 
       ->andWhere('s.offers = \'voucher\'') 
       ->setFirstResult(0) 
       ->setMaxResults(6) 
       ->addOrderBy('v.discount_amount', 'DESC') 
       ->groupBy('sh.shop') 
       ->getQuery() 
       ->getSql(); 

生成的查询看起来象下面这样:

SELECT v FROM AdminBundle:Voucher v INNER JOIN v.shop s LEFT JOIN AdminBundle:VoucherPrograms vp ON vp.id = v.program_id LEFT JOIN AdminBundle:shopHistory sh ON sh.shop = s.id WHERE s.shopStatus = :shopStatus AND s.highlightedHome = :highlightedHome AND s.offers = 'voucher' GROUP BY sh.shop ORDER BY v.discount_amount DESC 

在情况下,如果我删除一切,只保留内加入了Many2Many关系,它是按预期工作。

$sm->createQueryBuilder()      
       ->select('v') 
       ->from('AdminBundle:Voucher','v') 
       ->innerJoin('v.shop', 's'); 

下面是生成的查询:

SELECT l0.* FROM voucher l0_ INNER JOIN shop_voucher l2_ ON l0_.id = l2_.voucher_id INNER JOIN shop l1_ ON l1_.id = l2_.shop_id; 

所以我很奇怪,为什么系统不拿起正确的位置关系,当我添加更多的连接。

这里是具有Many2Many关系我的主要实体:

Shop.php

namespace AdminBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 
use Doctrine\Common\Collections\ArrayCollection; 

/** 
* Shop. 
* 
* @ORM\Table(name="shop") 
* @ORM\Entity(repositoryClass="AdminBundle\Entity\ShopRepository") 
*/ 
class Shop 
{ 
    const SHOP_DEACTIVATED = 0; 
    const SHOP_ACTIVATED = 1; 
    const SHOP_HIGHLIGHTED_HOME = 1; 
    ................................ 
    /** 
    * @ORM\ManyToMany(targetEntity="Voucher", inversedBy="shop") 
    * @ORM\JoinTable(name="shop_voucher") 
    */ 
    private $voucher; 
    ................................ 

Voucher.php

namespace AdminBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 
#use Doctrine\Common\Collections\ArrayCollection; 

/** 
* Voucher. 
* 
* @ORM\Table(name="voucher") 
* @ORM\Entity(repositoryClass="AdminBundle\Entity\VoucherRepository") 
*/ 
class Voucher 
{ 
    ................................ 
    /** 
    * @ORM\ManyToMany(targetEntity="Shop", mappedBy="voucher", cascade={"persist"})  
    */ 
    private $shop; 
    ................................ 

我已经检查堆栈有相同的问题,但我想知道我们如何扩展查询。对于我的问题,我有如下的解决方案,但没有完全确定上述情况中发生的情况。

$shopDataQuery = $connection->prepare('SELECT v.* FROM voucher AS v INNER JOIN shop_voucher AS sv ON sv.voucher_id = v.id INNER JOIN shop AS s ON s.id = sv.shop_id LEFT JOIN voucher_programs AS vp ON vp.id = v.program_id LEFT JOIN shop_history AS sh ON sh.shop = s.id WHERE s.shopStatus = :shopStatus AND s.highlightedHome = :highlightedHome AND s.offers = 'voucher' GROUP BY sh.shop ORDER BY v.discount_amount DESC LIMIT 6'); 

更新:

这里是shopHistory.php

namespace AdminBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 
use Doctrine\Common\Collections\ArrayCollection; 

/** 
* shopHistory. 
* 
* @ORM\Table(name="shop_history") 
* @ORM\Entity(repositoryClass="AdminBundle\Entity\shopHistoryRepository") 
*/ 
class shopHistory 
{ 
    /** 
    * @var int 
    * 
    * @ORM\Column(name="id", type="integer") 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    private $id; 

    /** 
    * @var int 
    * @ORM\ManyToOne(targetEntity="Shop", inversedBy="shopHistory") 
    * @ORM\JoinColumn(name="shop", referencedColumnName="id") 
    */ 
    private $shop; 

这里是VoucherProgram.php

namespace AdminBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 
use Doctrine\Common\Collections\ArrayCollection; 

/** 
* VoucherProgram. 
* 
* @ORM\Table(name="voucher_program") 
* @ORM\Entity(repositoryClass="AdminBundle\Entity\VoucherProgramRepository") 
*/ 
class VoucherProgram 
{ 
    /** 
    * @var int 
    * 
    * @ORM\Column(name="id", type="integer") 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    private $id; 

    /** 
    * @ORM\OneToMany(targetEntity="Voucher", mappedBy="program") 
    */ 
    private $voucher; 

    /** 
    * @ORM\OneToMany(targetEntity="Shop", mappedBy="vprogram") 
    */ 
    private $shop; 
+0

检查' - > groupBy('sh.shop')'这个语句,这需要'select' clausule中的商店实体的列ID。 – yceruto

+0

我已评论groupby,但结果相同或不完全符合您的意思。 – Kamal

+0

你如何得到疑问? 第一个似乎是DQL查询(因此,如果您不强制连接列,则不指定连接列),第二个是SQL查询 – Seb33300

回答

2

我想这是因为你命名你的关系与您的领域相同:

SELECT v 
FROM AdminBundle:Voucher v 
INNER JOIN v.shop s 
LEFT JOIN AdminBundle:VoucherPrograms vp ON vp.id = v.program_id 
LEFT JOIN AdminBundle:shopHistory sh ON sh.shop = s.id 
WHERE s.shopStatus = :shopStatus 
AND s.highlightedHome = :highlightedHome 
AND s.offers = 'voucher' 
GROUP BY sh.shop 
ORDER BY v.discount_amount DESC 

尝试在你的shopHistory实体领域shop_id重命名。

但我没有完整的实体模型来进行测试!

如果不是它,尝试粘贴5个实体的代码,请...

编辑:

我让我的本地机器上测试了symfony的2.8版。

我已经生成了给定模型的实体。

我刚才说这实体:

/** 
* @ORM\ManyToOne(targetEntity="voucherProgram", inversedBy="voucher") 
*/ 
private $program; 

我有这个疑问:

我的第一个查询

SELECT v 
FROM AppBundle:Voucher v 
INNER JOIN v.shop s 
LEFT JOIN AppBundle:voucherProgram vp WITH vp.id = v.program 
LEFT JOIN AppBundle:shopHistory sh 
WITH sh.shop = s.id 
GROUP BY sh.shop 

我的第二个SQL查询

SELECT v0_.id AS id0, v0_.program_id AS program_id1 
FROM voucher v0_ 
INNER JOIN shop_voucher s2_ ON v0_.id = s2_.voucher_id 
INNER JOIN shop s1_ ON s1_.id = s2_.shop_id 
LEFT JOIN voucher_program v3_ ON (v3_.id = v0_.program_id) 
LEFT JOIN shop_history s4_ ON (s4_.shop = s1_.id) 
GROUP BY s4_.shop 
LIMIT 6 
OFFSET 0 

我认为产生的东西是正确的!

编辑2:

我尝试用symfony的标准版:

$shopData = $this->getDoctrine() 
    ->getManager() 
    ->createQueryBuilder()      
    ->select('v') 
    ->from('AppBundle:Voucher','v') 
    ->innerJoin('v.shop', 's') 
    ->leftJoin('AppBundle:voucherProgram', 'vp', 'WITH', 'vp.id = v.program') 
    ->leftJoin('AppBundle:shopHistory', 'sh', 'WITH', 'sh.shop = s.id') 
    //->where('s.shopStatus = :shopStatus') 
    //->setParameter('shopStatus', Shop::SHOP_ACTIVATED) 
    //->andWhere('s.highlightedHome = :highlightedHome') 
    //->setParameter('highlightedHome', Shop::SHOP_HIGHLIGHTED_HOME) 
    //->andWhere('s.offers = \'voucher\'') 
    ->setFirstResult(0) 
    ->setMaxResults(6) 
    //->addOrderBy('v.discount_amount', 'DESC') 
    ->groupBy('sh.shop') 
    ->getQuery() 
    ->getDql(); 

我composer.json是:

"php": ">=5.3.9", 
"symfony/symfony": "2.8.*", 
"doctrine/orm": "^2.4.8", 
"doctrine/doctrine-bundle": "~1.4", 
"symfony/swiftmailer-bundle": "~2.3", 
"symfony/monolog-bundle": "~2.4", 
"sensio/distribution-bundle": "~5.0", 
"sensio/framework-extra-bundle": "^3.0.2", 
"incenteev/composer-parameter-handler": "~2.0" 

而且我的实体:

namespace AppBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 

/** 
* Shop 
* 
* @ORM\Table(name="shop") 
* @ORM\Entity(repositoryClass="AppBundle\Repository\ShopRepository") 
*/ 
class Shop 
{ 
    const SHOP_DEACTIVATED = 0; 
    const SHOP_ACTIVATED = 1; 
    const SHOP_HIGHLIGHTED_HOME = 1; 

    /** 
    * @var int 
    * 
    * @ORM\Column(name="id", type="integer") 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    private $id; 


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

    /** 
    * @ORM\ManyToMany(targetEntity="Voucher", inversedBy="shop") 
    * @ORM\JoinTable(name="shop_voucher") 
    */ 
    private $voucher; 
} 

shopHistory

namespace AppBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 

/** 
* shopHistory 
* 
* @ORM\Table(name="shop_history") 
* @ORM\Entity(repositoryClass="AppBundle\Repository\shopHistoryRepository") 
*/ 
class shopHistory 
{ 
    /** 
    * @var int 
    * 
    * @ORM\Column(name="id", type="integer") 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    private $id; 


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

    /** 
    * @var int 
    * @ORM\ManyToOne(targetEntity="Shop", inversedBy="shopHistory") 
    * @ORM\JoinColumn(name="shop", referencedColumnName="id") 
    */ 
    private $shop; 
} 

namespace AppBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 

/** 
* Voucher 
* 
* @ORM\Table(name="voucher") 
* @ORM\Entity(repositoryClass="AppBundle\Repository\VoucherRepository") 
*/ 
class Voucher 
{ 
    /** 
    * @var int 
    * 
    * @ORM\Column(name="id", type="integer") 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    private $id; 


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

    /** 
    * @ORM\ManyToMany(targetEntity="Shop", mappedBy="voucher", cascade={"persist"})  
    */ 
    private $shop; 

    /** 
    * @ORM\ManyToOne(targetEntity="voucherProgram", inversedBy="voucher") 
    */ 
    private $program; 
} 

voucherProgram

namespace AppBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 

/** 
* voucherProgram 
* 
* @ORM\Table(name="voucher_program") 
* @ORM\Entity(repositoryClass="AppBundle\Repository\voucherProgramRepository") 
*/ 
class voucherProgram 
{ 
    /** 
    * @var int 
    * 
    * @ORM\Column(name="id", type="integer") 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    private $id; 

    /** 
    * @ORM\OneToMany(targetEntity="Voucher", mappedBy="program") 
    */ 
    private $voucher; 

    /** 
    * @ORM\OneToMany(targetEntity="Shop", mappedBy="vprogram") 
    */ 
    private $shop; 

    /** 
    * Get id 
    * 
    * @return integer 
    */ 
    public function getId() 
    { 
     return $this->id; 
    } 
} 
+0

SQL查询工作正常,但我想知道为什么querybuilder没有给出正确的关系。我会尝试按照您建议的方式更改字段,但它具有相同的列名称 - 表格中的“shop_id”。 – Kamal

+0

我认为querybuilder没有给出正确的关系,可能是因为有“购物”在您的查询。你能否以另一种方式粘贴给我5个实体的示例代码(不是全部)?我会尽力帮助你,我可以用我的本地机器... –

+0

我已更新实体。 – Kamal

1

据我看到,第一个给出的查询是DQL查询,第二个是SQL查询。 DQL查询可能会更短,并且仍然按预期工作,因为它不会操作表格,但是可以实现。因此,您需要较少的连接来加入m2m关系(生成最终SQL时扩展为双连接)

如果生成的DQL与给定的查询生成器一起生成,则无法找到任何错误。

+0

感谢您的回答,但这不是我想要的。 DQL和SQL都不是错的。但是没有任何额外连接的DQL给出了正确的关系,而更多的连接关系发生了变化,它应该与SQL相同。 – Kamal

+0

'DQL'不应该与'SQL'相同。这是更高的抽象层次。你可以用'实际'和'预期'两种查询来更新帖子,因为我真的不明白什么是错误的检查 – ScayTrase

+0

SQL是预期的和实际的查询,我想要做的就是将其转换为DQL。 – Kamal