Параметр fetch атрибута OneToMany
Параметр fetch атрибута OneToMany
определяет стратегию загрузки связанных
сущностей из базы данных. Первым значением
передаётся константа из класса Doctrine\ORM\Mapping\ClassMetadata.
Доступны значения LAZY, EAGER и EXTRA_LAZY.
По умолчанию используется LAZY.
Синтаксис
#[OneToMany(
mappedBy: property,
targetEntity: Entity::class,
fetch: 'LAZY'
)]
Пример
Давайте создадим сущность Category со связью
OneToMany на сущность Article
со стратегией загрузки LAZY:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Category
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $title = null;
#[ORM\OneToMany(
mappedBy: 'category',
targetEntity: Article::class,
fetch: 'LAZY'
)]
private Collection $articles;
public function __construct()
{
$this->articles = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): static
{
$this->title = $title;
return $this;
}
public function getArticles(): Collection
{
return $this->articles;
}
}
?>
Теперь создадим сущность Article
с обратной связью ManyToOne:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Article
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $title = null;
#[ORM\ManyToOne(
inversedBy: 'articles',
targetEntity: Category::class
)]
#[ORM\JoinColumn(nullable: false)]
private ?Category $category = null;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): static
{
$this->title = $title;
return $this;
}
public function getCategory(): ?Category
{
return $this->category;
}
public function setCategory(?Category $category): static
{
$this->category = $category;
return $this;
}
}
?>
При стратегии LAZY связанные статьи
загружаются из базы данных только в момент
первого обращения к коллекции:
<?php
namespace App\Controller;
use App\Entity\Category;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ArticleController extends AbstractController
{
#[Route('/category/{id}', name: 'category_show')]
public function show(int $id, EntityManagerInterface $em): Response
{
$category = $em->getRepository(Category::class)->find($id);
$count = $category->getArticles()->count();
return new Response('Count: ' . $count);
}
}
?>
Результат выполнения кода для категории с тремя статьями:
"Count: 3"
Пример
Давайте изменим стратегию загрузки на EAGER,
чтобы связанные статьи загружались сразу
вместе с категорией:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Category
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $title = null;
#[ORM\OneToMany(
mappedBy: 'category',
targetEntity: Article::class,
fetch: 'EAGER'
)]
private Collection $articles;
public function __construct()
{
$this->articles = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): static
{
$this->title = $title;
return $this;
}
public function getArticles(): Collection
{
return $this->articles;
}
}
?>
Теперь при загрузке категории все связанные статьи будут загружены немедленно одним дополнительным запросом:
<?php
namespace App\Controller;
use App\Entity\Category;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ArticleController extends AbstractController
{
#[Route('/category/{id}', name: 'category_show')]
public function show(int $id, EntityManagerInterface $em): Response
{
$category = $em->getRepository(Category::class)->find($id);
$titles = [];
foreach ($category->getArticles() as $article) {
$titles[] = $article->getTitle();
}
return new Response(implode(', ', $titles));
}
}
?>
Результат выполнения кода:
"article 1, article 2, article 3"
Пример
Давайте используем стратегию EXTRA_LAZY,
которая позволяет выполнять операцию count
без загрузки всей коллекции в память:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Category
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $title = null;
#[ORM\OneToMany(
mappedBy: 'category',
targetEntity: Article::class,
fetch: 'EXTRA_LAZY'
)]
private Collection $articles;
public function __construct()
{
$this->articles = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): static
{
$this->title = $title;
return $this;
}
public function getArticles(): Collection
{
return $this->articles;
}
}
?>
При вызове count на коллекции
с стратегией EXTRA_LAZY Doctrine
выполнит отдельный SQL-запрос COUNT,
не загружая сами сущности:
<?php
namespace App\Controller;
use App\Entity\Category;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ArticleController extends AbstractController
{
#[Route('/category/{id}/count', name: 'category_count')]
public function count(int $id, EntityManagerInterface $em): Response
{
$category = $em->getRepository(Category::class)->find($id);
$count = $category->getArticles()->count();
return new Response('Total: ' . $count);
}
}
?>
Результат выполнения кода:
"Total: 3"
Смотрите также
-
атрибут
OneToMany,
который создаёт связь один-ко-многим -
параметр
mappedBy,
который указывает обратное поле связи -
параметр
cascade,
который задаёт каскадные операции -
параметр
orphanRemoval,
который удаляет осиротевшие сущности