gpt4 book ai didi

php - 使用 Doctrine 删除与 Symfony 3 的 3 实体(一对多对一)关联

转载 作者:行者123 更新时间:2023-11-29 18:57:48 24 4
gpt4 key购买 nike

这是我的第一个问题!

我有两个想要关联的实体:ProductCategory。一个产品可能有多个类别,一个类别可能对应多个产品。我决定将此关系实现为 3 类关联,具有中间 ProductCategory 实体,如下图所示。这使我能够灵活地在将来向关联添加属性。

Representation of my tree-class association

我想将现有类别分配给现有产品。 我想在实体本身内部建立关系。我可以在 Product 实体中执行此操作,使用接收 Category 实体数组的 setter 方法,并创建一个新的 ProductCategory 实体对于每个通过的类别。流程如下:

//Product.php

/**
* @param \Doctrine\Common\Collections\ArrayCollection $categories
* @return \TestBundle\Entity\Product
*/
public function setCategories($categories) {
$productCategoryReplacement = new \Doctrine\Common\Collections\ArrayCollection();
foreach ($categories as $category) {
$newProductCategory = new ProductCategory();
$newProductCategory->setProduct($this);
$newProductCategory->setCategory($category);
$productCategoryReplacement[] = $newProductCategory;
}
$this->productCategory = $productCategoryReplacement;

return $this;
}

请注意,我在添加新集合之前清除了 ProductCategory 集合;这样,只有在表单中选择的类别才会保存到数据库中。

我的问题是 Doctrine 在插入新记录之前不会从数据库中删除记录。当没有为产品分配任何类别时,这很好,但在尝试更新关联时,我收到完整性约束违规:1062 键“PRIMARY”的重复条目“1-1”。我检查了 Symfony 调试面板的 Doctrine 部分,并且在 INSERT 之前没有执行任何 DELETE 语句。

是否可以从实体内删除相关实体?如果没有,那为什么可以添加新的呢?提前致谢。

<小时/>

我的实体如下:

产品.php:

namespace TestBundle\Entity;

/**
* @ORM\Table(name="product")
* @ORM\Entity(repositoryClass="TestBundle\Repository\ProductRepository")
*/
class Product {

/**
* @var int
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

/**
* @var string
* @ORM\Column(name="name", type="string", length=255)
*/
private $name;

/**
* @var \Doctrine\Common\Collections\ArrayCollection
* @ORM\OneToMany(targetEntity="ProductCategory", mappedBy="product", cascade={"persist"})
*/
private $productCategory;

/**
* Constructor
*/
public function __construct() {
$this->productCategory = new \Doctrine\Common\Collections\ArrayCollection();
}

/**
* @param \TestBundle\Entity\ProductCategory $productCategory
* @return Product
*/
public function addProductCategory(\TestBundle\Entity\ProductCategory $productCategory) {
$this->productCategory[] = $productCategory;
return $this;
}

/**
* @param \TestBundle\Entity\ProductCategory $productCategory
*/
public function removeProductCategory(\TestBundle\Entity\ProductCategory $productCategory) {
$this->productCategory->removeElement($productCategory);
}

/**
* @return \Doctrine\Common\Collections\Collection
*/
public function getProductCategory() {
return $this->productCategory;
}
/**
* @param \Doctrine\Common\Collections\ArrayCollection $categories
* @return \TestBundle\Entity\Product
*/
public function setCategories($categories) {
$productCategoryReplacement = new \Doctrine\Common\Collections\ArrayCollection();
foreach ($categories as $category) {
$newProductCategory = new ProductCategory();
$newProductCategory->setProduct($this);
$newProductCategory->setCategory($category);
$productCategoryReplacement[] = $newProductCategory;
}
$this->productCategory = $productCategoryReplacement;

return $this;
}

/**
* @return \Doctrine\Common\Collections\ArrayCollection
*/
public function getCategories() {
$categories = new \Doctrine\Common\Collections\ArrayCollection();
foreach ($this->getProductCategory() as $pc) {
$categories[] = $pc->getCategory();
}
return $categories;
}
}

类别.php:

namespace TestBundle\Entity;

