negromovich/doctrine-relations-loader

用于 Doctrine 懒加载关系的库

1.0.0 2019-06-10 14:36 UTC

This package is auto-updated.

Last update: 2024-09-06 23:26:04 UTC


README

该库是一个单类,允许以最佳方式和性能批量加载许多相关实体。

使用 Doctrine UnitOfWork 的 identityMap 进行加载。因此,UnitOfWork 中的 "clear" 方法会中断加载。

用法

例如使用具有用户、帖子、评论和点赞的博客结构。

/** @ORM\Entity() */
class User
{
    /**
     * @ORM\Id()
     * @ORM\Column(type="integer")
     */
    public $id;

    /** @ORM\Column(type="string") */
    public $name;
    
    /** @ORM\Column(type="string") */
    public $country;

    /** @ORM\OneToMany(targetEntity="Post", mappedBy="user") */
    public $posts;
}

/** @ORM\Entity() */
class Post
{
    /**
     * @ORM\Id()
     * @ORM\Column(type="integer")
     */
    public $id;

    /** @ORM\Column(type="string") */
    public $title;

    /** @ORM\Column(type="string") */
    public $content;

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

    /** @ORM\OneToMany(targetEntity="Comment", mappedBy="post") */
    public $comments;

    /** @ORM\OneToMany(targetEntity="Like", mappedBy="post") */
    public $likes;
}

/** @ORM\Entity() */
class Comment
{
    /**
     * @ORM\Id()
     * @ORM\Column(type="integer")
     */
    public $id;

    /** @ORM\Column(type="string") */
    public $content;

    /**
     * @ORM\ManyToOne(targetEntity="Post")
     * @ORM\JoinColumn(name="post_id", referencedColumnName="id")
     */
    public $post;

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

/**
 * @ORM\Entity()
 * @ORM\Table(name="`like`")
 */
class Like
{
    /**
     * @ORM\Id()
     * @ORM\Column(type="integer")
     */
    public $id;

    /**
     * @ORM\ManyToOne(targetEntity="Post")
     * @ORM\JoinColumn(name="post_id", referencedColumnName="id")
     */
    public $post;

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

现在我们统计特定用户点赞来自哪些国家(仅为例子,最好在数据库中进行计算)。

$countries = [];
$user = $this->em->getRepository(User::class)->find(1);
foreach ($user->posts as $post) {
    foreach ($post->likes as $like) {
        $countries[$like->user->country] = 1;
    }
}
$num = count($countries);

默认情况下,将执行到数据库的查询数 = 帖子数 + 用户数 + 2

SELECT * FROM user WHERE id = 1;
SELECT * FROM post WHERE user_id = 1;
SELECT * FROM `like` WHERE post_id = ?; # for each post
SELECT * FROM user WHERE id = ?; # for unique user

查询数量太大。我想合并相同表中的相同查询

SELECT * FROM `like` WHERE post_id IN (?); # for all post
SELECT * FROM user WHERE id IN (?); # for all unique users

可以使用预加载,但这不适用于大量级联实体。另一种方法是使用该库中的 RelationsLoader。

$countries = [];
$user = $this->em->getRepository(User::class)->find(1);

$relationsLoader = new RelationsLoader($this->em);
$relationsLoader->load($user, ['posts' => ['likes' => ['user']]]);

foreach ($user->posts as $post) {
    foreach ($post->likes as $like) {
        $countries[$like->user->country] = 1;
    }
}
$num = count($countries);

在这种情况下,将执行 4 次数据库查询。

语法

RelationsLoader::load 中,您必须传递您想要加载关系的哪些数据(实体、实体数组、集合)以及要加载的关系的层次结构列表。层次结构列表是如何形成的,请考虑以下示例。

  • 为用户加载帖子(类似于 doctrine 中的预加载)

      $relationsLoader->load($users, ['posts']);
    
  • 为帖子加载帖子及点赞

      $relationsLoader->load($users, [
          'posts' => ['likes']
      ]);
    
  • 为帖子加载帖子、评论及点赞

      $relationsLoader->load($users, [
          'posts' => [
              'comments',
              'likes'
          ]
      ]);
    
  • 为帖子、评论及点赞加载帖子,为评论加载用户

      $relationsLoader->load($users, [
          'posts' => [
              'comments' => ['user'],
              'likes'
          ]
      ]);
    
  • 为帖子、评论及点赞加载帖子,为评论和点赞加载用户

      $relationsLoader->load($users, [
          'posts' => [
              'comments' => ['user'],
              'likes' => ['user']
          ]
      ]);