Картографиране и DQL

Имам 3 таблици - потребител, зона и контакти. Контактът може да принадлежи на потребител или област. Един потребител може да принадлежи към много области.

Искам да изтегля всички контакти, които принадлежат на потребител (както е конкретно дефинирано в DB), както и всички контакти, които принадлежат към същата област като потребителя.

Мога ли да получа нов поглед върху картографирането на моята база данни и заявката, която трябва да напиша в DQL, за да получа това, което искам. Правя ли нещо нередно в картографирането на моята база данни?

Определено съм SQL човек и мога лесно да извлека това, което искам в обикновен SQL. В обикновен SQL, ето какво искам да направя:

 select c.* from contact c LEFT JOIN user_area ua ON c.area_id=ua.area_id where (ua.user_id=XXX OR c.user_id=XXX);

ПОТРЕБИТЕЛ

/**
 * @ORM\ManyToMany(targetEntity="area", inversedBy="areas")
 * @ORM\JoinTable(name="user_area",
 *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="area_id", referencedColumnName="id")}
 *      )
 */
private $areas;

/**
 * @ORM\OneToMany(targetEntity="Contact", mappedBy="user")
 */
private $contacts;

КОНТАКТИ

/**
 * @ORM\ManyToOne(targetEntity="Area")
 * @ORM\JoinColumn(name="area_id", referencedColumnName="id")
 */
private $area;

/**
 * @ORM\ManyToOne(targetEntity="User", inversedBy="Contacts")
 * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
 */
private $user;

ОБЛАСТ

/**
 * @ORM\ManyToMany(targetEntity="User", mappedBy="users")
 */
private $users;

/**
 * @ORM\OneToMany(targetEntity="Contact", mappedBy="area")
 */
private $contacts;

Основният проблем, с който се сблъсквам, е, че DQL наистина иска да направите заявка за обект и е просто по-лесно в SQL да заявите таблицата за връзка потребител/област, за да получа това, което искам. Опитах се да напиша заявка, която извлича области от контакти, след това потребители от контакти и след това потребители от области, но получавам съобщение за грешка, че „потребители“ не е дефиниран индекс в моя обект на области. Отново, аз съм начинаещ в Doctrine, така че вероятно правя нещо нередно.

Ето моя опит за заявка от потребителския обект в Symfony:

    $qb = $em->createQueryBuilder()
        ->addSelect('c')
        ->from('MyBundle:Contact', 'c')
        ->leftJoin('c.area', 'ca')
        ->leftJoin('c.user', 'cu')
        ->leftJoin('ca.users', 'cau')
        ->add('where', 'c.user = ?1 OR cau.id = ?1')
        ->add('orderBy', 'c.name')
        ->setParameter(1, $this->getId());

person thexfactor    schedule 03.05.2013    source източник
comment
$qb = $em-›createQueryBuilder('c') задава основния псевдоним за избор, така че няма да е необходимо да добавятеSelect. Освен това не мисля, че имате нужда от третото присъединяване.   -  person Lighthart    schedule 04.05.2013
comment
Можете просто да използвате PDO, не е нужно да използвате doctorine. И тъй като нямате обекти, какъв е смисълът   -  person raam86    schedule 04.05.2013
comment
@Lighthart Ако изключа третото присъединяване (и следователно също премахна cau.id от клаузата WHERE), тогава оставам само с всички контакти, които са собственост на потребителя. Няма грешка, но също така искам да изтегля всички контакти, които имат обща област с всички области, към които принадлежи потребителят. Все пак благодаря за предложението.   -  person thexfactor    schedule 04.05.2013
comment
Трябва да получавате тези отношения чрез извлечените обекти. Доктрина: получава обекти, а не кортежи.   -  person Lighthart    schedule 04.05.2013


Отговори (2)


Някой трябваше да ми удари шамар за предишния отговор. Въпреки че свърши работата, бях абсолютно прав, не беше оптимизиран. Заявките, използващи този метод, отнемаха 3 секунди, за да се върнат напред и назад към базата данни (3 секунди!). Ясно е, че имаше много други неща, които се случваха в моя свят, които отнеха изпълнението като изискване за постигане на това, но нещата се промениха. Успях да разделя тази заявка на две по-малки (генерирани от Doctrine), всяка от които отнема може би 0,2 или 0,3 s.

    $areas = $user->getAreas();

    $qb = $this->getEntityManager()->createQueryBuilder();
    $qb->select('c')
        ->from('MyBundle:Contact', 'c')
        ->where($qb->expr()->in('c.area', '?1'))
        ->orWhere('c.user = ?2')
        ->setParameter(1, $areas->toArray())
        ->setParameter(2, $user);
    $query = $qb->getQuery();
    $result = $query->getResult();
    return $result;

Фактът, че трябва да извикам $user->getAreas(), добавя заявка към базата данни (ако Doctrine все още няма тази информация), но този код, използващ изрази на Query Builder, работи много по-добре (0,3 s срещу 3 s е 10% от първоначалното време за заявка !).

Мисля, че основната концепция, която ми липсваше тогава, беше, че Query Builder иска да работи с вашите обекти (Entities) и свойствата, които сте дефинирали във вашите обекти. Идвайки със силен опит в SQL и знаейки конкретната SQL заявка, която исках да произведе Doctrine, не подходих правилно към проблема.

Надяваме се, че тази актуализация на 8-месечен въпрос ще помогне на някого!

person thexfactor    schedule 24.01.2014

Така се оказва, че не можете да извличате обекти на обекти в DQL. Трябваше да извлека всички „Зона“ (обект на „Контакт“) и след това да извлека всички „Потребители“ на тази област.

В DQL можете да посочите множество помощни методи "from()" и това беше нещото, от което се нуждаех, за да свърша работата.

$qb = $em->createQueryBuilder()
    ->addSelect('c')
    ->from('MyBundle:Contact', 'c')
    ->from('MyBundle:User', 'u')
    ->leftJoin('c.area', 'ca')
    ->leftJoin('c.user', 'cu')
    ->leftJoin('u.areas', 'ua')
    ->add('where', 'c.user = ?1 OR (ua.id=ca.id AND u.id = ?1')
    ->add('orderBy', 'c.name')
    ->setParameter(1, $this->getId());

Полученият SQL, генериран от Doctrine, не изглежда особено оптимизиран, но върши работата. Ако някой има някакви мисли как да накара Doctrine да оптимизира по-добре следната заявка, ще се радвам да чуя мнения.

ИЗБЕРЕТЕ m0_.id AS id0, m0_.name AS name1, m0_.email AS email2, m0_.media_area_id AS media_area_id3, m0_.user_id AS user_id4 ОТ контакт m0_ LEFT JOIN user u1_ ON m0_.user_id = u1_.id LEFT JOIN area m2_ ON m0_.area_id = m2_.id, потребител u3_ LEFT JOIN потребителска_област u5_ ON u3_.id = u5_.user_id LEFT JOIN област m4_ ON m4_.id = u5_.area_id WHERE u1_.id = ? ИЛИ (m4_.id = m2_.id И u3_.id = ?);

person thexfactor    schedule 04.05.2013