tobento / app-seeding
应用种子支持。
Requires
- php: >=8.0
- tobento/app: ^1.0.7
- tobento/app-console: ^1.0
- tobento/app-migration: ^1.0
- tobento/service-iterable: ^1.0
- tobento/service-seeder: ^1.0.1
- tobento/service-support: ^1.0
Requires (Dev)
- mockery/mockery: ^1.6
- phpunit/phpunit: ^9.5
- tobento/app-user: ^1.0
- vimeo/psalm: ^4.0
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; } }