Атрибут OneToMany
Атрибут OneToMany вешается на свойство сущности и указывает, что она связана с коллекцией других сущностей. Первым параметром передаётся имя свойства целевой сущности (через targetEntity или mappedBy). Связь является обратной стороной для ManyToOne.
Синтаксис
#[OneToMany(mappedBy: 'property', targetEntity: Entity::class, cascade: ['persist'], orphanRemoval: true)]
Пример
Давайте создадим сущность Category, у которой есть коллекция статей Article:
<?php
namespace AppEntity;
use AppRepositoryCategoryRepository;
use DoctrineCommonCollectionsArrayCollection;
use DoctrineCommonCollectionsCollection;
use DoctrineORMMapping as ORM;
#[ORMEntity(repositoryClass: CategoryRepository::class)]
class Category
{
#[ORMId]
#[ORMGeneratedValue]
#[ORMColumn]
private ?int $id = null;
#[ORMColumn(length: 255)]
private ?string $name = null;
#[ORMOneToMany(mappedBy: 'category', targetEntity: Article::class)]
private Collection $articles;
public function __construct()
{
$this->articles = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): static
{
$this->name = $name;
return $this;
}
public function getArticles(): Collection
{
return $this->articles;
}
public function addArticle(Article $article): static
{
if (!$this->articles->contains($article)) {
$this->articles->add($article);
$article->setCategory($this);
}
return $this;
}
public function removeArticle(Article $article): static
{
if ($this->articles->removeElement($article)) {
if ($article->getCategory() === $this) {
$article->setCategory(null);
}
}
return $this;
}
}
?>
Теперь создадим сущность Article, которая будет владельцем связи через ManyToOne:
<?php
namespace AppEntity;
use AppRepositoryArticleRepository;
use DoctrineORMMapping as ORM;
#[ORMEntity(repositoryClass: ArticleRepository::class)]
class Article
{
#[ORMId]
#[ORMGeneratedValue]
#[ORMColumn]
private ?int $id = null;
#[ORMColumn(length: 255)]
private ?string $title = null;
#[ORMManyToOne(inversedBy: 'articles')]
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;
}
}
?>
Результат выполнения кода (структура базы данных):
CREATE TABLE category (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id));
CREATE TABLE article (id INT AUTO_INCREMENT NOT NULL, title VARCHAR(255) NOT NULL, category_id INT DEFAULT NULL, INDEX IDX_ARTICLE_CATEGORY (category_id), PRIMARY KEY(id));
ALTER TABLE article ADD CONSTRAINT FK_ARTICLE_CATEGORY FOREIGN KEY (category_id) REFERENCES category (id);
Пример
Давайте добавим статью в категорию и выведем список заголовков:
<?php
namespace AppController;
use AppEntityArticle;
use AppEntityCategory;
use DoctrineORMEntityManagerInterface;
use SymfonyBundleFrameworkBundleControllerAbstractController;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentRoutingAttributeRoute;
class ArticleController extends AbstractController
{
#[Route('/article', name: 'article_index')]
public function index(EntityManagerInterface $entityManager): Response
{
$category = new Category();
$category->setName('abcde');
$article1 = new Article();
$article1->setTitle('article 1');
$article1->setCategory($category);
$article2 = new Article();
$article2->setTitle('article 2');
$article2->setCategory($category);
$entityManager->persist($category);
$entityManager->persist($article1);
$entityManager->persist($article2);
$entityManager->flush();
$titles = [];
foreach ($category->getArticles() as $article) {
$titles[] = $article->getTitle();
}
return new Response(json_encode($titles));
}
}
?>
Результат выполнения кода:
["article 1", "article 2"]