negromovich / doctrine-relations-loader
用于 Doctrine 懒加载关系的库
1.0.0
2019-06-10 14:36 UTC
Requires
- php: ^5.5|^7.0
Requires (Dev)
- doctrine/orm: ^2.5
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'] ] ]);