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

如何用Doctrine注解将对象持久化到两张不同数据表?

Hey there! As someone who's worked with Doctrine for a while, let's break down how to solve this problem step by step. It's actually straightforward once you split the problem into defining your entities and handling the dual persistence logic.

Step 1: Define Your Two Entities with Doctrine Annotations

First, you'll need two separate entity classes: one for the current vehicle tracking state, and another for the tour history. This keeps your data models clean and aligned with each table's purpose.

1.1 CarTracking Entity (Current State Table)

This entity maps to your tracking table, holds the current vehicle state, and maintains a relationship to the Company entity (using companyId as the foreign key):

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use App\Entity\Company;

/**
 * @ORM\Entity(repositoryClass="App\Repository\CarTrackingRepository")
 * @ORM\Table(name="car_tracking")
 */
class CarTracking
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

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

    /**
     * @ORM\Column(type="datetime")
     */
    private $startTime;

    /**
     * @ORM\Column(type="datetime", nullable=true)
     */
    private $endTime;

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

    /**
     * @ORM\ManyToOne(targetEntity=Company::class)
     * @ORM\JoinColumn(nullable=false, name="company_id")
     */
    private $company;

    // Getters and Setters
    public function getId(): ?int
    {
        return $this->id;
    }

    public function getTourNumber(): ?string
    {
        return $this->tourNumber;
    }

    public function setTourNumber(string $tourNumber): self
    {
        $this->tourNumber = $tourNumber;
        return $this;
    }

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

    public function setStartTime(\DateTimeInterface $startTime): self
    {
        $this->startTime = $startTime;
        return $this;
    }

    // Add getters/setters for endTime, currentPosition, and company
    public function getCompany(): ?Company
    {
        return $this->company;
    }

    public function setCompany(?Company $company): self
    {
        $this->company = $company;
        return $this;
    }
}

1.2 TourHistory Entity (History Table)

This entity maps to your tourHistory table, stores historical records, and directly holds companyName (no foreign key to Company, since we want to preserve the name at the time of the record):

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\TourHistoryRepository")
 * @ORM\Table(name="tour_history")
 */
class TourHistory
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

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

    /**
     * @ORM\Column(type="datetime")
     */
    private $startTime;

    /**
     * @ORM\Column(type="datetime", nullable=true)
     */
    private $endTime;

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

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

    // Getters and Setters
    public function getId(): ?int
    {
        return $this->id;
    }

    public function getTourNumber(): ?string
    {
        return $this->tourNumber;
    }

    public function setTourNumber(string $tourNumber): self
    {
        $this->tourNumber = $tourNumber;
        return $this;
    }

    public function getCompanyName(): ?string
    {
        return $this->companyName;
    }

    public function setCompanyName(string $companyName): self
    {
        $this->companyName = $companyName;
        return $this;
    }

    // Add getters/setters for startTime, endTime, currentPosition
}

Step 2: Handle Dual Persistence

Now you need to save both entities whenever you create or update the CarTracking record. There are two simple ways to do this:

Option 1: Manual Persistence (Great for Beginners)

This approach is straightforward—you explicitly create and save the TourHistory object alongside CarTracking in your business logic (e.g., a controller or service):

<?php

namespace App\Controller;

use App\Entity\CarTracking;
use App\Entity\TourHistory;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Response;

class CarTrackingController
{
    public function saveOrUpdateTracking(EntityManagerInterface $em): Response
    {
        // Fetch an existing company (or create a new one)
        $company = $em->getRepository(Company::class)->find(1);

        // Create or update your CarTracking entity
        $carTracking = new CarTracking();
        $carTracking->setTourNumber('TOUR-2024-001');
        $carTracking->setStartTime(new \DateTime());
        $carTracking->setCurrentPosition('Downtown Chicago');
        $carTracking->setCompany($company);

        // Create a TourHistory record and copy data from CarTracking
        $tourHistory = new TourHistory();
        $tourHistory->setTourNumber($carTracking->getTourNumber());
        $tourHistory->setStartTime($carTracking->getStartTime());
        $tourHistory->setCurrentPosition($carTracking->getCurrentPosition());
        $tourHistory->setCompanyName($company->getName()); // Grab the current company name

        // Persist both entities and flush to the database
        $em->persist($carTracking);
        $em->persist($tourHistory);
        $em->flush();

        return new Response('Tracking state and history record saved!');
    }
}

Option 2: Doctrine Lifecycle Callback (Automated)

If you want the history record to be created automatically whenever CarTracking is saved/updated, use a Doctrine lifecycle callback. This keeps your business logic cleaner:

First, update the CarTracking entity to include the callback and inject the Entity Manager:

/**
 * @ORM\Entity(repositoryClass="App\Repository\CarTrackingRepository")
 * @ORM\Table(name="car_tracking")
 * @ORM\HasLifecycleCallbacks()
 */
class CarTracking
{
    // ... existing properties and methods

    private $entityManager;

    // Inject the Entity Manager via dependency injection
    public function setEntityManager(EntityManagerInterface $em): void
    {
        $this->entityManager = $em;
    }

    /**
     * @ORM\PostPersist()
     * @ORM\PostUpdate()
     */
    public function createHistoryRecord(): void
    {
        $tourHistory = new TourHistory();
        $tourHistory->setTourNumber($this->tourNumber);
        $tourHistory->setStartTime($this->startTime);
        $tourHistory->setEndTime($this->endTime);
        $tourHistory->setCurrentPosition($this->currentPosition);
        $tourHistory->setCompanyName($this->getCompany()->getName());

        $this->entityManager->persist($tourHistory);
        $this->entityManager->flush();
    }
}

Then, configure the entity to autowire the Entity Manager in your services.yaml:

# config/services.yaml
services:
    App\Entity\CarTracking:
        autowire: true
        calls:
            - method: setEntityManager
              arguments: ['@doctrine.orm.entity_manager']

Key Notes

  • Make sure your Company entity has a getName() method to retrieve the company name.
  • If you want to create a new history record every time CarTracking is updated (not just created), use both @ORM\PostPersist() and @ORM\PostUpdate() annotations in the lifecycle callback.
  • Run php bin/console make:migration and php bin/console doctrine:migrations:migrate to generate and apply the database tables from your entities.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:12:30