專案開發、測試、部署階段常需預載基礎資料(如管理員帳號、角色權限、選單項目、測試商品)。DoctrineFixturesBundle 提供標準化、可版控、可重複執行的資料初始化機制,是 Symfony 專案的必備工具。
composer require --dev doctrine/doctrine-fixtures-bundle
對應 Symfony 版本相容性:
// config/bundles.php
return [
// ...
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
];
或傳統 AppKernel.php 寫法:
if (in_array($this->getEnvironment(), ['dev', 'test'])) {
$bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();
}
預設路徑:src/DataFixtures/(SF4+)或 src/AppBundle/DataFixtures/ORM/(SF3)
passwordHasher = $passwordHasher;
}
public function load(ObjectManager $manager): void
{
// 1. 建立角色
$roleAdmin = new Role();
$roleAdmin->setName('ROLE_ADMIN');
$manager->persist($roleAdmin);
$roleUser = new Role();
$roleUser->setName('ROLE_USER');
$manager->persist($roleUser);
// 2. 建立管理員帳號
$admin = new User();
$admin->setEmail('admin@example.com');
$admin->setRoles(['ROLE_ADMIN']);
$admin->setPassword($this->passwordHasher->hashPassword($admin, 'secure_password_123'));
$manager->persist($admin);
// 3. 批次建立測試資料
for ($i = 1; $i <= 10; $i++) {
$user = new User();
$user->setEmail("user{$i}@example.com");
$user->setRoles(['ROLE_USER']);
$user->setPassword($this->passwordHasher->hashPassword($user, 'password'));
$manager->persist($user);
}
$manager->flush();
}
}
# 基本載入(會先清空相關資料表)
php bin/console doctrine:fixtures:load
# 保留現有資料,僅追加(⚠️ 可能導致主鍵衝突)
php bin/console doctrine:fixtures:load --append
# 指定特定 fixture 目錄
php bin/console doctrine:fixtures:load --fixtures=src/DataFixtures/Custom
# 指定 Entity Manager(多資料庫專案)
php bin/console doctrine:fixtures:load --em=custom_em
# 無互動模式(CI/CD 適用)
php bin/console doctrine:fixtures:load --no-interaction
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
class UserFixtures extends Fixture implements DependentFixtureInterface
{
public function load(ObjectManager $manager): void
{
// 此時 RoleFixtures 已執行完成,可安全引用
$adminRole = $manager->getRepository(Role::class)->findOneBy(['name' => 'ROLE_ADMIN']);
// ...
}
public function getDependencies(): array
{
return [RoleFixtures::class];
}
}
// 在同一個 Fixture 類別內
$manager->persist($admin);
$this->addReference('admin_user', $admin); // 註冊參考
// 稍後取用
$user = new User();
$user->setCreatedBy($this->getReference('admin_user'));
// UserFixtures.php
public function load(ObjectManager $manager): void
{
$admin = new User();
// ...
$this->setReference('admin_user', $admin); // 注意:使用 setReference
$manager->persist($admin);
}
// ArticleFixtures.php (implements DependentFixtureInterface)
public function load(ObjectManager $manager): void
{
$admin = $this->getReference('admin_user'); // 取得共享實例
$article->setAuthor($admin);
}
建立 tests/DataFixtures/ 目錄,配置 phpunit.xml.dist:
<phpunit>
<extensions>
<extension class="Doctrine\Bundle\FixturesBundle\Test\FixturesExtension"/>
</extensions>
</phpunit>
測試類別中自動載入:
use Doctrine\Bundle\FixturesBundle\Test\FixturesTrait;
class UserTest extends KernelTestCase
{
use FixturesTrait;
public function testUserCreation(): void
{
$this->loadFixtures([AppFixtures::class]);
// ...
}
}
| 問題 | 原因 | 解決 |
|---|---|---|
| 主鍵重複錯誤 | 使用 --append 但資料已存在 | 加入檢查邏輯或使用 UUID/自然鍵 |
| 外鍵約束失敗 | 載入順序錯誤 | 實作 DependentFixtureInterface |
| 生產環境誤執行 | Bundle 註冊未限制環境 | 僅在 dev/test 註冊 Bundle |
| 大量資料效能差 | 逐筆 flush | 批次 persist,定期 flush(如每 100 筆) |
DoctrineFixturesBundle 讓資料庫初始化資料成為程式碼一部分,支援版本控管、團隊協作、CI/CD 自動化部署。善用依賴排序、Reference 共享、批次處理等技巧,可建構健全、可維護的資料初始化體系。