Как получить (объединить) две записи из базы данных с помощью doctrine / symfony4

Я изучаю Symfony и Doctrine и создал простой сайт, но застрял на этом этапе.

У меня две таблицы: users и languages

Пользователи Содержит: id, имя пользователя ...
Языки Содержит: user_id, язык ...

Вот изображение двух Как получить (объединить) две записи из базы данных с помощью doctrine / symfony4

Как получить (объединить) две записи из базы данных с помощью doctrine / symfony4

Теперь я пытаюсь получить по языку, например: получить пользователя, который говорит на обоих englishиfrench, и результат вернет идентификатор пользователя 2

В простом PHP я могу выполнить внутреннее соединение с PDO, но я пытаюсь следовать синтаксису доктрины, и это не возвращает правильный результат.

public function getMatchingLanguages ($a, $b) {
  return $this->createQueryBuilder('u')
    ->andWhere('u.language = :val1 AND u.language = :val2')
    ->setParameter('val1', $a)
    ->setParameter('val2', $b)
    ->getQuery()
    ->execute();
}

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

Я не думаю, что Doctrine предоставляет более изящный способ сделать это.

Jovan Perovic 29.08.2018 01:56

Что ты имеешь в виду? Я просто спрашиваю, потому что код работает не так, как задумано. Не возвращает пользователя для языков

hidar 29.08.2018 02:03

Не поймите это неправильно, но вам действительно нужно немного внимательнее изучить документацию. Много примеров. Не говоря уже о мягких вопросах.

Cerad 29.08.2018 02:18

Я понимаю, без проблем. Мне действительно удалось создать блог-сайт с системой входа в систему, прочитав документацию по Symfony, но доктрину немного сложнее осмыслить. Мне это почему-то кажется сложным.

hidar 29.08.2018 04:34

Похоже, у вас есть классические отношения OneToMany между вашими объектами. Для этого есть много документации. Попробуйте здесь для запуска.

ehymel 01.09.2018 14:13

Можете ли вы добавить аннотации доктрины для связи между этими сущностями?

Jannes Botis 03.09.2018 23:25
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
11
6
1 445
4

Ответы 4

Анализируя ваши таблицы БД, я предполагаю, что ваши Сущности такие

// User.php

class User implements UserInterface
{
    /**
     * @ORM\Column(type = "guid")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy = "UUID")
     */
    private $id;

    /**
     * @ORM\Column(type = "string", length=100)
     */
    private $username;
}

// Language.php

class Language
{

    /**
     * @ORM\Column(type = "guid")
     */
    private $userId;

    /**
     * @ORM\Column(type = "string", length=30)
     */
    private $language;
}

Если у вас такая же настройка (как и выше Entities), вы можете написать свой запрос, подобный этому, в UserRepository.php

public function getUsersForMatchingLanguages ($langOne, $langTwo) {
    return $this->createQueryBuilder('user')
        ->select('user.id, user.username, language.language')
        ->innerJoin(Language::class, 'language', 'WITH', 'language.user_id = user.id')
        ->where('language.language = :langOne AND language.language = :langTwo')
        ->setParameter('langOne ', $langOne )
        ->setParameter('langTwo', $langTwo)
        ->getQuery()
        ->getResult();
}

Это вернет вам массив результатов.

Как правило, не нужно «думать», что ответ правильный. Вы должны это знать. Возможно путем тестирования перед публикацией. Ваше соединение полностью испорчено. И даже если вы исправите это, использование предложения IN покажет, что вы не поняли исходный вопрос.

Cerad 29.08.2018 16:39

Думаю, это не так просто, как кажется в конце концов, потому что я получаю ошибку в вашем коде. Я десятки раз пытался понять и исправить проблему, но всегда возникала такая ошибка: [Semantical Error] line 0, col 21 near 'username, language.language': Error: Class App\Entity\Language has no field or association named username

hidar 30.08.2018 22:46

@Cerad Вы могли бы просто вставить комментарий с ответом, и вы бы помогли мне, вместо того, чтобы комментировать, что правильно и что неправильно

hidar 30.08.2018 22:47

@hanish, если вы блуждаете, я пошел дальше и добавил поле public $username в App\Entity\Languge (что, я не уверен, должно быть сделано), и он все еще выдает ошибку SQLSTATE[42S22]: Column not found: 1054 Unknown column 'u0_.username' in 'field list'

hidar 30.08.2018 22:49

Я предполагаю, что вы поместили мою функцию в репозиторий языков. Вместо этого переместите его в репозиторий пользователей, так как у вас есть столбец «имя пользователя» в сущности «Пользователь», а не на языке. Я также предлагаю вам вставить свои объекты (пользователя и язык), чтобы дать нам больше информации. Ваши объекты и таблица должны быть синхронизированы (иметь одинаковые столбцы).

hanish singla 31.08.2018 07:08

Нет, к сожалению, я работаю над этим проектом после того, как вернусь с работы, я попробовал ваше решение и вскоре сдался, все еще нет рабочего решения, я жду новых ответов

