Сохранение связанных сущностей в Symfony 5.
У вас в проекте написанном на фреймворке Symfony, имеются связные между собой Entity, которые с помощью Doctrine можно сохранить в БД в связанных таблицах. Например у вас могут быть отношения Многие к Одному (Many To One). Сделать это очень легко.
Для примера код ниже представлен внутри контроллера:
// ...
use App\Entity\Category;
use App\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
class ProductController extends AbstractController
{
/**
* @Route("/product", name="product")
*/
public function index()
{
$category = new Category();
$category->setName('Computer Peripherals');
$product = new Product();
$product->setName('Keyboard');
$product->setPrice(19.99);
$product->setDescription('Ergonomic and stylish!');
// relates this product to the category
$product->setCategory($category);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($category);
$entityManager->persist($product);
$entityManager->flush();
return new Response(
'Saved new product with id: '.$product->getId()
.' and new category with id: '.$category->getId()
);
}
}
В примере кода, выше, когда вы переходите к роуту в приложении /product, в таблицы категорий и продуктов добавляется одна строка. Столбцу product.category_id для нового продукта присваивается любой идентификатор новой категории. Доктрина управляет сохранением этих отношений для вас:
Если вы новичок с работой ORM Doctrine, то это самая сложная концепция: вам нужно перестать думать о своей базе данных, а вместо этого думать только о своих объектах. Вместо установки целочисленного идентификатора категории в Product, вы устанавливаете весь объект Category. Доктрина позаботится об остальном при сохранении.
Сохранение значений для связи Many To One.
В примере кода выше были обновлены данные в таблицах между связными сущностями, вызвав $product->setCategory($category). При этом каждое отношение имеет две стороны: в этом примере Product.category является владельцем, а Category.products является обратной стороной.
Чтобы обновить отношения в базе данных, вы должны установить отношения на стороне владельца. Сторона-владелец всегда находится там, где установлено отображение ManyToOne (для отношения ManyToMany вы можете выбрать, какая сторона является стороной-владельцем).
В классе сущности, с помощью аннотации, сторона-владелец будет выглядеть, например так:
class Product
{
// ...
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Category", inversedBy="products")
*/
private $category;
public function getCategory(): ?Category
{
return $this->category;
}
public function setCategory(?Category $category): self
{
$this->category = $category;
return $this;
}
}
Означает ли это, что невозможно обновить $category->addProduct() или $category->removeProduct() для обновления базы данных? На самом деле, это возможно благодаря некоторому умному коду, который был сгенерирован командой bin/console make:entity:
// src/Entity/Category.php
// ...
class Category
{
// ...
public function removeProduct(Product $product): self
{
if ($this->products->contains($product)) {
$this->products->removeElement($product);
// set the owning side to null (unless already changed)
if ($product->getCategory() === $this) {
$product->setCategory(null);
}
}
return $this;
}
}
Благодаря этому, если вы вызовете $category->removeProduct($product) , для category_id этого продукта будет установлено значение null в базе данных.
Но вместо того, чтобы установить для category_id значение null, что, если вы хотите, чтобы Продукт был удален, если он становится «осиротевшим» (т.е. без категории)? Чтобы выбрать это поведение, используйте параметр orphanRemoval внутри категории:
// src/Entity/Category.php
// ...
/**
* @ORM\OneToMany(targetEntity="App\Entity\Product", mappedBy="category", orphanRemoval=true)
*/
private $products;
Благодаря указанию orphanRemoval=true в аннотации, теперь если продукт будет удален из категории, он будет полностью удален из базы данных.