Я изучаю Symfony и Doctrine и создал простой сайт, но застрял на этом этапе.
У меня две таблицы: users и languages
Пользователи Содержит: id, имя пользователя ...
Языки Содержит: 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();
}
Я вызываю этот метод в своих контроллерах, и запрос довольно простой, так как я не могу найти документацию, как выполнять соединения в соответствии с моим примером.
Что ты имеешь в виду? Я просто спрашиваю, потому что код работает не так, как задумано. Не возвращает пользователя для языков
Не поймите это неправильно, но вам действительно нужно немного внимательнее изучить документацию. Много примеров. Не говоря уже о мягких вопросах.
Я понимаю, без проблем. Мне действительно удалось создать блог-сайт с системой входа в систему, прочитав документацию по Symfony, но доктрину немного сложнее осмыслить. Мне это почему-то кажется сложным.
Похоже, у вас есть классические отношения OneToMany между вашими объектами. Для этого есть много документации. Попробуйте здесь для запуска.
Можете ли вы добавить аннотации доктрины для связи между этими сущностями?






Анализируя ваши таблицы БД, я предполагаю, что ваши Сущности такие
// 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 покажет, что вы не поняли исходный вопрос.
Думаю, это не так просто, как кажется в конце концов, потому что я получаю ошибку в вашем коде. Я десятки раз пытался понять и исправить проблему, но всегда возникала такая ошибка: [Semantical Error] line 0, col 21 near 'username, language.language': Error: Class App\Entity\Language has no field or association named username
@Cerad Вы могли бы просто вставить комментарий с ответом, и вы бы помогли мне, вместо того, чтобы комментировать, что правильно и что неправильно
@hanish, если вы блуждаете, я пошел дальше и добавил поле public $username в App\Entity\Languge (что, я не уверен, должно быть сделано), и он все еще выдает ошибку SQLSTATE[42S22]: Column not found: 1054 Unknown column 'u0_.username' in 'field list'
Я предполагаю, что вы поместили мою функцию в репозиторий языков. Вместо этого переместите его в репозиторий пользователей, так как у вас есть столбец «имя пользователя» в сущности «Пользователь», а не на языке. Я также предлагаю вам вставить свои объекты (пользователя и язык), чтобы дать нам больше информации. Ваши объекты и таблица должны быть синхронизированы (иметь одинаковые столбцы).
Нет, к сожалению, я работаю над этим проектом после того, как вернусь с работы, я попробовал ваше решение и вскоре сдался, все еще нет рабочего решения, я жду новых ответов
@hidar ... Интересно. Я добавил к своему ответу еще несколько деталей. Если он по-прежнему не работает, можете ли вы указать свои сущности (пользователя и язык) в своем вопросе, чтобы мы могли лучше понять ваш запрос?
Возможно, я не понимаю вопрос правильно, пожалуйста, поправьте меня, если я ошибаюсь, но если вам нужны пользователи, которые говорят на языках ОБА, у вас есть ошибка в логике 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 ()?
Пожалуйста, проверьте мой другой ответ ниже с подробным объяснением моей идеи.
В вашей модели пользователя добавьте следующий код:
/**
* @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, пожалуйста, представьте, это рабочее решение для вашего случая?
Ставить на контроллер не рекомендую. Это нарушит смысл классов репозитория и сделает ваш код более раздутым и трудным для понимания.
@RobertoMaldonado полностью согласен. Лучше разместить в репозитории или отдельном сервисе, но главный вопрос не в этом.
Ты можешь:
Код:
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 ('а')
вам нужно будет сделать еще один запрос.
Я не думаю, что
Doctrineпредоставляет более изящный способ сделать это.