/**
* @ORM\Table(name="category")
* @ORM\Entity(repositoryClass="TestBundle\Repository\CategoryRepository")
*/
class Category {
/**
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

/**
* @ORM\Column(name="name", type="string", length=255)
*/
private $name;

/**
* @var \Doctrine\Common\Collections\ArrayCollection
* @ORM\OneToMany(targetEntity="ProductCategory", mappedBy="category", cascade={"persist"})
*/
private $productCategory;
}

产品类别.php

namespace TestBundle\Entity;

/**
* @ORM\Table(name="product_category")
* @ORM\Entity(repositoryClass="TestBundle\Repository\ProductCategoryRepository")
*/
class ProductCategory {

/**
* @ORM\Id
* @ORM\ManyToOne(targetEntity="Product", inversedBy="productCategory")
* @ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
private $product;

/**
* @ORM\Id
* @ORM\ManyToOne(targetEntity="Category", inversedBy="productCategory")
* @ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
}

我的产品表单生成如下:

public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name')
->add('categories', EntityType::class, array(
'class' => 'TestBundle:Category',
'choice_label' => 'name',
'expanded' => true,
'multiple' => true,
));
}

请注意,我使用 categories 字段名称,该字段名称将填充取自 Category 实体的类别。该表单返回一个 Category 对象数组,我用它来在 Product.php< 内的 setCategories() 方法中生成 ProductCategory 实体。/.

/**
* @param \Doctrine\Common\Collections\ArrayCollection $categories
* @return \TestBundle\Entity\Product
*/
public function setCategories($categories) {
$productCategoryReplacement = new \Doctrine\Common\Collections\ArrayCollection();
foreach ($categories as $category) {
$newProductCategory = new ProductCategory();
$newProductCategory->setProduct($this);
$newProductCategory->setCategory($category);
$productCategoryReplacement[] = $newProductCategory;
}
$this->productCategory = $productCategoryReplacement;

return $this;
}

编辑1:

我在 Product 中没有 categories 字段,我只有 getCategories()setCategories() 方法。如我的表单类型代码所示,我添加了 Categories 类的 EntityType 字段,该字段映射到 categories 属性(实际上并不存在)。通过这种方式,我可以将现有类别显示为复选框,并正确检查产品的类别。

编辑 2:可能的解决方案

我最终遵循了 Sam Jenses 的建议。我创建了一个服务,如下所示:

文件:src/TestBundle/Service/CategoryCleaner.php

namespace TestBundle\Service;

use Doctrine\ORM\EntityManagerInterface;
use TestBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Request;

class CategoryCleaner {

/**
*
* @var EntityManagerInterface
*/
private $em;

public function __construct(EntityManagerInterface $em) {
$this->em = $em;
}

public function cleanCategories(Product $product, Request $request) {
if ($this->em == null) {
throw new Exception('Entity manager parameter must not be null');
}
if ($request == null) {
throw new Exception('Request parameter must not be null');
}

if ($request->getMethod() == 'POST') {
$categories = $this->em->getRepository('TestBundle:ProductCategory')->findByProduct($product);
foreach ($categories as $category) {
$this->em->remove($category);
}
$this->em->flush();
}
}
}

在接收当前 Product 和 Request 作为参数的 cleanCategories 方法中,与 Product 对应的 ProductCategory 的所有条目都将被删除,仅在 POST 请求的情况下。

服务注册如下:

文件app/config/services.yml

services:
app.category_cleaner:
class: TestBundle\Service\CategoryCleaner
arguments: ['@doctrine.orm.entity_manager']

必须在之前handleRequest($request)(即添加新类别之前)从 Controller 调用该服务。如果没有,我们会得到重复条目异常。

从文件 TestBundle/Controller/ProductController.php 编辑方法

public function editAction(Request $request, Product $product) {
$deleteForm = $this->createDeleteForm($product);
$editForm = $this->createForm('TestBundle\Form\ProductType', $product);

$this->container->get('app.category_cleaner')->cleanCategories($product, $request);

$editForm->handleRequest($request);

if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('product_edit', array('id' => $product->getId()));
}

return $this->render('product/edit.html.twig', array(
'product' => $product,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));

请验证我的方法。

最佳答案

创建一个中间服务,您还可以在其中使用 Doctrine 删除现有实体

关于php - 使用 Doctrine 删除与 Symfony 3 的 3 实体(一对多对一)关联,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44058702/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com