跳到内容

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 目录。

这项工作,包括代码示例,根据 Creative Commons BY-SA 3.0 许可获得许可。
TOC
    版本