Doctrine 2:在单表继承中获取具有或不具有一对多关联的匹配记录的所有记录

时间:2014-02-25 07:34:09

标签: symfony orm doctrine-orm

我试图根据列表位置找到每个类别中的商品总数

我要做的是,如果来自美国的用户访问该网站,则只显示该位置的记录。此外,侧栏上的类别小部件还应考虑该位置中每个类别下的列表总数。如果访问者点击芝加哥,则窗口小部件应重新计算以反映位置“芝加哥”

使用以下代码

    public function findCategoriesWithTotalListing($location)
{
    $builder = $this->_em->createQueryBuilder();

    $builder->select('c, count(l.id) AS num')
       ->from('Bundle\AdvertBundle\Entity\Category', 'c')
       ->leftJoin('c.listings','l')
       ->leftJoin('l.country','co')
       ->leftJoin('l.state','st')
       ->leftJoin('l.city','ci');

    $builder->groupBy('c.id');

    $result = $builder->getQuery()->getArrayResult();
    print_r($result);
    exit;
}

我可以检索所有类别,每个类别的总数不包含位置参数。

但是,如果我引入位置参数,则只检索那些在该位置有列表的类别。

    public function findCategoriesWithTotalListing($location)
{
    $builder = $this->_em->createQueryBuilder();

    $builder->select('c, count(l.id) AS num')
       ->from('Bundle\AdvertBundle\Entity\Category', 'c')
       ->leftJoin('c.listings','l')
       ->leftJoin('l.country','co')
       ->leftJoin('l.state','st')
       ->leftJoin('l.city','ci');

    $orx = $builder->expr()->orX();
    $orx->add($builder->expr()->like("co.slug", ':location'));
    $orx->add($builder->expr()->like("st.slug", ':location'));
    $orx->add($builder->expr()->like("ci.slug", ':location'));

    $builder->andWhere($orx);
    $builder->groupBy('c.id');
    $builder->setParameter('location',$location);

    $result = $builder->getQuery()->getArrayResult();
    print_r($result);
    exit;
}

我想要所有类别

我在这里做错了什么

由于

以下实体

类别实体

<?php

namespace Bundle\AdvertBundle\Entity;

use Bundle\FrameworkBundle\Entity\BaseEntity;
use Bundle\FrameworkBundle\Entity\DocumentInterface;
use Doctrine\ORM\Mapping as ORM;

/**
* Category
*
* @ORM\Table(name="listings_categories")
* @ORM\Entity(repositoryClass="Bundle\AdvertBundle\Repository\CategoryRepository")
*/
class Category extends BaseEntity implements DocumentInterface
{

/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer", precision=0, scale=0, nullable=false, unique=false)
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="IDENTITY")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="title", type="string", length=255, precision=0, scale=0, nullable=false, unique=false)
 */
private $title;

/**
 * @var string
 *
 * @ORM\Column(name="slug", type="string", length=255, precision=0, scale=0, nullable=false, unique=false)
 */
private $slug;

/**
 * @var string
 *
 * @ORM\Column(name="summary", type="text", precision=0, scale=0, nullable=false, unique=false)
 */
private $summary;

/**
 * @var integer
 *
 * @ORM\Column(name="parent", type="integer", precision=0, scale=0, nullable=false, unique=false)
 */
private $parent;

/**
 * @var \Doctrine\Common\Collections\Collection
 *
 * @ORM\OneToMany(targetEntity="ListingItem", mappedBy="category")
 */
private $listings;

/**
 * @var string
 *
 * @ORM\Column(name="thumbnail", type="string", length=255, precision=0, scale=0, nullable=true, unique=false)
 */
private $thumbnail;

/**
 * @var string
 *
 * @ORM\Column(name="form_template", type="string", length=255, precision=0, scale=0, nullable=true, unique=false)
 */
private $formTemplate;


private $children;


private $totalChildren;

private $totaListing;


/* Getters and Setters*/


}

列出实体

<?php
namespace Bundle\AdvertBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Bundle\FrameworkBundle\Entity\DocumentInterface;
use Bundle\FrameworkBundle\Entity\BaseEntity;


/**
* @ORM\Entity (repositoryClass="Bundle\AdvertBundle\Repository\ListingItemRepository")
* @ORM\Table(name="listings")
* @ORM\InheritanceType("SINGLE_TABLE")
* @ORM\DiscriminatorColumn(name="listing_type", type="string")
* @ORM\DiscriminatorMap({
 "forsale" = "ForsaleItem", 
  "service" = "ServiceItem",
 "job" = "JobItem",
 "realestate" = "RealEstateItem",
 "land" = "LandItem",
"house" = "HouseItem"
})
*/
abstract class ListingItem extends BaseEntity
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer", precision=0, scale=0, nullable=false, unique=false)
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="IDENTITY")
 */
