实体管理 #
一、实体CRUD #
1.1 创建实体 #
php
<?php
namespace App\Service;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
class UserService
{
public function __construct(
private EntityManagerInterface $entityManager
) {}
public function create(array $data): User
{
$user = new User();
$user->setName($data['name']);
$user->setEmail($data['email']);
$user->setPassword(password_hash($data['password'], PASSWORD_BCRYPT));
$user->setActive(true);
$user->setCreatedAt(new \DateTimeImmutable());
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user;
}
}
1.2 查询实体 #
php
<?php
namespace App\Service;
use App\Entity\User;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
class UserService
{
public function __construct(
private EntityManagerInterface $entityManager,
private UserRepository $userRepository
) {}
public function find(int $id): ?User
{
return $this->userRepository->find($id);
}
public function findByEmail(string $email): ?User
{
return $this->userRepository->findOneBy(['email' => $email]);
}
public function findAll(): array
{
return $this->userRepository->findAll();
}
public function findActive(): array
{
return $this->userRepository->findBy(['active' => true], ['createdAt' => 'DESC']);
}
public function findPaginated(int $page, int $limit): array
{
return $this->userRepository->findBy(
['active' => true],
['createdAt' => 'DESC'],
$limit,
($page - 1) * $limit
);
}
}
1.3 更新实体 #
php
<?php
class UserService
{
public function update(User $user, array $data): User
{
if (isset($data['name'])) {
$user->setName($data['name']);
}
if (isset($data['email'])) {
$user->setEmail($data['email']);
}
if (isset($data['password'])) {
$user->setPassword(password_hash($data['password'], PASSWORD_BCRYPT));
}
$this->entityManager->flush();
return $user;
}
public function activate(User $user): void
{
$user->setActive(true);
$this->entityManager->flush();
}
public function deactivate(User $user): void
{
$user->setActive(false);
$this->entityManager->flush();
}
}
1.4 删除实体 #
php
<?php
class UserService
{
public function delete(User $user): void
{
$this->entityManager->remove($user);
$this->entityManager->flush();
}
public function softDelete(User $user): void
{
$user->setDeletedAt(new \DateTimeImmutable());
$this->entityManager->flush();
}
public function deleteById(int $id): bool
{
$user = $this->userRepository->find($id);
if (!$user) {
return false;
}
$this->entityManager->remove($user);
$this->entityManager->flush();
return true;
}
}
二、生命周期回调 #
2.1 回调注解 #
php
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\HasLifecycleCallbacks]
class User
{
#[ORM\PrePersist]
public function onPrePersist(): void
{
$this->createdAt = new \DateTimeImmutable();
$this->updatedAt = new \DateTimeImmutable();
}
#[ORM\PostPersist]
public function onPostPersist(): void
{
// 实体保存后的操作
}
#[ORM\PreUpdate]
public function onPreUpdate(): void
{
$this->updatedAt = new \DateTimeImmutable();
}
#[ORM\PostUpdate]
public function onPostUpdate(): void
{
// 实体更新后的操作
}
#[ORM\PreRemove]
public function onPreRemove(): void
{
// 实体删除前的操作
}
#[ORM\PostRemove]
public function onPostRemove(): void
{
// 实体删除后的操作
}
#[ORM\PostLoad]
public function onPostLoad(): void
{
// 实体加载后的操作
}
}
2.2 时间戳Trait #
php
<?php
namespace App\Entity\Traits;
use Doctrine\ORM\Mapping as ORM;
trait TimestampableTrait
{
#[ORM\Column]
private ?\DateTimeImmutable $createdAt = null;
#[ORM\Column]
private ?\DateTimeImmutable $updatedAt = null;
public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeImmutable $createdAt): static
{
$this->createdAt = $createdAt;
return $this;
}
public function getUpdatedAt(): ?\DateTimeImmutable
{
return $this->updatedAt;
}
public function setUpdatedAt(\DateTimeImmutable $updatedAt): static
{
$this->updatedAt = $updatedAt;
return $this;
}
#[ORM\PrePersist]
public function setCreatedAtValue(): void
{
$this->createdAt = new \DateTimeImmutable();
$this->updatedAt = new \DateTimeImmutable();
}
#[ORM\PreUpdate]
public function setUpdatedAtValue(): void
{
$this->updatedAt = new \DateTimeImmutable();
}
}
2.3 使用Trait #
php
<?php
namespace App\Entity;
use App\Entity\Traits\TimestampableTrait;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ORM\HasLifecycleCallbacks]
class Article
{
use TimestampableTrait;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $title = null;
#[ORM\Column(type: 'text')]
private ?string $content = null;
}
三、软删除 #
3.1 软删除Trait #
php
<?php
namespace App\Entity\Traits;
use Doctrine\ORM\Mapping as ORM;
trait SoftDeletableTrait
{
#[ORM\Column(nullable: true)]
private ?\DateTimeImmutable $deletedAt = null;
public function getDeletedAt(): ?\DateTimeImmutable
{
return $this->deletedAt;
}
public function setDeletedAt(?\DateTimeImmutable $deletedAt): static
{
$this->deletedAt = $deletedAt;
return $this;
}
public function isDeleted(): bool
{
return $this->deletedAt !== null;
}
public function softDelete(): void
{
$this->deletedAt = new \DateTimeImmutable();
}
public function restore(): void
{
$this->deletedAt = null;
}
}
3.2 软删除过滤器 #
php
<?php
namespace App\Filter;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\Filter\SQLFilter;
class SoftDeletableFilter extends SQLFilter
{
public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string
{
if (!$targetEntity->hasField('deletedAt')) {
return '';
}
return $targetTableAlias . '.deleted_at IS NULL';
}
}
3.3 配置过滤器 #
yaml
# config/packages/doctrine.yaml
doctrine:
orm:
filters:
soft_deletable: App\Filter\SoftDeletableFilter
3.4 使用过滤器 #
php
<?php
use Doctrine\ORM\EntityManagerInterface;
class UserService
{
public function __construct(
private EntityManagerInterface $entityManager
) {}
public function findAllIncludingDeleted(): array
{
$filter = $this->entityManager->getFilters()->disable('soft_deletable');
$users = $this->userRepository->findAll();
$this->entityManager->getFilters()->enable('soft_deletable');
return $users;
}
}
四、实体验证 #
4.1 实体约束 #
php
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity]
class User
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 100)]
#[Assert\NotBlank(message: '姓名不能为空')]
#[Assert\Length(min: 2, max: 100, minMessage: '姓名至少2个字符')]
private ?string $name = null;
#[ORM\Column(length: 180, unique: true)]
#[Assert\NotBlank(message: '邮箱不能为空')]
#[Assert\Email(message: '邮箱格式不正确')]
#[Assert\Length(max: 180)]
private ?string $email = null;
#[ORM\Column]
#[Assert\NotBlank(message: '密码不能为空')]
#[Assert\Length(min: 8, minMessage: '密码至少8个字符')]
private ?string $password = null;
#[ORM\Column]
#[Assert\Range(min: 0, max: 150)]
private int $age = 0;
}
4.2 验证实例 #
php
<?php
namespace App\Service;
use App\Entity\User;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class UserService
{
public function __construct(
private ValidatorInterface $validator
) {}
public function validate(User $user): array
{
$errors = $this->validator->validate($user);
$messages = [];
foreach ($errors as $error) {
$messages[$error->getPropertyPath()] = $error->getMessage();
}
return $messages;
}
public function isValid(User $user): bool
{
return count($this->validator->validate($user)) === 0;
}
}
五、实体继承 #
5.1 单表继承 #
php
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ORM\InheritanceType('SINGLE_TABLE')]
#[ORM\DiscriminatorColumn(name: 'type', type: 'string')]
#[ORM\DiscriminatorMap(['person' => Person::class, 'employee' => Employee::class])]
abstract class Person
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
protected ?int $id = null;
#[ORM\Column(length: 100)]
protected ?string $name = null;
}
#[ORM\Entity]
class Employee extends Person
{
#[ORM\Column(length: 100)]
private ?string $department = null;
#[ORM\Column]
private float $salary = 0.0;
}
5.2 类表继承 #
php
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ORM\InheritanceType('JOINED')]
#[ORM\DiscriminatorColumn(name: 'type', type: 'string')]
#[ORM\DiscriminatorMap(['product' => Product::class, 'digital_product' => DigitalProduct::class])]
abstract class Product
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
protected ?int $id = null;
#[ORM\Column(length: 255)]
protected ?string $name = null;
#[ORM\Column]
protected float $price = 0.0;
}
#[ORM\Entity]
#[ORM\Table(name: 'digital_products')]
class DigitalProduct extends Product
{
#[ORM\Column(length: 255)]
private ?string $downloadUrl = null;
#[ORM\Column]
private int $fileSize = 0;
}
六、实体事件监听器 #
6.1 创建监听器 #
php
<?php
namespace App\EventListener;
use App\Entity\User;
use Doctrine\ORM\Event\PrePersistEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
class UserListener
{
public function prePersist(User $user, PrePersistEventArgs $event): void
{
$user->setCreatedAt(new \DateTimeImmutable());
$user->setUpdatedAt(new \DateTimeImmutable());
// 发送欢迎邮件
$this->sendWelcomeEmail($user);
}
public function preUpdate(User $user, PreUpdateEventArgs $event): void
{
$user->setUpdatedAt(new \DateTimeImmutable());
if ($event->hasChangedField('email')) {
$this->handleEmailChange($user, $event->getOldValue('email'));
}
}
private function sendWelcomeEmail(User $user): void
{
// 发送欢迎邮件逻辑
}
private function handleEmailChange(User $user, string $oldEmail): void
{
// 处理邮箱变更逻辑
}
}
6.2 注册监听器 #
yaml
# config/services.yaml
services:
App\EventListener\UserListener:
tags:
- { name: doctrine.orm.entity_listener, event: prePersist, entity: App\Entity\User }
- { name: doctrine.orm.entity_listener, event: preUpdate, entity: App\Entity\User }
七、实体代理 #
7.1 理解代理 #
text
实体代理:
├── Doctrine使用代理类延迟加载关联
├── 代理类继承实体类
├── 首次访问时才查询数据库
└── 提高性能,减少不必要的查询
7.2 初始化代理 #
php
<?php
use Doctrine\Persistence\Proxy;
class UserService
{
public function initializeProxy($entity): void
{
if ($entity instanceof Proxy) {
$entity->__load();
}
}
public function isProxyInitialized($entity): bool
{
if ($entity instanceof Proxy) {
return $entity->__isInitialized();
}
return true;
}
}
八、最佳实践 #
8.1 实体设计原则 #
text
实体设计原则:
├── 单一职责:每个实体代表一个概念
├── 使用值对象:复杂属性使用值对象
├── 避免贫血模型:实体包含业务逻辑
├── 使用Trait:复用通用属性和方法
├── 合理使用继承:避免过度继承
└── 软删除:重要数据使用软删除
8.2 性能优化 #
php
<?php
class UserService
{
public function batchOperation(array $users): void
{
$batchSize = 100;
foreach ($users as $i => $user) {
$this->entityManager->persist($user);
if (($i + 1) % $batchSize === 0) {
$this->entityManager->flush();
$this->entityManager->clear();
}
}
$this->entityManager->flush();
}
public function updateWithDql(int $userId, string $newName): int
{
return $this->entityManager->createQueryBuilder()
->update(User::class, 'u')
->set('u.name', ':name')
->where('u.id = :id')
->setParameter('name', $newName)
->setParameter('id', $userId)
->getQuery()
->execute();
}
}
九、总结 #
本章学习了:
- 实体CRUD操作
- 生命周期回调
- 时间戳Trait
- 软删除实现
- 实体验证
- 实体继承
- 事件监听器
- 实体代理
下一章将学习 查询构建器。
最后更新:2026-03-28