如何使用doctrine / symfony4从数据库中获取(合并)两个记录

时间:2018-08-28 23:51:02

标签: php mysql symfony doctrine-orm symfony4

我正在学习有关Symfony和Doctrine的知识,并创建了一个简单的网站,但在这一步中我陷入了困境。

我有两个表:userslanguages

用户包含:ID,用户名...
语言,包含:user_id,语言...

这是两者的图片enter image description here

enter image description here

现在,我正在尝试按语言获取信息,例如:获取同时说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();
}

我在控制器中调用此方法,并且查询非常基本,因为找不到根据我的示例进行连接的文档

4 个答案:

答案 0 :(得分:6)

在您的用户模型中添加下一个代码:

$post = Post::find(1); dd($post->images)

在您的语言模型中添加下一个代码:

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

通过这种方式,您可以在用户和语言之间定义简单的一对多关系,但这不足以让您的用户支持两种语言。您需要将用户表和语言表进行2次连接。 就是这样(如果使用控制器):

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

或者,如果您使用UserRepository类(最好):

  $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();

因此,主要技巧是加入具有英语条件的语言表以过滤支持英语的用户 AND 再次加入语言表,但在“ ON”部分中添加法语以过滤支持法语的用户<也是

答案 1 :(得分: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;
}

如果您具有相同的设置(与上述实体相同),则可以在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();
}

这将返回结果数组。

答案 2 :(得分:0)

也许我对问题的理解不正确,如果我错了,请纠正我,但是如果您需要使用两种语言的用户,则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解释。

答案 3 :(得分:0)

您可以:

  • 内部加入您想要的语言
  • 使用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实体添加到结果中,则不能将其添加到querybuilder结果中,因为按以下条件进行了分组

-> addSelect('a')

您将不得不再次进行查询。