DoctrineFixturesBundle
Fixtures 用于将“伪造”数据集加载到数据库中,然后可用于测试或在开发应用程序时帮助您获得一些有趣的数据。
此 bundle 兼容 Doctrine ORM(MySQL、PostgreSQL、SQLite 等)支持的任何数据库。如果您正在使用 MongoDB,则必须使用 DoctrineMongoDBBundle 代替。
安装
如果您正在使用 Symfony Flex,运行此命令即可完成
1
$ composer require --dev orm-fixtures
如果您没有使用 Flex,请运行此其他命令代替
1
$ composer require --dev doctrine/doctrine-fixtures-bundle
编写 Fixtures
数据 fixtures 是 PHP 类,您可以在其中创建对象并将它们持久化到数据库。
假设您想向数据库中添加一些 Product
对象。没问题!创建一个 fixtures 类并开始添加产品
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
// src/DataFixtures/AppFixtures.php
namespace App\DataFixtures;
use App\Entity\Product;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager): void
{
// create 20 products! Bam!
for ($i = 0; $i < 20; $i++) {
$product = new Product();
$product->setName('product '.$i);
$product->setPrice(mt_rand(10, 100));
$manager->persist($product);
}
$manager->flush();
}
}
加载 Fixtures
编写完 fixtures 后,通过执行此命令加载它们
1 2
# when using the ORM
$ php bin/console doctrine:fixtures:load
注意
默认情况下,load
命令会**清除数据库**,删除每个表中的所有数据。要追加 fixtures 的数据,请添加 --append
选项。
此命令查找所有标记为 doctrine.fixture.orm
的服务。如果您正在使用 默认服务配置,任何实现 ORMFixtureInterface
的类(例如,那些从 Fixture
扩展的类)都将自动注册此标签。
要查看该命令的其他选项,请运行
1
$ php bin/console doctrine:fixtures:load --help
从 Fixtures 中访问服务
在某些情况下,您可能需要在 fixtures 类中访问应用程序的服务。没问题!您的 fixtures 类是一个服务,因此您可以使用正常的依赖注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// src/DataFixtures/AppFixtures.php
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
class AppFixtures extends Fixture
{
private UserPasswordHasherInterface $hasher;
public function __construct(UserPasswordHasherInterface $hasher)
{
$this->hasher = $hasher;
}
// ...
public function load(ObjectManager $manager): void
{
$user = new User();
$user->setUsername('admin');
$password = $this->hasher->hashPassword($user, 'pass_1234');
$user->setPassword($password);
$manager->persist($user);
$manager->flush();
}
}
将 Fixtures 分割到单独的文件中
在大多数应用程序中,在一个类中创建所有 fixtures 都可以。这个类最终可能会有点长,但这很值得,因为拥有一个文件有助于保持简单。
如果您确实决定将 fixtures 分割到单独的文件中,Symfony 可以帮助您解决两个最常见的问题:在 fixtures 之间共享对象以及按顺序加载 fixtures。
在 Fixtures 之间共享对象
当使用多个 fixtures 文件时,您可以借助**对象引用**在不同文件之间重用 PHP 对象。使用 addReference()
方法为任何对象命名,然后使用 getReference()
方法通过其名称获取完全相同的对象。
注意
添加对象引用仅适用于 ORM 实体或 ODM 文档。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// src/DataFixtures/UserFixtures.php
// ...
class UserFixtures extends Fixture
{
public const ADMIN_USER_REFERENCE = 'admin-user';
public function load(ObjectManager $manager): void
{
$userAdmin = new User('admin', 'pass_1234');
$manager->persist($userAdmin);
$manager->flush();
// other fixtures can get this object using the UserFixtures::ADMIN_USER_REFERENCE constant
$this->addReference(self::ADMIN_USER_REFERENCE, $userAdmin);
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// src/DataFixtures/GroupFixtures.php
// ...
class GroupFixtures extends Fixture
{
public function load(ObjectManager $manager): void
{
$userGroup = new Group('administrators');
// this reference returns the User object created in UserFixtures
$userGroup->addUser($this->getReference(UserFixtures::ADMIN_USER_REFERENCE, User::class));
$manager->persist($userGroup);
$manager->flush();
}
}
使用引用的唯一注意事项是 fixtures 需要按特定顺序加载(在此示例中,如果 Group
fixtures 在 User
fixtures 之前加载,您将看到错误)。默认情况下,Doctrine 按字母顺序加载 fixture 文件,但您可以按照下一节中解释的方式控制其顺序。
按顺序加载 Fixture 文件
Doctrine 没有定义必须加载所有 fixture 文件的确切顺序,而是使用更智能的方法来确保某些 fixtures 在其他 fixtures 之前加载。实现 DependentFixtureInterface
并向 fixtures 类添加新的 getDependencies()
方法。这将返回必须在此 fixture 类之前加载的 fixture 类数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
// src/DataFixtures/UserFixtures.php
namespace App\DataFixtures;
// ...
class UserFixtures extends Fixture
{
public function load(ObjectManager $manager): void
{
// ...
}
}
// src/DataFixtures/GroupFixtures.php
namespace App\DataFixtures;
// ...
use App\DataFixtures\UserFixtures;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
class GroupFixtures extends Fixture implements DependentFixtureInterface
{
public function load(ObjectManager $manager): void
{
// ...
}
public function getDependencies(): array
{
return [
UserFixtures::class,
];
}
}
Fixture 组:仅执行部分 Fixtures
默认情况下,所有 fixture 类都会被执行。如果您只想执行部分 fixture 类,您可以将它们组织成组。
将 fixture 类组织成组的最简单方法是使您的 fixture 实现 FixtureGroupInterface
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// src/DataFixtures/UserFixtures.php
+ use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
- class UserFixtures extends Fixture
+ class UserFixtures extends Fixture implements FixtureGroupInterface
{
// ...
+ public static function getGroups(): array
+ {
+ return ['group1', 'group2'];
+ }
}
要执行给定组的所有 fixtures,请传递 --group
选项
1 2 3 4
$ php bin/console doctrine:fixtures:load --group=group1
# or to execute multiple groups
$ php bin/console doctrine:fixtures:load --group=group1 --group=group2
或者,除了实现 FixtureGroupInterface
之外,您还可以使用 doctrine.fixture.orm
标记您的服务,并添加一个额外的 group
选项,设置为您的 fixture 应属于的组。
无论是在 fixture 还是服务定义中定义的组,fixture 加载器始终将类的短名称添加为单独的组,以便您可以一次加载单个 fixture。在上面的示例中,您可以使用 UserFixtures
组加载 fixture
1
$ php bin/console doctrine:fixtures:load --group=UserFixtures
指定清除行为
默认情况下,所有先前存在的数据都使用 DELETE FROM table
语句清除。如果您更喜欢使用 TRUNCATE table
语句进行清除,请使用 --purge-with-truncate
。
如果您想排除一组表不被清除,例如,因为您的模式带有预填充的半静态数据,请传递选项 --purge-exclusions
。多次指定 --purge-exclusions
以排除多个表
1
$ php bin/console doctrine:fixtures:load --purge-exclusions=post_category --purge-exclusions=comment_type
您还可以更显著地自定义清除行为,并实现自定义清除器和自定义清除器工厂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
// src/Purger/CustomPurger.php
namespace App\Purger;
use Doctrine\Common\DataFixtures\Purger\PurgerInterface;
// ...
class CustomPurger implements PurgerInterface
{
public function purge(): void
{
// ...
}
}
// src/Purger/CustomPurgerFactory.php
namespace App\Purger;
// ...
use Doctrine\Bundle\FixturesBundle\Purger\PurgerFactory;
class CustomPurgerFactory implements PurgerFactory
{
public function createForEntityManager(?string $emName, EntityManagerInterface $em, array $excluded = [], bool $purgeWithTruncate = false) : PurgerInterface
{
return new CustomPurger($em);
}
}
下一步是注册我们的自定义清除器工厂并指定其别名。
1 2 3 4 5
# config/services.yaml
services:
App\Purger\CustomPurgerFactory:
tags:
- { name: 'doctrine.fixtures.purger_factory', alias: 'my_purger' }
使用 --purger
选项,我们现在可以指定使用 my_purger
而不是 default
清除器。
1
$ php bin/console doctrine:fixtures:load --purger=my_purger
如何从不同的目录加载 Fixtures
默认情况下,fixtures 从 src/DataFixtures
目录加载。在此示例中,我们将从新的 fixtures
目录加载 DataFixtures。
首先,在 composer.json
中使用新的 fixtures
目录添加新的 PSR-4
autoload-entry
1 2 3 4 5 6
"autoload-dev": {
"psr-4": {
"...": "...",
"DataFixtures\\": "fixtures/"
}
},
注意
您需要使用 composer dump-autoload
转储 autoloader
然后,为 fixtures
目录启用依赖注入
1 2 3 4
# config/services.yaml
services:
DataFixtures\:
resource: '../fixtures'
注意
使用 Symfony MakerBundle (make:fixtures
) 创建 fixtures 时,这不会覆盖默认的 src/DataFixtures
目录。