Doctrine基础 #
一、Doctrine概述 #
1.1 什么是Doctrine #
Doctrine是Symfony默认的ORM(对象关系映射)框架,提供强大的数据库操作能力。
text
┌─────────────────────────────────────────────────────┐
│ Doctrine 组成 │
├─────────────────────────────────────────────────────┤
│ │
│ DBAL (数据库抽象层) │
│ ├── 数据库连接 │
│ ├── SQL查询构建 │
│ └── 平台抽象 │
│ │
│ ORM (对象关系映射) │
│ ├── 实体管理 │
│ ├── 查询构建器 │
│ ├── DQL查询语言 │
│ └── 关联映射 │
│ │
└─────────────────────────────────────────────────────┘
1.2 ORM优势 #
| 优势 | 说明 |
|---|---|
| 面向对象 | 使用PHP对象操作数据库 |
| 数据库无关 | 轻松切换数据库类型 |
| 自动映射 | 自动转换数据类型 |
| 关联管理 | 自动处理表关联 |
| 缓存支持 | 提升查询性能 |
二、安装配置 #
2.1 安装Doctrine #
bash
# 安装Doctrine包
composer require doctrine
# 安装Maker Bundle(用于生成实体)
composer require --dev maker
2.2 数据库配置 #
bash
# .env 文件
DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=8.0"
# PostgreSQL
DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=14"
# SQLite
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
2.3 Doctrine配置 #
yaml
# config/packages/doctrine.yaml
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
driver: pdo_mysql
server_version: '8.0'
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
orm:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
mappings:
App:
is_bundle: false
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
三、数据库操作 #
3.1 创建数据库 #
bash
# 创建数据库
php bin/console doctrine:database:create
# 删除数据库
php bin/console doctrine:database:drop --force
3.2 数据库连接测试 #
php
<?php
namespace App\Controller;
use Doctrine\DBAL\Connection;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
class DatabaseController extends AbstractController
{
public function test(Connection $connection): Response
{
try {
$connection->executeQuery('SELECT 1');
return new Response('Database connected!');
} catch (\Exception $e) {
return new Response('Connection failed: ' . $e->getMessage());
}
}
}
3.3 原生SQL查询 #
php
<?php
use Doctrine\DBAL\Connection;
class ReportService
{
public function __construct(
private Connection $connection
) {}
public function getUserStats(): array
{
$sql = 'SELECT COUNT(*) as total, status FROM users GROUP BY status';
return $this->connection->fetchAllAssociative($sql);
}
public function searchUsers(string $keyword): array
{
$sql = 'SELECT * FROM users WHERE name LIKE :keyword';
return $this->connection->fetchAllAssociative($sql, [
'keyword' => '%' . $keyword . '%',
]);
}
public function updateUserStatus(int $id, string $status): int
{
return $this->connection->update('users', [
'status' => $status,
'updated_at' => new \DateTime(),
], ['id' => $id]);
}
}
四、实体基础 #
4.1 创建实体 #
bash
# 创建实体
php bin/console make:entity User
# 输出
created: src/Entity/User.php
created: src/Repository/UserRepository.php
4.2 实体定义 #
php
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\Table(name: 'users')]
class User
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 100)]
private ?string $name = null;
#[ORM\Column(length: 180, unique: true)]
private ?string $email = null;
#[ORM\Column]
private bool $active = true;
#[ORM\Column]
private ?\DateTimeImmutable $createdAt = null;
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): static
{
$this->name = $name;
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): static
{
$this->email = $email;
return $this;
}
public function isActive(): bool
{
return $this->active;
}
public function setActive(bool $active): static
{
$this->active = $active;
return $this;
}
public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeImmutable $createdAt): static
{
$this->createdAt = $createdAt;
return $this;
}
}
4.3 字段类型 #
php
<?php
use Doctrine\DBAL\Types\Types;
class Product
{
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $name = null;
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $description = null;
#[ORM\Column(type: Types::DECIMAL, precision: 10, scale: 2)]
private ?string $price = null;
#[ORM\Column(type: Types::INTEGER)]
private int $stock = 0;
#[ORM\Column(type: Types::BOOLEAN)]
private bool $active = true;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
private ?\DateTimeImmutable $createdAt = null;
#[ORM\Column(type: Types::JSON)]
private array $metadata = [];
#[ORM\Column(type: Types::SIMPLE_ARRAY)]
private array $tags = [];
#[ORM\Column(type: Types::DATE_IMMUTABLE)]
private ?\DateTimeImmutable $publishedAt = null;
#[ORM\Column(type: Types::TIME_IMMUTABLE)]
private ?\DateTimeImmutable $openTime = null;
}
4.4 字段选项 #
php
<?php
class Article
{
#[ORM\Column(
name: 'article_title',
length: 200,
nullable: false,
unique: true,
options: [
'comment' => '文章标题',
'default' => '无标题'
]
)]
private ?string $title = null;
#[ORM\Column(
type: Types::STRING,
length: 100,
columnDefinition: 'VARCHAR(100) NOT NULL'
)]
private ?string $author = null;
}
五、迁移管理 #
5.1 创建迁移 #
bash
# 安装迁移包
composer require doctrine/migrations
# 创建迁移
php bin/console make:migration
# 查看迁移状态
php bin/console doctrine:migrations:status
# 执行迁移
php bin/console doctrine:migrations:migrate
# 回滚迁移
php bin/console doctrine:migrations:execute DoctrineMigrations\\Version20240101000000 --down
5.2 迁移文件 #
php
<?php
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20240101000000 extends AbstractMigration
{
public function getDescription(): string
{
return 'Create users table';
}
public function up(Schema $schema): void
{
$this->addSql('
CREATE TABLE users (
id INT AUTO_INCREMENT NOT NULL,
name VARCHAR(100) NOT NULL,
email VARCHAR(180) NOT NULL,
active TINYINT(1) NOT NULL,
created_at DATETIME NOT NULL,
UNIQUE INDEX UNIQ_1483A5E9E7927C74 (email),
PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB
');
}
public function down(Schema $schema): void
{
$this->addSql('DROP TABLE users');
}
}
5.3 迁移配置 #
yaml
# config/packages/doctrine_migrations.yaml
doctrine_migrations:
migrations_paths:
DoctrineMigrations: '%kernel.project_dir%/migrations'
enable_profiler: false
六、Repository #
6.1 创建Repository #
php
<?php
namespace App\Repository;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
class UserRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, User::class);
}
public function findActive(): array
{
return $this->createQueryBuilder('u')
->where('u.active = :active')
->setParameter('active', true)
->orderBy('u.createdAt', 'DESC')
->getQuery()
->getResult();
}
public function findByEmail(string $email): ?User
{
return $this->createQueryBuilder('u')
->where('u.email = :email')
->setParameter('email', $email)
->getQuery()
->getOneOrNullResult();
}
public function search(string $keyword): array
{
return $this->createQueryBuilder('u')
->where('u.name LIKE :keyword OR u.email LIKE :keyword')
->setParameter('keyword', '%' . $keyword . '%')
->getQuery()
->getResult();
}
}
6.2 使用Repository #
php
<?php
namespace App\Controller;
use App\Entity\User;
use App\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
class UserController extends AbstractController
{
public function list(UserRepository $userRepository): Response
{
$users = $userRepository->findAll();
$activeUsers = $userRepository->findActive();
$user = $userRepository->find(1);
$userByEmail = $userRepository->findByEmail('john@example.com');
return $this->render('user/list.html.twig', [
'users' => $users,
]);
}
}
七、EntityManager #
7.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->setCreatedAt(new \DateTimeImmutable());
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user;
}
public function update(User $user, array $data): User
{
$user->setName($data['name'] ?? $user->getName());
$user->setEmail($data['email'] ?? $user->getEmail());
$this->entityManager->flush();
return $user;
}
public function delete(User $user): void
{
$this->entityManager->remove($user);
$this->entityManager->flush();
}
}
7.2 批量操作 #
php
<?php
class BatchService
{
public function __construct(
private EntityManagerInterface $entityManager
) {}
public function batchInsert(array $users): void
{
$batchSize = 100;
$i = 0;
foreach ($users as $userData) {
$user = new User();
$user->setName($userData['name']);
$user->setEmail($userData['email']);
$this->entityManager->persist($user);
if (($i % $batchSize) === 0) {
$this->entityManager->flush();
$this->entityManager->clear();
}
$i++;
}
$this->entityManager->flush();
$this->entityManager->clear();
}
public function batchUpdate(array $users): void
{
$batchSize = 100;
$i = 0;
foreach ($users as $user) {
$user->setActive(false);
if (($i % $batchSize) === 0) {
$this->entityManager->flush();
$this->entityManager->clear();
}
$i++;
}
$this->entityManager->flush();
}
}
八、数据填充 #
8.1 创建Fixture #
bash
# 安装Fixture包
composer require --dev doctrine/fixtures-bundle
php
<?php
namespace App\DataFixtures;
use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
class UserFixtures extends Fixture
{
public function load(ObjectManager $manager): void
{
for ($i = 1; $i <= 10; $i++) {
$user = new User();
$user->setName("User $i");
$user->setEmail("user$i@example.com");
$user->setCreatedAt(new \DateTimeImmutable());
$user->setActive(true);
$manager->persist($user);
$this->addReference("user_$i", $user);
}
$manager->flush();
}
}
8.2 关联Fixture #
php
<?php
namespace App\DataFixtures;
use App\Entity\Article;
use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Persistence\ObjectManager;
class ArticleFixtures extends Fixture implements DependentFixtureInterface
{
public function load(ObjectManager $manager): void
{
for ($i = 1; $i <= 20; $i++) {
$article = new Article();
$article->setTitle("Article $i");
$article->setContent("Content for article $i");
$article->setAuthor($this->getReference("user_" . rand(1, 10), User::class));
$manager->persist($article);
}
$manager->flush();
}
public function getDependencies(): array
{
return [
UserFixtures::class,
];
}
}
8.3 执行Fixture #
bash
# 执行所有Fixture
php bin/console doctrine:fixtures:load
# 追加数据(不删除现有数据)
php bin/console doctrine:fixtures:load --append
九、总结 #
本章学习了:
- Doctrine ORM概述
- 数据库配置
- 实体定义
- 字段类型和选项
- 迁移管理
- Repository使用
- EntityManager操作
- 数据填充
下一章将学习 实体管理。
最后更新:2026-03-28