hidar 02.09.2018 18:23

@hidar ... Интересно. Я добавил к своему ответу еще несколько деталей. Если он по-прежнему не работает, можете ли вы указать свои сущности (пользователя и язык) в своем вопросе, чтобы мы могли лучше понять ваш запрос?

hanish singla 02.09.2018 18:59

Возможно, я не понимаю вопрос правильно, пожалуйста, поправьте меня, если я ошибаюсь, но если вам нужны пользователи, которые говорят на языках ОБА, у вас есть ошибка в логике SQL, а не в доктрине. Вам следует делать что-то вроде:

SELECT * FROM user u JOIN language l ON u.id = l.user_id AND l.language = 'english' JOIN language l2 ON u.id = l2.user_id AND l2.language = 'french' GROUP BY u.id;

Если запрос вам подходит, я могу написать для него DQL-интерпретацию.

Я скоро это проверю. Могу ли я создать запрос с помощью createQueryBuilder ()?

hidar 02.09.2018 18:32

Пожалуйста, проверьте мой другой ответ ниже с подробным объяснением моей идеи.

Igor Skobelev 02.09.2018 23:01

В вашей модели пользователя добавьте следующий код:

/**
 * @ORM\OneToMany(targetEntity = "Language", mappedBy = "user", fetch = "EXTRA_LAZY")
 */
public $languages;

В вашей языковой модели добавьте следующий код:

/**
 * @ORM\ManyToOne(targetEntity = "User", inversedBy = "languages")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name = "user_id", referencedColumnName = "id")
 * })
 */
public $user;

Таким образом вы определяете простую связь «один ко многим» между пользователем и языком, но этого недостаточно для того, чтобы ваш пользователь поддерживал оба языка. Вам нужно сделать 2 соединения пользовательской таблицы и языковой таблицы. Вот как это выглядит (если вы используете контроллер):

  $user = $this->get('doctrine')
        ->getEntityManager()
        ->createQueryBuilder()
        ->select('u')
        ->from(User::class, 'u')
        ->join('u.languages', 'l_eng', 'WITH', 'l_eng.language = :engCode')
        ->join('u.languages', 'l_fr', 'WITH', 'l_fr.language = :frCode')
        ->setParameters([
            'engCode' => 'english',
            'frCode' => 'french'
        ])
        ->getQuery()->execute();

Или, если вы используете класс UserRepository (наиболее предпочтительно):

public function findAllByLangs()
{
    return $this->createQueryBuilder('u')
        ->join('u.languages', 'l_eng', 'WITH', 'l_eng.lang = :engCode')
        ->join('u.languages', 'l_fr', 'WITH', 'l_fr.lang = :frCode')
        ->setParameters([
            'engCode' => 'english',
            'frCode' => 'french'
        ])
        ->getQuery()->execute();
}

Таким образом, основная хитрость заключается в объединении языковой таблицы с условием английского языка для фильтрации пользователей, которые поддерживают английский язык И, снова присоединяются к языковой таблице, но с французским в разделе «ВКЛ» для фильтрации пользователей, которые поддерживают французский язык также.

@hidar, пожалуйста, представьте, это рабочее решение для вашего случая?

Igor Skobelev 03.09.2018 08:04

Ставить на контроллер не рекомендую. Это нарушит смысл классов репозитория и сделает ваш код более раздутым и трудным для понимания.

Roberto Maldonado 03.09.2018 08:27

@RobertoMaldonado полностью согласен. Лучше разместить в репозитории или отдельном сервисе, но главный вопрос не в этом.

Igor Skobelev 03.09.2018 10:36

Ты можешь:

  • Внутреннее соединение с языками, которые вы хотите
  • используйте агрегатные функции для считать объединенных результатов (соединенные языки)
  • группа по субъекту пользователя
  • отфильтровать результаты для count (lang) = 2

Код:

use Doctrine\ORM\Query\Expr\Join;

public function getMatchingLanguages ($a, $b) {
    return $this->createQueryBuilder('u')
                ->addSelect('COUNT(a) as HIDDEN total')
                ->innerJoin('u.languages', 'a', Join::WITH, 'a.language = :val1 OR a.language = :val2')
        ->groupBy('u');
                 ->having('total = :total') //or ->having('COUNT(a) = :total')
        ->setParameter('total', 2)
        ->setParameter('val1', $a)
        ->setParameter('val2', $b)
        ->getQuery()
        ->execute();
}

$this->getMatchingLanguages('english', 'french');

Это работает путем внутреннего присоединения пользователя только к строкам с английским или французским языком, а затем с помощью mysql, имеющий, чтобы «проверить», получили ли мы 2 строки для каждого пользователя.

Если вы хотите, чтобы сущности языков были гидратированы в ваш результат, вы не можете добавить его к результату построителя запросов, так как вы группируете по:

-> addSelect ('а')

вам нужно будет сделать еще один запрос.

Другие вопросы по теме