Как получить (объединить) две записи из базы данных с помощью doctrine/symfony4
Я изучаю Symfony и Doctrine и создал простой сайт, но я застрял на этом шаге.
У меня две таблицы: users
и languages
Пользователи Содержит: id, username...
Языки Содержит: user_id, язык...
Вот изображение двух
Теперь я пытаюсь извлечь язык, например: получить пользователя, который говорит как на 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();
}
Я вызываю этот метод в своих контроллерах, и запрос довольно простой, так как я не могу найти документацию о том, как делать соединения в соответствии с моим примером
Ответы
Ответ 1
В вашей модели пользователя добавьте следующий код:
/**
* @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();
}
Поэтому основной фокус в том, присоединиться к языковой таблице с условием английского для фильтрации пользователей, которые поддерживают английский язык и снова, но с французами в присоединиться к языковому столу "ON" раздел для фильтрации пользователей, которые поддерживают французский язык, а также.
Ответ 2
Анализируя таблицы БД, я предполагаю, что ваши Сущности подобны этому
//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();
}
Это вернет вам массив результатов.
Ответ 3
Может быть, я не понимаю вопроса правильно, пожалуйста, исправьте меня, если я ошибаюсь, но если вам нужны пользователи, которые говорят на двух языках, у вас есть ошибка в логике 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.
Ответ 4
Вы можете:
- Внутреннее соединение с языками, которые вы хотите
- использовать агрегированные функции для подсчета объединенных результатов (объединенные языки)
- группы субъектом пользователя
- фильтровать результаты для 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 строки для каждого пользователя.
Если вы хотите, чтобы объекты "Язык" гидратировались по вашему результату, вы не можете добавить его к результату querybuilder, поскольку вы группируете:
-> addSelect ( 'а')
вам нужно будет сделать еще один запрос.