tobento/app-seeding

应用种子支持。

1.0.2 2024-08-13 13:36 UTC

This package is auto-updated.

Last update: 2024-09-13 13:48:01 UTC


README

使用 Seeder Service 为应用提供种子支持。

目录

入门

运行此命令以添加应用种子项目的最新版本。

composer require tobento/app-seeding

需求

  • PHP 8.0 或更高版本

文档

应用

如果您使用骨架,请查看 App Skeleton

您还可以查看 App 了解更多关于应用的一般信息。

种子引导

种子引导执行以下操作

  • SeedInterface 实现
  • SeedersInterface 实现
  • 添加种子控制台命令

以下种子器将可用

请注意,没有 资源 被设置为它们可能特定于您的应用需求。因此,种子器主要使用 Lorem Seeder 作为后备。

use Tobento\App\AppFactory;
use Tobento\Service\Seeder\SeedInterface;
use Tobento\App\Seeding\SeedersInterface;

// Create the app
$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'vendor', 'vendor');

// Adding boots
$app->boot(\Tobento\App\Seeding\Boot\Seeding::class);
$app->booting();

// Available Interfaces:
$seed = $app->get(SeedInterface::class);
$seeders = $app->get(SeedersInterface::class);

// Run the app
$app->run();

添加种子资源

您可以通过以下方式添加种子器资源

全局使用应用的 on 方法

use Tobento\App\AppFactory;
use Tobento\Service\Seeder\SeedInterface;
use Tobento\Service\Seeder\Resource;

// Create the app
$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'vendor', 'vendor');
    
// Adding boots
$app->boot(\Tobento\App\Seeding\Boot\Seeding::class);
$app->booting();

// Add resources:
$app->on(SeedInterface::class, function(SeedInterface $seed) {

    $seed->resources()->add(new Resource('countries', 'en', [
        'Usa', 'Switzerland', 'Germany',
    ]));
});

$seed = $app->get(SeedInterface::class);

var_dump($seed->country());
// string(7) "Germany"

// Run the app
$app->run();

针对使用种子的任何服务进行特定配置

use Tobento\Service\Seeder\SeedInterface;
use Tobento\Service\Seeder\Resource;

class ServiceUsingSeed
{
    public function __construct(
        protected SeedInterface $seed,
    ) {
        $seed->resources()->add(new Resource('countries', 'en', [
            'Usa', 'Switzerland', 'Germany',
        ]));
    }
}

工厂

创建工厂

您可以为测试或其他目的创建种子器工厂。

要创建一个工厂,创建一个扩展 AbstractFactory::class 的类,并使用 definition 方法配置您的实体

use Tobento\App\Seeding\AbstractFactory;

class UserFactory extends AbstractFactory
{
    public function definition(): array
    {
        return [
            'firstname' => $this->seed->firstname(),
            'role' => 'admin',
        ];
    }
}

创建实体

您可以使用 createEntity 方法使用定义创建特定的实体。默认情况下,将创建一个 stdClass 类。

use Tobento\App\Seeding\AbstractFactory;

class UserFactory extends AbstractFactory
{
    protected function createEntity(array $definition): object
    {
        return new User(
            firstname: $definition['firstname'],
        );
    }
}

存储实体

默认情况下,实体不会被存储。您可以使用 storeEntity 方法根据定义存储实体。此外,您还可以使用 getService 方法从应用容器中获取任何服务。

use Tobento\App\Seeding\AbstractFactory;

class UserFactory extends AbstractFactory
{
    protected function storeEntity(array $definition): object
    {
        return $this->getService(UserRepositoryInterface::class)
            ->create($definition);
    }
}

使用工厂

可以通过在工厂类上调用 new 方法来创建一个工厂

$factory = UserFactory::new();

// you may overwrite the default definition:
$factory = UserFactory::new(['role' => 'editor']);

制作实体

make 方法创建一个实体数组并将其返回以供进一步在代码中使用,但不将其存储在数据库中,例如。

// make 10 entities:
$users = $factory->times(10)->make();

// make one:
$user = $factory->makeOne();

创建实体

create 方法创建一个实体数组,将它们存储在数据库中(例如),并返回它们以供进一步在代码中使用。

// create 10 entities:
$users = $factory->times(10)->create();

// create one:
$user = $factory->createOne();

原始实体

raw 方法仅从定义创建一个属性数组并将其返回以供进一步在代码中使用。

// create 10:
$attributes = $factory->times(10)->raw();

// create one:
$attributes = $factory->rawOne();

修改定义

您可以使用 modify 方法修改定义

use Tobento\Service\Seeder\SeedInterface;

$users = $factory
    ->modify(fn (SeedInterface $seed, array $definition) => [
        'email' => $seed->email(),
    ])
    ->times(10)
    ->make();

您可以在您的工厂类中使用该方法

use Tobento\App\Seeding\AbstractFactory;
use Tobento\Service\Seeder\SeedInterface;

class UserFactory extends AbstractFactory
{
    public function withEmail(string $email): static
    {
        return $this->modify(fn(SeedInterface $seed, array $definition) => [
            'email' => $email
        ]);
    }
}

$users = $factory
    ->withEmail('admin@example.com')
    ->times(10)
    ->make();

修改实体

您可以使用 modifyEntity 方法通过修改实体

use Tobento\Service\Seeder\SeedInterface;