protected $id;

/**
 * @var string
 *
 * @ORM\Column(name="title", type="string", length=255, precision=0, scale=0, nullable=false, unique=false)
 */
protected $title;

/**
 * @var string
 *
 * @ORM\Column(name="slug", type="string", length=255, precision=0, scale=0, nullable=false, unique=false)
 */
protected $slug;


/**
 * @var string
 *
 * @ORM\Column(name="price", type="decimal", precision=0, scale=2, nullable=false, unique=false)
 */
protected $price;


/**
 * @var string
 *
 * @ORM\Column(name="description", type="text", precision=0, scale=0, nullable=false, unique=false)
 */
protected $description;

/**
 * @var string
 *
 * @ORM\Column(name="thumbnail", type="string", length=255, precision=0, scale=0, nullable=true, unique=false)
 */
protected $thumbnail;

/**
 * @var string
 *
 * @ORM\Column(name="status", type="string", length=100, precision=0, scale=0, nullable=false, unique=false)
 */
protected $status;

/**
 * @var \DateTime
 *
 * @ORM\Column(name="date_created", type="datetime", precision=0, scale=0, nullable=true, unique=false)
 */
protected $dateCreated;

/**
 * @var \DateTime
 *
 * @ORM\Column(name="date_approved", type="datetime", precision=0, scale=0, nullable=true, unique=false)
 */
protected $dateApproved;

/**
 * @var \Bundle\AdvertBundle\Entity\Category
 *
 * @ORM\ManyToOne(targetEntity="Bundle\AdvertBundle\Entity\Category", inversedBy="listings")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=true)
 * })
 */
protected $category;

/**
 * @var \Bundle\UserBundle\Entity\User
 *
 * @ORM\ManyToOne(targetEntity="Bundle\UserBundle\Entity\User", inversedBy="listings")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=true)
 * })
 */
protected $user;

/**
 * @var string
 *
 * @ORM\Column(name="name", type="string", length=255, precision=0, scale=0, nullable=false, unique=false)
 */
protected $name;

/**
 * @var string
 *
 * @ORM\Column(name="email", type="string", length=255, precision=0, scale=0, nullable=false, unique=false)
 */
protected $email;

/**
 * @var string
 *
 * @ORM\Column(name="contact_number", type="string", length=255, precision=0, scale=0, nullable=false, unique=false)
 */
protected $contactNumber;

/**
 * @var \Bundle\LocationBundle\Entity\Country
 *
 * @ORM\ManyToOne(targetEntity="Bundle\LocationBundle\Entity\Country")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="country_id", referencedColumnName="id", nullable=true)
 * })
 */
private $country;

/**
 * @var \Bundle\LocationBundle\Entity\State
 *
 * @ORM\ManyToOne(targetEntity="Bundle\LocationBundle\Entity\State")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="state_id", referencedColumnName="id", nullable=true)
 * })
 */
private $state;

/**
 * @var \Bundle\LocationBundle\Entity\City
 *
 * @ORM\ManyToOne(targetEntity="Bundle\LocationBundle\Entity\City")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="city_id", referencedColumnName="id", nullable=true)
 * })
 */
private $city;


protected $dateformat = 'd-M-Y';

}

位置实体     

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity (repositoryClass="Bundle\LocationBundle\Repository\LocationRepository")
* @ORM\Table(name="locations")
* @ORM\InheritanceType("SINGLE_TABLE")
* @ORM\DiscriminatorColumn(name="location_type", type="string")
* @ORM\DiscriminatorMap({
"country" = "Country", 
"state" = "State",
"city" = "City"
})
*/
abstract class Location
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer", precision=0, scale=0, nullable=false, unique=false)
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="IDENTITY")
 */
protected $id;

/**
 * @var string
 *
 * @ORM\Column(name="name", type="string", length=255, precision=0, scale=0, nullable=false, unique=false)
 */
protected $name;

/**
 * @var string
 *
 * @ORM\Column(name="country_code", type="string", length=3, precision=0, scale=0, nullable=true, unique=false)
 */
protected $countryCode;

/**
 * @var string
 *
 * @ORM\Column(name="slug", type="string", length=255, precision=0, scale=0, nullable=false, unique=false)
 */
protected $slug;

/**
 * @var integer
 *
 * @ORM\Column(name="geoname_id", type="integer", precision=0, scale=0, nullable=false, unique=false)
 */
