Запутался в использовании DQL с сущностями Doctrine2 и фреймворком Zend

У меня есть 4 объекта: страна, регион, провинция, город.

<?php

namespace Entities;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @Entity (repositoryClass="Repositories\Region") 
 * @Table(name="regions") 
 * @HasLifecycleCallbacks
 */
class Region {

    /**
     * @Id @Column(type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    private $id;

    /** @Column(type="string", length=30,unique=TRUE) */
    private $regionname;

    /** @Column(type="boolean") */
    private $active;

    /**
     * @ManyToOne(targetEntity="Country", inversedBy="regions")
     * @JoinColumn(name="countries_id", referencedColumnName="id",nullable=FALSE)
     */
    private $countries_id;

    /**
     * @OneToMany(targetEntity="Province", mappedBy="provinces")
     */
    private $provinces;

    public function __construct() {
        $this->provinces = new ArrayCollection();
        $this->active = true;
    }

<?php

namespace Entities;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @Entity (repositoryClass="Repositories\Province") 
 * @Table(name="provinces") 
 * @HasLifecycleCallbacks
 */
class Province {

    /**
     * @Id @Column(type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    private $id;

    /** @Column(type="string", length=30,unique=TRUE) */
    private $provincename;

    /** @Column(type="boolean") */
    private $active;

    /**
     * @ManyToOne(targetEntity="Region", inversedBy="provinces")
     * @JoinColumn(name="regions_id", referencedColumnName="id",nullable=FALSE)
     */
    private $regions_id;

    /**
     * @OneToMany(targetEntity="Town", mappedBy="towns")
     */
    private $towns;

    public function __construct() {
        $this->towns = new ArrayCollection();
        $this->active = true;
    }

<?php

namespace Entities;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @Entity (repositoryClass="Repositories\Town") 
 * @Table(name="towns") 
 * @HasLifecycleCallbacks
 */
class Town {

    /**
     * @Id @Column(type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    private $id;

    /** @Column(type="string", length=30,unique=FALSE) */
    private $townname;

    /** @Column(type="boolean") */
    private $active;
    // so that we know when a user has added a town
    /** @Column(type="boolean") */
    private $verified;

    /**
     * @OneToMany(targetEntity="User", mappedBy="users")
     */
    private $users;

    /**
     * @ManyToOne(targetEntity="Province", inversedBy="towns")
     * @JoinColumn(name="provinces_id", referencedColumnName="id",nullable=FALSE)
     */
    private $provinces_id;

    public function __construct() {
        $this->users = new ArrayCollection();

        $this->active = true;
    }

Я хочу создать запрос с использованием DQL, который даст мне список городов для данного региона.

Чтобы получить простой список активных городов, я использую:

public function findActiveTowns($provinces_id = null)
// we can pass in a specific provinces_id if we want
{

    $qb = $this->_em->createQueryBuilder();
    $qb->select('a.townname, a.id')
    ->from('Entities\Town', 'a');

    if (!is_null($provinces_id)){
        $qb->where('a.provinces_id = :provinces_id AND a.active = TRUE')
        ->setParameter('provinces_id', $provinces_id);
    } else {
        $qb->where('a.active = TRUE');
    }

    $towns=$qb->getQuery()->getResult();

    // make pairs array suitable for select lists
    $options = array();
    foreach ($towns as $key => $value) {
        $options[$value['id']] = $value['townname'];
    }
    return $options;
}

Теперь, чтобы добраться до сути. Как мне настроить соединения и заставить это работать, чтобы мы могли передать region_id и вернуть все города в регионе.

В родном SQL я бы сделал что-то вроде этого:

SELECT towns.id
FROM  `towns` 
INNER JOIN  `provinces` 
INNER JOIN  `regions` 
WHERE regions.id =1

Спасибо.


person dimbo    schedule 16.01.2012    source источник


Ответы (1)


Сначала несколько вещей.

  1. Не называйте свои поля с помощью _id, потому что они являются не идентификаторами, а отношениями с другими объектами. Аннотация столбца соединения идет с реальным именем БД, поле в объектной модели не используется.
  2. Напишите/создайте методы get/set/add для всех полей, чтобы инкапсулировать их, чтобы вы могли их использовать. Вы не можете читать приватные поля «снаружи».

Что касается вашего вопроса, не проверял, но что-то вроде этого должно работать.

class Town {
    /**
     * @ManyToOne(targetEntity="Province", inversedBy="towns")
     * @JoinColumn(name="provinces_id", referencedColumnName="id",nullable=FALSE)
     */
    private $province;

class Province {
    /**
     * @ManyToOne(targetEntity="Region", inversedBy="provinces")
     * @JoinColumn(name="regions_id", referencedColumnName="id",nullable=FALSE)
     */
    private $region;

$qb->select('a.townname, a.id')
   ->from('Entities\Town', 'a')
   ->leftJoin('a.province', 'p');

if (!is_null($provinces_id) && !is_null($region_id)){
    $qb->where('a.province = :province AND a.active = TRUE')
       ->andWhere('p.region = :region')
       ->setParameter('province', $provinces_id)
       ->setParameter('region', $region_id);
} else {
    $qb->where('a.active = TRUE');
}
person ZolaKt    schedule 16.01.2012
comment
Привет, спасибо за ваши очки. У меня есть геттеры и сеттеры, но я просто исключил их, чтобы сделать код короче. - person dimbo; 16.01.2012
comment
Привет, я пытался сделать ЛЕВЫЕ соединения весь день и продолжаю сталкиваться с этой ошибкой: Ошибка: Класс Entities\Town не имеет ассоциации с названием провинции. Я пробовал ваш код выше (исключая условия WHERE) и получаю ту же ошибку. Любые идеи? - person dimbo; 16.01.2012
comment
Вы должны переименовать частный $provinces_id; в частную провинцию; в вашем городе, и это должно помочь! - person Kees Schepers; 17.01.2012
comment
Спасибо вам обоим. Работает отлично! - person dimbo; 17.01.2012