如何使用教义/symfony4 从数据库中获取(连接)两条记录
Posted
技术标签:
【中文标题】如何使用教义/symfony4 从数据库中获取(连接)两条记录【英文标题】:How to fetch (join) two records from database using doctrine/symfony4 【发布时间】:2019-02-03 15:54:50 【问题描述】:我正在学习 Symfony 和 Doctrine 并创建了一个简单的网站,但我被困在这一步。
我有两张桌子:users
和 languages
用户包含:id、用户名...语言包含:user_id、语言...
这是两个的图片
现在我正在尝试按语言获取,例如:获取同时说 english
和 french
的用户,结果将返回用户 ID 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
提供了任何更奇特的方法。
花哨是什么意思?我只是问,因为代码没有按预期工作。它不会返回用户的语言
不要采取错误的方式,但您确实需要更加努力地查看文档。很多例子。更不用说软问题了。
我明白,没问题。实际上,通过阅读 Symfony 上的文档,我设法创建了一个带有登录系统的博客站点,但学说有点难以理解。由于某种原因,我觉得它很复杂。
看起来您的实体之间具有经典的 OneToMany 关系。这方面有很多文档。尝试here 开始。
【参考方案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();
所以主要技巧是加入带有英语条件的语言表来过滤支持英语的用户AND再次加入语言表,但在“ON”部分使用法语来过滤支持法语的用户以及。
【讨论】:
@hidar 请提供,它是否适合您的案例? 我不建议把它放在控制器上。这会破坏存储库类的含义,并使您的代码更加臃肿且难以理解。 @RobertoMaldonado 完全同意。最好放在存储库或单独的服务中,但主要问题不在于它。【参考方案2】:通过分析您的数据库表,我假设您的实体是这样的
// 用户.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;
// 语言.php
class Language
/**
* @ORM\Column(type="guid")
*/
private $userId;
/**
* @ORM\Column(type="string", length=30)
*/
private $language;
如果您有相同的设置(如上面的实体),那么您可以在 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'
错误
我假设您已将我的功能放入语言存储库。请改为将其移至用户存储库,因为您在用户实体中有“用户名”列,而不是在语言中。我还建议您粘贴您的实体(用户和语言),以便我们更深入地了解。您的实体和表必须同步(具有相同的列)。【参考方案3】:
也许我没有正确理解问题,如果我错了,请纠正我,但如果您需要会说 BOTH 语言的用户,则您的 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()”创建查询? 请在下面查看我的另一个答案,详细解释我的想法。【参考方案4】:你可以:
内部加入您想要的语言 使用aggregate functions计数加入的结果(加入的语言) 按用户实体分组 过滤 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 having“检查”我们是否为每个用户获得 2 行。
如果您还希望将 Languages 实体添加到结果中,则不能将其添加到查询构建器结果中,因为您按以下方式分组:
->addSelect('a')
您将不得不进行另一个查询。
【讨论】:
以上是关于如何使用教义/symfony4 从数据库中获取(连接)两条记录的主要内容,如果未能解决你的问题,请参考以下文章
Symfony 4 |教义 |驱动程序发生异常:SQLSTATE[HY000] [2002] Connection denied