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

如何用Doctrine生成指定MySQL Schema?关联映射遇问题求助

问题描述

我想知道是否可以用Doctrine生成以下MySQL Schema:

目标表结构

languages表

CREATE TABLE `languages` (
  `id` int(4) UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `status` INT(1) NOT NULL DEFAULT '0',
  `created_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

languages_content表

CREATE TABLE `languages_content` (
  `language_id` int(4) UNSIGNED NOT NULL,
  `language` int(4) UNSIGNED NOT NULL,  
  `label` varchar(50) NOT NULL,
  PRIMARY KEY (`language_id`,`language`),
  KEY `language` (`language`),
  CONSTRAINT `languages_content_idfk_1` FOREIGN KEY (`language_id`) REFERENCES `languages` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `languages_content_idfk_2` FOREIGN KEY (`language`) REFERENCES `languages` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

这个languages_content表用于关联语言表中的翻译与语言。

尝试过的方案及问题

  • 多对多关联:使用Doctrine的多对多注解后,生成的languages_content表会丢失label字段,代码如下:
/**
 * Languages
 *
 * @ORM\Table(name="languages")
 * @ORM\Entity
 */
class Languages
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer", nullable=false, options={"unsigned"=true})
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

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

    /**
     * @var int
     *
     * @ORM\Column(name="status", type="integer", nullable=false)
     */
    private $status = '0';

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="created_at", type="datetime", nullable=false)
     */
    private $createdAt;

    /**
     * @var \Doctrine\Common\Collections\Collection 
     *
     * @ORM\ManyToMany(targetEntity="Languages", inversedBy="language")
     * @ORM\JoinTable(name="languages_content",
     *   joinColumns={
     *     @ORM\JoinColumn(name="language_id", referencedColumnName="id")
     *   },
     *   inverseJoinColumns={
     *     @ORM\JoinColumn(name="language", referencedColumnName="id")
     *   }
     * )
     */
    private $language = array();
  • 多对一关联:改用一对多/多对一关联后,languages_content表会被自动添加一个原本不存在的自增id字段,代码如下:
#[ORM\Entity(repositoryClass: LanguagesRepository::class)]
class Languages
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 50, nullable: false)] 
    private ?string $name = null;

    #[ORM\Column(nullable: true)] 
    private ?int $status = 0; 

    #[ORM\Column(nullable: true)]
    private ?\DateTimeImmutable $createdAt = null;

    #[ORM\OneToMany(mappedBy: 'languages', targetEntity: LanguagesContent::class)]
    private Collection $language;

想请教如何用Doctrine生成符合要求的Schema,提前感谢帮助或建议。


解决方案

完全可以生成,问题出在关联方式的选择和配置细节上:

为什么之前的方案不行?

  • 原生多对多:Doctrine自带的多对多只支持纯关联表(仅包含两个外键),一旦关联表需要额外字段(比如你的label),就必须把关联表单独定义为实体,不能再用原生多对多注解。
  • 普通一对多:直接用一对多关联时,Doctrine默认会给关联实体添加自增主键,但你需要的是language_id+language的复合主键,所以得手动配置主键规则。

正确实现步骤

1. 创建LanguagesContent实体

把关联表做成独立实体,指定复合主键,同时配置两个外键关联到Languages:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table(name: 'languages_content')]
#[ORM\IdJoinColumns([
    new ORM\IdJoinColumn(name: 'language_id', referencedColumnName: 'id'),
    new ORM\IdJoinColumn(name: 'language', referencedColumnName: 'id')
])]
class LanguagesContent
{
    #[ORM\ManyToOne(targetEntity: Languages::class, inversedBy: 'translations')]
    #[ORM\JoinColumn(name: 'language_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
    private ?Languages $originalLanguage = null;

    #[ORM\ManyToOne(targetEntity: Languages::class)]
    #[ORM\JoinColumn(name: 'language', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
    private ?Languages $targetLanguage = null;

    #[ORM\Column(type: 'string', length: 50, nullable: false)]
    private ?string $label = null;

    // 构造函数
    public function __construct(Languages $original, Languages $target)
    {
        $this->originalLanguage = $original;
        $this->targetLanguage = $target;
    }

    // 省略getter、setter方法
}

2. 修改Languages实体的关联配置

把原来的多对多改成一对多关联到LanguagesContent:

<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: LanguagesRepository::class)]
#[ORM\Table(name: 'languages')]
class Languages
{
    #[ORM\Id]
    #[ORM\GeneratedValue(strategy: 'IDENTITY')]
    #[ORM\Column(type: 'integer', options: ['unsigned' => true])]
    private ?int $id = null;

    #[ORM\Column(type: 'string', length: 50, nullable: false)]
    private ?string $name = null;

    #[ORM\Column(type: 'integer', nullable: false, options: ['default' => 0])]
    private int $status = 0;

    #[ORM\Column(type: 'datetime', nullable: false)]
    private ?\DateTimeInterface $createdAt = null;

    #[ORM\OneToMany(mappedBy: 'originalLanguage', targetEntity: LanguagesContent::class, cascade: ['persist', 'remove'])]
    private Collection $translations;

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

    // 省略其他getter、setter方法

    public function addTranslation(LanguagesContent $translation): self
    {
        if (!$this->translations->contains($translation)) {
            $this->translations->add($translation);
        }
        return $this;
    }

    public function removeTranslation(LanguagesContent $translation): self
    {
        $this->translations->removeElement($translation);
        return $this;
    }
}

3. 关键配置说明

  • 复合主键:通过#[ORM\IdJoinColumns]指定language_id和language作为联合主键,避免Doctrine自动添加自增id。
  • 关联关系:Languages与LanguagesContent是一对多关系,LanguagesContent与Languages是两个多对一关系(分别对应原始语言和目标语言)。
  • 级联操作:配置onDelete: 'CASCADE'和cascade: ['persist', 'remove'],与你原始SQL中的约束规则保持一致。

配置完成后,运行doctrine:schema:update --force就能生成完全符合要求的表结构。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 15:20:32