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

Symfony 6.2:访问OneToMany集合时出现「主键langId缺失值」错误

解决Doctrine关联查询时的复合主键缺失问题

我定义了AccessoryGroup实体,通过OneToMany关联到AccessoryPart实体。获取AccessoryGroup列表操作正常,但访问其关联的accessoryPartList集合时,系统抛出错误:

Missing value for primary key langId on App\Entity\AccessoryGroup

相关代码

AccessoryGroup.php

#[ORM\Entity(repositoryClass: AccessoryGroupRepository::class, readOnly: true)]
class AccessoryGroup
{
    #[ORM\Id]
    #[ORM\GeneratedValue(strategy: "NONE")]
    #[ORM\Column(name: "accessory_group_id" , type: "string", length: 10, nullable: false)]
    private string $accessoryGroupId = "";

    #[ORM\Id]
    #[ORM\GeneratedValue(strategy: "NONE")]
    #[ORM\Column(type: "string", length: 2, nullable: false, options: ["default" => "DE"])]
    private string $langId = "DE";

    #[ORM\Column(type: "string", length: 50, nullable: false)]
    private string $title = "";

    #[ORM\Column(name: "last_update", type: "datetime", columnDefinition: "DATETIME default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP")]
    private DateTime $lastUpdate;

    #[ORM\OneToMany( mappedBy: "accessoryGroup", targetEntity: "AccessoryPart")]
    private Collection $accessoryPartList;

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

    /** getters **/
    ...

    /**
     * @return Collection
     */
    public function getAccessoryPartList(): Collection
    {
        return $this->accessoryPartList;
    }

}

AccessoryPart.php

#[ORM\Entity(repositoryClass: AccessoryPartRepository::class, readOnly: true)]
class AccessoryPart
{
    #[ORM\Id]
    #[ORM\ManyToOne(targetEntity: "AccessoryGroup", inversedBy: "accessoryPartList")]
    #[ORM\JoinColumn(name: "accessory_group_id", referencedColumnName: "accessory_group_id", nullable: false)]
    private ?AccessoryGroup $accessoryGroup = null;

    #[ORM\Id]
    #[ORM\ManyToOne(targetEntity: "Articles")]
    #[ORM\JoinColumn(name: "article_part_id", referencedColumnName: "article_id", nullable: false)]
    private ?Articles $articlePart = null;

    #[ORM\Id]
    #[ORM\ManyToOne(targetEntity: "Manufacturers")]
    #[ORM\JoinColumn(name: "manufacturer_part_id", referencedColumnName: "manufacturer_id", nullable: false)]
    private ?Manufacturers $manufacturerPart = null;

    #[ORM\Id]
    #[ORM\ManyToOne(targetEntity: "Articles")]
    #[ORM\JoinColumn(name: "article_accessory_id", referencedColumnName: "article_id", nullable: false)]
    private ?Articles $articleAccessory = null;

    #[ORM\Id]
    #[ORM\ManyToOne(targetEntity: "Manufacturers")]
    #[ORM\JoinColumn(name: "manufacturer_acc_id", referencedColumnName: "manufacturer_id", nullable: false)]
    private ?Manufacturers $manufacturerAcc = null;

    #[ORM\Column(type: "integer", nullable: true, options: ["default" => 0])]
    private int $flag = 0;

    #[ORM\Column(name: "last_update", type: "datetime", columnDefinition: "DATETIME default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP")]
    private DateTime $lastUpdate;

    /** getters *//
}

AccessoryGroupRepository.php

/**
 * @param $articleId
 * @param $language
 * @return AccessoryGroup[]
 */
public function getAccessoryFromPart($articleId, $language): array
{
    return $this->createQueryBuilder('ag')
        ->where('ag.langId = :lng')
        ->setParameter('lng', $language)
        ->distinct()
        ->getQuery()
        ->getResult();
}

控制器代码

$accessoryGList = $agr->getAccessoryFromPart($articleId, $this->sesCustomer['lang'] ?? "DE"); // 结果正常,返回两行数据
echo "Size: ".sizeof($accessoryGList[0]->getAccessoryPartList()); // <== 触发错误

问题原因及解决方法

核心问题

AccessoryGroup使用复合主键(accessoryGroupId + langId),但AccessoryPart关联AccessoryGroup时,只配置了accessory_group_id这一个主键字段。Doctrine无法通过单一主键定位完整的AccessoryGroup实体,加载关联集合时会找不到langId的值,从而抛出错误。

修复步骤

  1. 补全AccessoryPart的关联映射
    修改AccessoryPart中accessoryGroup的关联定义,添加复合主键的第二个字段映射,确保Doctrine能获取完整的主键信息:

    #[ORM\Id]
    #[ORM\ManyToOne(targetEntity: "AccessoryGroup", inversedBy: "accessoryPartList")]
    #[ORM\JoinColumns([
        new ORM\JoinColumn(name: "accessory_group_id", referencedColumnName: "accessory_group_id", nullable: false),
        new ORM\JoinColumn(name: "lang_id", referencedColumnName: "langId", nullable: false)
    ])]
    private ?AccessoryGroup $accessoryGroup = null;
    

    注意:需确保数据库中accessory_part表存在lang_id字段,且与accessory_group表的langId字段对应。

  2. 提前加载关联数据(可选)
    为避免懒加载时的额外查询,可在Repository查询中提前关联并获取accessoryPartList集合:

    public function getAccessoryFromPart($articleId, $language): array
    {
        return $this->createQueryBuilder('ag')
            ->where('ag.langId = :lng')
            ->setParameter('lng', $language)
            ->leftJoin('ag.accessoryPartList', 'apl')
            ->addSelect('apl')
            ->distinct()
            ->getQuery()
            ->getResult();
    }
    
  3. 验证主键Getter方法完整性
    确保AccessoryGroup的两个主键字段都有对应的Getter方法,Doctrine需要通过这些方法获取主键值构建关联查询:

    public function getAccessoryGroupId(): string
    {
        return $this->accessoryGroupId;
    }
    
    public function getLangId(): string
    {
        return $this->langId;
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 21:35:14