$users = $factory
    ->modifyEntity(static function (SeedInterface $seed, object $user) {
        return $user->markAsDeleted();
    })
    ->times(10)
    ->make();

您可以在您的工厂类中使用该方法

use Tobento\App\Seeding\AbstractFactory;
use Tobento\Service\Seeder\SeedInterface;

class UserFactory extends AbstractFactory
{
    public function deleted(string $email): static
    {
        return $this->modifyEntity(static function (SeedInterface $seed, object $user) {
            return $user->markAsDeleted();
        });
    }
}

$users = $factory
    ->deleted()
    ->times(10)
    ->make();

种子器

您可以通过创建一个种子器类来轻松地为您的应用程序提供测试数据。

创建种子器

要创建一个种子器,创建一个实现 SeederInterface::class 的类

use Tobento\App\Seeding\SeederInterface;

class UserSeeder implements SeederInterface
{
    public function run(): \Generator
    {
        foreach (UserFactory::new()->times(100)->create() as $user) {
            yield $user;
        }
    }
}

如果您想要种子数百万的测试数据,使用工厂可能不是最快的解决方案。相反,您可以直接使用(如果可用)存储,这要快得多。

使用App User组件的用户存储库的示例

use Tobento\App\Seeding\SeederInterface;
use Tobento\App\User\UserRepositoryInterface;
use Tobento\Service\Repository\Storage\StorageRepository;
use Tobento\Service\Iterable\ItemFactoryIterator;

class UserSeeder implements SeederInterface
{
    public function __construct(
        protected UserRepositoryInterface $userRepository,
    ) {
        if (! $userRepository instanceof StorageRepository) {
            throw new \InvalidArgumentException('Not supported ...');
        }
    }
    
    public function run(): \Generator
    {        
        yield from $this->userRepository
            ->query()
            ->chunk(length: 10000)
            ->insertItems(new ItemFactoryIterator(
                factory: function (): array {
                    return UserFactory::new()->definition();
                },
                create: 1000000,
            ));
    }
}

添加种子器

您可以使用应用控制台中的on方法添加要运行的种子。

use Tobento\App\Seeding\SeedersInterface;

// ...

$app->on(
    SeedersInterface::class,
    static function (SeedersInterface $seeders): void {
        $seeders->addSeeder('users', UserSeeder::class);
    }
);

运行种子器

要运行您添加的种子,请使用seed控制台命令。

php ap seed

通过名称运行特定的种子

php ap seed --name=users

显示已种子的实体

您可以使用详细程度选项显示已种子的实体

php ap seed -v

列出所有种子名称

您可以使用seed:list控制台命令显示种子名称。

php ap seed:list

用户种子

如果您已安装用户应用组件,您可以使用提供的用户工厂和用户种子。

用户工厂

use Tobento\App\Seeding\User\UserFactory;

$user = UserFactory::new()
    ->withEmail('foo@example.com')
    ->withSmartphone('22334455')
    ->withUsername('Username')
    ->withPassword('123456')
    ->withRoleKey('admin') // 'guest' if role does not exist.
    ->withAddress(['firstname' => 'Firstname'])
    ->makeOne();

User 种子器

use Tobento\App\Seeding\User\UserStorageSeeder;

$app->on(
    SeedersInterface::class,
    static function (SeedersInterface $seeders): void {
        $seeders->addSeeder('users', UserStorageSeeder::class);
    }
);

存储库

您可以从实现存储库接口的任何存储库中轻松创建种子工厂种子

创建存储库工厂

使用存储库工厂

通过使用RepositoryFactory::new方法,您可以快速创建一个种子工厂,这在测试目的上可能很有用。

use Tobento\App\Seeding\Repository\RepositoryFactory;
use Tobento\Service\Repository\RepositoryInterface;
use Tobento\Service\Seeder\SeedInterface;
use Tobento\Service\Seeder\Lorem;

$factory = RepositoryFactory::new(
    repository: MyProductRepository::class, // string|RepositoryInterface
    definition: function (SeedInterface $seed): array {
        return [
            'sku' => Lorem::word(number: 1),
            'desc' => Lorem::sentence(number: 2),
        ];
    }
);

// using the factory:
$products = $factory->times(10)->make();

存储存储库具有定义的,将根据列自动创建定义。您根本不需要设置定义。

use Tobento\App\Seeding\Repository\RepositoryFactory;

$factory = RepositoryFactory::new(repository: MyProductRepository::class);

// using the factory:
$products = $factory->times(10)->make();

查看使用工厂部分,获取更多有关一般使用工厂的信息。

使用抽象工厂

要创建一个工厂,创建一个扩展AbstractFactory::class的类,并使用REPOSITORY常量定义您的存储库类。

use Tobento\App\Seeding\Repository\AbstractFactory;

class ProductFactory extends AbstractFactory
{
    public const REPOSITORY = MyProductRepository::class;
}

// using the factory:
$products = ProductFactory::new()->times(10)->make();

当然,您可以根据需要自定义定义以获得更大的灵活性。

use Tobento\App\Seeding\Repository\AbstractFactory;

class ProductFactory extends AbstractFactory
{
    public function definition(): array
    {
        // you may call the parent if you have
        // repositories with columns so it will
        // automatically create the definition
        // based on the columns.
        $definition = parent::definition();
        
        // and just overwrite if needed:
        $definition['name'] = $this->seed->firstname();
        
        return $definition;
    }
}

致谢