如何使用教义/symfony4 从数据库中获取(连接)两条记录

Posted

技术标签:

【中文标题】如何使用教义/symfony4 从数据库中获取(连接)两条记录【英文标题】:How to fetch (join) two records from database using doctrine/symfony4 【发布时间】:2019-02-03 15:54:50 【问题描述】:

我正在学习 Symfony 和 Doctrine 并创建了一个简单的网站,但我被困在这一步。

我有两张桌子:userslanguages

用户包含: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

使用教义插入查询

教义 2 - 从 $em->find() 获取 SQL

Symfony4如何使用查询构建器获取对象

Symfony 4,从控制器获取 .env 参数,是不是可能以及如何?

如何在 symfony WebTestCase 的测试中按夹具类型获取教义夹具参考?