protected $geonameId;

/**
 * @var string
 *
 * @ORM\Column(name="lng", type="string", length=255, precision=0, scale=0, nullable=false, unique=false)
 */

protected $lng;

/**
 * @var string
 *
 * @ORM\Column(name="lat", type="string", length=255, precision=0, scale=0, nullable=false, unique=false)
 */
protected $lat;


/**
 * @var \Doctrine\Common\Collections\Collection
 *
 * @ORM\OneToMany(targetEntity="Bundle\LocationBundle\Entity\Location", mappedBy="parent")
 */
protected $children;

/**
 * @var \Bundle\LocationBundle\Entity\Location
 *
 * @ORM\ManyToOne(targetEntity="Bundle\LocationBundle\Entity\Location", inversedBy="children")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true)
 * })
 */
protected $parent;

/**
 * @var string
 *
 * @ORM\Column(name="currency_symbol", type="string", length=100, precision=0, scale=0, nullable=true, unique=false)
 */
protected $currencySymbol;

/* getters and setters*/
}

国家实体    

namespace Bundle\LocationBundle\Entity;

use Bundle\FrameworkBundle\Entity\DocumentInterface;
use Doctrine\ORM\Mapping as ORM;

/**
 * Country
 *
 * @ORM\Table(name="locations")
 * @ORM\Entity(repositoryClass="Bundle\LocationBundle\Repository\LocationRepository")
 */
class Country extends Location
{

/**
 * Set countryCode
 *
 * @param string $countryCode
 * @return Country
 */
public function setCountryCode($countryCode)
{
    $this->countryCode = $countryCode;

    return $this;
}

/**
 * Get countryCode
 *
 * @return string 
 */
public function getCountryCode()
{
    return $this->countryCode;
}
}

州和城市的吸引力就像国家一样

2 个答案:

答案 0 :(得分:0)

使用您的WHERE,您将过滤掉所有不匹配类别的内容,因此无法计算它们。应该可以通过一个查询来执行,但是您必须从WHERE移动条件以获得甚至不匹配的行并将它们计为0。 Simple SQL version:

SELECT c.*, sum(IF(loc.code = 'x', 1, 0))
FROM category c
LEFT JOIN list l ON c.id = l.c_id
LEFT JOIN loc ON loc.id = l.loc_id
GROUP BY c.id

在Doctrine中没有IF,但你可以使用

CASE WHEN (<cond>) THEN 1 ELSE 0

使用$ orx expr。

另一个可能性是将条件移动到this example之类的连接,但是你必须在学说中指定自定义ON:

  来自文档的

:任意JOIN语法(FROM User u JOIN Comment c WITH c.user = u.id)   那么你将为这三个联合表总计三个计数。这是可行但丑陋的。

或@ denys281所述的子查询 - 仅从类别中选择,但select子句包含列,该列计算每个类别的所有匹配列表。

答案 1 :(得分:0)

经过几天查阅学说中的子查询后,我终于得到了适合我情况的代码。 以下是一些让我重新思考如何编写第一个代码的pionts。

  1. 我需要所有类别。
  2. 我需要计算每个类别中的所有商品
  3. 仅选择与传入的位置参数相关的列表。
  4. 列表与位置有多对一关联。
  5. 显而易见的解决方案是使用@ danys281建议的子查询。

        public function findCategoriesWithTotalListing($location)
    {
        $subQuery = $this->_em->createQueryBuilder();
        $subQuery->select('COUNT(l.id)')
                 ->from('Bundle\AdvertBundle\Entity\ListingItem','l')
                 ->leftJoin('l.category','cat')
                 ->leftJoin('l.country','co')
                 ->leftJoin('l.state','st')
                 ->leftJoin('l.city','ci')
                 ->where('cat.id = c.id');  
    
        if($location instanceOf Country){
            $subQuery->andWhere('co.slug = :location');
        }
    
        if($location instanceOf State){
            $subQuery->andWhere('st.slug = :location');    
        }
    
        if($location instanceOf City){
            $subQuery->andWhere('ci.slug = :location');
        }
    
        $subQuery->andWhere("l.status = :status");
    
    
        $builder = $this->_em->createQueryBuilder();
        $builder->select("c, (".$subQuery->getDql().") AS num")
           ->from('Bundle\AdvertBundle\Entity\Category', 'c')
           ->setParameters(['location'=>$location->getSlug(),'status'=>'active'])
           ->groupBy('c');
    
        return $builder->getQuery()->getResult();
    }
    

    谢谢大家给我的提示。