You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony创建订单时触发唯一键重复约束错误求助

解决Symfony 6+中创建订单时的唯一约束重复错误

你遇到的SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1' for key 'UNIQ_ED896F46AF89CCED'错误,根源是实体关联配置错误,具体问题点和解决方案如下:

问题根源

  1. OrderDetail的Product关联类型错误:OrderDetail中productid使用了#[ORM\OneToOne]关联,这会让Doctrine自动为该字段添加唯一约束,意味着同一个Product只能被绑定到一个OrderDetail。第一次创建订单用产品ID 1没问题,第二次再用该产品创建订单详情时,就会触发唯一键重复。
  2. Order实体存在冗余关联:Order中重复定义了两个指向OrderDetail的#[ORM\OneToMany]关联($productid和$orderDetails),属于无效冗余配置,会干扰Doctrine的映射逻辑。

解决方案

步骤1:修正OrderDetail的Product关联类型

将OrderDetail中的productid从OneToOne改为ManyToOne(一个产品可被多个订单详情引用,一个订单详情对应一个产品,符合业务逻辑):

<?php

namespace App\Entity;

use App\Repository\OrderDetailRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: OrderDetailRepository::class)]
class OrderDetail
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\ManyToOne(inversedBy: 'orderDetails')]
    private ?Order $orderid = null;

    // 关键修改:从OneToOne改为ManyToOne
    #[ORM\ManyToOne(targetEntity: Product::class)]
    private ?Product $productid = null;

    #[ORM\Column]
    private ?int $quantity = null;

    #[ORM\Column(type: Types::DECIMAL, precision: 10, scale: 0)]
    private ?string $price = null;

    // 以下getter/setter保持不变
    public function getId(): ?int
    {
        return $this->id;
    }

    public function getOrderid(): ?Order
    {
        return $this->orderid;
    }

    public function setOrderid(?Order $orderid): static
    {
        $this->orderid = $orderid;
        return $this;
    }

    public function getProductid(): ?Product
    {
        return $this->productid;
    }

    public function setProductid(?Product $productid): static
    {
        $this->productid = $productid;
        return $this;
    }

    public function getQuantity(): ?int
    {
        return $this->quantity;
    }

    public function setQuantity(int $quantity): static
    {
        $this->quantity = $quantity;
        return $this;
    }

    public function getPrice(): ?string
    {
        return $this->price;
    }

    public function setPrice(string $price): static
    {
        $this->price = $price;
        return $this;
    }
}

步骤2:删除Order实体中的冗余关联

删除Order中多余的$productid字段及对应的addProductid/removeProductid方法,只保留$orderDetails管理订单详情:

<?php

namespace App\Entity;

use App\Repository\OrderRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: OrderRepository::class)]
#[ORM\Table(name: '`order`')]
class Order
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(type: Types::DECIMAL, precision: 10, scale: 0)]
    private ?string $total = null;

    #[ORM\Column(type: Types::DATETIME_MUTABLE)]
    private ?\DateTimeInterface $createat = null;

    #[ORM\Column(type: Types::DATETIME_MUTABLE)]
    private ?\DateTimeInterface $updateat = null;

    #[ORM\Column]
    private ?int $status = null;

    /**
     * @var Collection<int, OrderDetail>
     */
    #[ORM\OneToMany(targetEntity: OrderDetail::class, mappedBy: 'orderid', cascade: ['persist', 'remove'])]
    private Collection $orderDetails;

    public const STATUS_NEW = 0;

    public function __construct()
    {
        $this->orderDetails = new ArrayCollection();
    }

    // 以下getter/setter及orderDetails的add/remove方法保持不变
    public function getId(): ?int
    {
        return $this->id;
    }

    public function getTotal(): ?string
    {
        return $this->total;
    }

    public function setTotal(string $total): static
    {
        $this->total = $total;
        return $this;
    }

    public function getCreateat(): ?\DateTimeInterface
    {
        return $this->createat;
    }

    public function setCreateat(\DateTimeInterface $createat): static
    {
        $this->createat = $createat;
        return $this;
    }

    public function getUpdateat(): ?\DateTimeInterface
    {
        return $this->updateat;
    }

    public function setUpdateat(\DateTimeInterface $updateat): static
    {
        $this->updateat = $updateat;
        return $this;
    }

    public function getStatus(): ?int
    {
        return $this->status;
    }

    public function setStatus(int $status): static
    {
        $this->status = $status;
        return $this;
    }

    /**
     * @return Collection<int, OrderDetail>
     */
    public function getOrderDetails(): Collection
    {
        return $this->orderDetails;
    }

    public function addOrderDetail(OrderDetail $orderDetail): static
    {
        if (!$this->orderDetails->contains($orderDetail)) {
            $this->orderDetails->add($orderDetail);
            $orderDetail->setOrderid($this);
        }
        return $this;
    }

    public function removeOrderDetail(OrderDetail $orderDetail): static
    {
        if ($this->orderDetails->removeElement($orderDetail)) {
            if ($orderDetail->getOrderid() === $this) {
                $orderDetail->setOrderid(null);
            }
        }
        return $this;
    }
}

步骤3:同步数据库结构

执行以下命令生成迁移并更新数据库:

# 生成迁移文件
php bin/console make:migration

# 执行迁移
php bin/console doctrine:migrations:migrate

额外优化

由于在Order的OneToMany关联中添加了cascade: ['persist', 'remove'],控制器中可以去掉$em->persist($orderDetail);这行代码,简化逻辑:

foreach ($data as $item) {
    $product = $em->getRepository(Product::class)->find($item['id']);

    if (!$product) {
        return new JsonResponse(['error' => 'Product not found'], 404);
    }

    $orderDetail = new OrderDetail();
    $orderDetail->setProductid($product);
    $orderDetail->setQuantity($item['quantity']);
    $orderDetail->setPrice($product->getPrice() * $item['quantity']);
    $orderDetail->setOrderid($order);

    $total += $orderDetail->getPrice();

    $order->addOrderDetail($orderDetail);
    // 移除该行:$em->persist($orderDetail);
}

内容的提问来源于stack exchange,提问作者bratniak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 23:17:02