如何通过理论实体之间的id建立一对一的关系


How to establish one-to-one relation through IDs between doctrine entities

我希望这是一个直接的过程,但似乎Doctrine不太喜欢通过它们的id链接实体的想法。

所有我打算做的是标准化一个表,从它运送一些字段到一个新的表,而不是添加一个新的引用字段到原来的表中,以保持新的对应记录的ID,确保在子表中的新记录将具有相同的ID,它的父行。

下面是我所拥有的一个例子:

一个User实体,带注释字段$user引用UserDetail实体中的列ID到它自己的ID

/**
 * @ORM'Table(name="user", options={"collate"="utf8_general_ci", "charset"="utf8", "engine"="InnoDB"})
 * @ORM'Entity
 */
class User extends Entity
{
    /**
     * @var integer
     *
     * @ORM'Column(name="id", type="integer", nullable=false)
     * @ORM'Id
     * @ORM'GeneratedValue(strategy="IDENTITY")
     */
    private $id
     /**
      * @ORM'OneToOne(targetEntity="UserDetail", cascade={"persist"})
      * @ORM'JoinColumn(name="id", referencedColumnName="id", nullable=true)
      */
     private $userDetail;
     ...
}

这里是UserDetail删除了ID的@GeneratedValue

/**
 * @ORM'Table(name="user_detail", options={"collate"="utf8_general_ci", "charset"="utf8", "engine"="InnoDB"})
 * @ORM'Entity
 */
class UserDetail extends Entity
{
    /**
     * @var integer
     *
     * @ORM'Column(name="id", type="integer", nullable=false)
     * @ORM'Id
     */
    private $id;
    ...
}

在这一点上,我的期望是能够做这样的事情:

$user = new User();
$userDetail = new UserDetail();
$user->setUserDetail($userDetail)
$entityManager->persist($user);
$entityManager->flush();

并获得两个记录持久化到useruser_detail表中,它们具有相同的 ID,但现实是,没有为UserDetail的标识符定义任何策略,doctrine将抱怨缺少ID, Entity of type UserDetail is missing an assigned ID for field 'id'.

当然也可以手动完成这项工作,并且在多个调用中

$user = new User();
$entityManager->persist($user);
$entityManager->flush();
$userDetail = new UserDetail();
$userDetail->setId($user->getId)    
$user->setUserDetail($userDetail)
$entityManager->persist($user);
$entityManager->flush();

但是我仍然希望有一个正确的配置(注释)可以帮助我避免这些额外的步骤,并通过实体的id将一对一关系的处理留给Doctrine。

这是未经测试的,但我认为以下可能工作,根据文档(http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/composite-primary-keys.html):

)
    /**
     * @ORM'Table(name="user_detail", options={"collate"="utf8_general_ci", "charset"="utf8", "engine"="InnoDB"})
     * @ORM'Entity
     */
    class UserDetail extends Entity
    {
        /**
         * @var integer
         *
         * @ORM'OneToOne(targetEntity="User")
         * @ORM'JoinColumn(name="id", referencedColumnName="id")
         * @ORM'Id
         */
        private $user;
    ...
    }

我成功地创建了一个一对一的关系,其中主键也是外键。见下文:

汽车实体:

/**
 * @ORM'Table(name="cars")
 * @ORM'Entity(repositoryClass="App'Repository'CarRepository") 
 */
class Car
{
    /**
     * @ORM'OneToOne(targetEntity="CarConfiguration", mappedBy="car")
     */
    private ?CarConfiguration $carConfiguration = null;
    
    public function getCarConfiguration(): ?CarConfiguration
    {
        return $this->carConfiguration;
    }
    public function setCarConfiguration(CarConfiguration $carConfiguration): void
    {
        $this->carConfiguration = $carConfiguration;
    }
}

Car Configuration实体:

/**
 * @ORM'Table(name="car_configurations")
 * @ORM'Entity()
 */
class CarConfiguration
{
    /**
     * @ORM'Id
     * @ORM'JoinColumn(name="car_id", referencedColumnName="id", unique=true, nullable=false, onDelete="CASCADE")
     * @ORM'OneToOne(targetEntity="Car", inversedBy="carConfiguration", cascade={"all"})
     */
    private Car $car;
    /**
     * @ORM'Column(name="has_radio", type="boolean")
     */
    private bool $hasRadio;
    public function __construct(Car $car, bool $hasRadio = false)
    {
        $this->car = $car;
        $this->hasRadio = $hasRadio;
    }
    public function getCar(): Car
    {
        return $this->car;
    }
    public function hasRadio(): bool
    {
        return $this->hasRadio;
    }
    public function setHasRadio(bool $hasRadio): void
    {
        $this->hasRadio = $hasRadio;
    }
}

Car存储库:

class CarRepository extends EntityRepository
{
    public function persist(Car $car): void
    {
        $entityManager = $this->getEntityManager();
        $entityManager->persist($car);
    }
    public function flush(): void
    {
        $entityManager = $this->getEntityManager();
        $entityManager->flush();
    }   
}   

客户端代码:

class AwesomeCommand extends Command
{
    private CarRepository $carRepository;
    
    public function __construct(CarRepository $carRepository) {
        parent::__construct();
        $this->carRepository = $carRepository;
    }
    
    protected function execute(InputInterface $input, OutputInterface $output): void
    {
        $car = $this->carRepository->find(1234);
        $carConfiguration = new CarConfiguration($car, true);
        $car->setCarConfiguration($carConfiguration);
        $this->carRepository->persist($car);
        $this->carRepository->flush();
    }
}   
链接:

https://www.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/composite-primary-keys.html use-case-2-simple-derived-identity