Added seed commands.

This commit is contained in:
You Ming 2019-09-05 15:39:44 +08:00
parent 9c885f49bf
commit 3b1afc3a2c
8 changed files with 690 additions and 0 deletions

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Database\Commands\Seeders;
use Hyperf\Command\Command;
abstract class BaseCommand extends Command
{
/**
* Get seeder path (either specified by '--path' option or default location).
*
* @return string
*/
protected function getSeederPath()
{
if (! is_null($targetPath = $this->input->getOption('path'))) {
return ! $this->usingRealPath()
? BASE_PATH . '/' . $targetPath
: $targetPath;
}
return BASE_PATH . DIRECTORY_SEPARATOR . 'seeders';
}
/**
* Determine if the given path(s) are pre-resolved "real" paths.
*
* @return bool
*/
protected function usingRealPath()
{
return $this->input->hasOption('realpath') && $this->input->getOption('realpath');
}
}

View File

@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Database\Commands\Seeders;
use Hyperf\Database\Seeders\SeederCreator;
use Hyperf\Utils\Str;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
class GenSeederCommand extends BaseCommand
{
/**
* The seeder creator instance.
*
* @var \Hyperf\Database\Seeders\SeederCreator
*/
protected $creator;
/**
* Create a new seeder generator command instance.
*
* @param \Hyperf\Database\Seeders\SeederCreator $creator
*/
public function __construct(SeederCreator $creator)
{
parent::__construct('gen:seeder');
$this->setDescription('Create a new seeder class');
$this->creator = $creator;
}
/**
* Handle the current command.
*/
public function handle()
{
$name = Str::snake(trim($this->input->getArgument('name')));
$this->writeMigration($name);
}
/**
* Write the seeder file to disk.
*
* @param string $name
*/
protected function writeMigration(string $name)
{
$path = $this->ensureSeederDirectoryAlreadyExist(
$this->getSeederPath()
);
$file = pathinfo($this->creator->create($name, $path), PATHINFO_FILENAME);
$this->info("<info>[INFO] Created Seeder:</info> {$file}");
}
protected function ensureSeederDirectoryAlreadyExist(string $path)
{
if (! file_exists($path)) {
mkdir($path, 0755, true);
}
return $path;
}
protected function getArguments(): array
{
return [
['name', InputArgument::REQUIRED, 'The name of the seeder'],
];
}
protected function getOptions(): array
{
return [
['path', null, InputOption::VALUE_OPTIONAL, 'The location where the seeder file should be created'],
['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided seeder file paths are pre-resolved absolute paths'],
];
}
}

View File

@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Database\Commands\Seeders;
use Hyperf\Command\ConfirmableTrait;
use Hyperf\Database\Seeders\Seed;
use Symfony\Component\Console\Input\InputOption;
class SeedCommand extends BaseCommand
{
use ConfirmableTrait;
/**
* The console command name.
*
* @var string
*/
protected $name = 'db:seed';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Seed the database with records';
/**
* The seed instance.
*
* @var \Hyperf\Database\Seeders\Seed
*/
protected $seed;
/**
* Create a new seed command instance.
*
* @param \Hyperf\Database\Seeders\Seed $seed
*/
public function __construct(Seed $seed)
{
parent::__construct();
$this->seed = $seed;
}
/**
* Handle the current command.
*/
public function handle()
{
if (! $this->confirmToProceed()) {
return;
}
$this->seed->setOutput($this->output);
if ($this->input->hasOption('database')) {
$this->seed->setConnection($this->input->getOption('database'));
}
$this->seed->run([$this->getSeederPath()]);
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['path', null, InputOption::VALUE_OPTIONAL, 'The location where the seeders file stored'],
['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided seeder file paths are pre-resolved absolute paths'],
['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to seed'],
['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'],
];
}
}

View File

@ -0,0 +1,265 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Database\Seeders;
use Hyperf\Database\Connection;
use Hyperf\Database\ConnectionResolverInterface as Resolver;
use Hyperf\Database\Model\Model;
use Hyperf\Database\Schema\Grammars\Grammar;
use Hyperf\Utils\Collection;
use Hyperf\Utils\Filesystem\Filesystem;
use Hyperf\Utils\Str;
use Symfony\Component\Console\Output\OutputInterface;
class Seed
{
/**
* The filesystem instance.
*
* @var \Hyperf\Utils\Filesystem\Filesystem
*/
protected $files;
/**
* The connection resolver instance.
*
* @var \Hyperf\Database\ConnectionResolverInterface
*/
protected $resolver;
/**
* The name of the default connection.
*
* @var string
*/
protected $connection;
/**
* The paths to all of the seeder files.
*
* @var array
*/
protected $paths = [];
/**
* The output interface implementation.
*
* @var OutputInterface
*/
protected $output;
/**
* Create a new seed instance.
*
* @param \Hyperf\Database\ConnectionResolverInterface $resolver
* @param \Hyperf\Utils\Filesystem\Filesystem $files
*/
public function __construct(Resolver $resolver, Filesystem $files)
{
$this->files = $files;
$this->resolver = $resolver;
}
/**
* Run the pending seeders at a given path.
*
* @param array|string $paths
* @param array $options
* @return array
*/
public function run($paths = [], array $options = [])
{
$files = $this->getSeederFiles($paths);
$this->requireFiles($files);
$this->runSeeders($files, $options);
return $files;
}
/**
* Set the default connection name.
*
* @param string $name
*/
public function setConnection(string $name): void
{
if (! is_null($name)) {
$this->resolver->setDefaultConnection($name);
}
$this->connection = $name;
}
/**
* Run an array of seeders.
*
* @param array $seeders
* @param array $options
*/
public function runSeeders(array $seeders, array $options = [])
{
if (count($seeders) === 0) {
return;
}
foreach ($seeders as $file) {
$this->runSeeder($file);
}
}
/**
* Run a seeder.
*
* @param string $file
*/
public function runSeeder($file)
{
Model::unguarded(function () use ($file) {
$seeder = $this->resolve(
$name = $this->getSeederName($file)
);
$this->note("<comment>Seed:</comment> {$name}");
$connection = $this->resolveConnection(
$seeder->getConnection()
);
$callback = function () use ($seeder) {
if (method_exists($seeder, 'run')) {
$seeder->run();
}
};
if ($this->getSchemaGrammar($connection)->supportsSchemaTransactions() && $seeder->withinTransaction) {
$connection->transaction($callback);
} else {
$callback();
}
$this->note("<info>Seeded:</info> {$name}");
});
}
/**
* Resolve a seeder instance from a file.
*
* @param string $file
*
* @return object
*/
public function resolve(string $file): object
{
$class = Str::studly(implode('_', array_slice(explode('_', $file), 4)));
return new $class();
}
/**
* Resolve the database connection instance.
*
* @param string $connection
*
* @return Connection
*/
public function resolveConnection(string $connection)
{
return $this->resolver->connection($connection ?: $this->connection);
}
/**
* Get all of the seeder files in a given path.
*
* @param string|array $paths
* @return array
*/
public function getSeederFiles($paths)
{
return Collection::make($paths)->flatMap(function ($path) {
return Str::endsWith($path, '.php') ? [$path] : $this->files->glob($path.'/*.php');
})->filter()->sortBy(function ($file) {
return $this->getSeederName($file);
})->values()->keyBy(function ($file) {
return $this->getSeederName($file);
})->all();
}
/**
* Get the name of the seeder.
*
* @param string $path
* @return string
*/
public function getSeederName($path)
{
return str_replace('.php', '', basename($path));
}
/**
* Require in all the seeder files in a given path.
*
* @param array $files
*/
public function requireFiles(array $files)
{
foreach ($files as $file) {
$this->files->requireOnce($file);
}
}
/**
* Set the output implementation that should be used by the console.
*
* @param OutputInterface $output
* @return $this
*/
public function setOutput(OutputInterface $output)
{
$this->output = $output;
return $this;
}
/**
* Get the schema grammar out of a migration connection.
*
* @param Connection $connection
*
* @return \Hyperf\Database\Schema\Grammars\Grammar
*/
protected function getSchemaGrammar($connection): Grammar
{
if (is_null($grammar = $connection->getSchemaGrammar())) {
$connection->useDefaultSchemaGrammar();
$grammar = $connection->getSchemaGrammar();
}
return $grammar;
}
/**
* Write a note to the console's output.
*
* @param string $message
* @return void
*/
protected function note($message)
{
if ($this->output) {
$this->output->writeln($message);
}
}
}

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Database\Seeders;
use InvalidArgumentException;
abstract class Seeder
{
/**
* The name of the database connection to use.
*
* @var string|null
*/
protected $connection = 'default';
/**
* Enables, if supported, wrapping the seeder within a transaction.
*
* @var bool
*/
public $withinTransaction = true;
/**
* Get the seeder connection name.
*
* @return string|null
*/
public function getConnection()
{
return $this->connection;
}
}

View File

@ -0,0 +1,138 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Database\Seeders;
use Hyperf\Utils\Filesystem\Filesystem;
use Hyperf\Utils\Str;
use InvalidArgumentException;
class SeederCreator
{
/**
* The filesystem instance.
*
* @var Filesystem
*/
protected $files;
/**
* Create a new seeder creator instance.
*
* @param Filesystem $files
*/
public function __construct(Filesystem $files)
{
$this->files = $files;
}
/**
* Create a new seeder at the given path.
*
* @param string $name
* @param string $path
* @return string
*/
public function create($name, $path)
{
$this->ensureSeederDoesntAlreadyExist($name);
$stub = $this->getStub();
$this->files->put(
$path = $this->getPath($name, $path),
$this->populateStub($name, $stub)
);
return $path;
}
/**
* Get the seeder stub file.
*
* @return string
*/
protected function getStub()
{
return $this->files->get($this->stubPath().'/seeder.stub');
}
/**
* Populate the place-holders in the seeder stub.
*
* @param string $name
* @param string $stub
* @return string
*/
protected function populateStub($name, $stub)
{
return str_replace('DummyClass', $this->getClassName($name), $stub);
}
/**
* Ensure that a seeder with the given name doesn't already exist.
*
* @param string $name
* @return void
*
* @throws \InvalidArgumentException
*/
protected function ensureSeederDoesntAlreadyExist($name)
{
if (class_exists($className = $this->getClassName($name))) {
throw new InvalidArgumentException("A {$className} class already exists.");
}
}
/**
* Get the class name of a seeder name.
*
* @param string $name
* @return string
*/
protected function getClassName($name)
{
return Str::studly($name);
}
/**
* Get the full path to the seeder.
*
* @param string $name
* @param string $path
* @return string
*/
protected function getPath($name, $path)
{
return $path.'/'.$name.'.php';
}
/**
* Get the path to the stubs.
*
* @return string
*/
public function stubPath()
{
return __DIR__.'/stubs';
}
/**
* Get the filesystem instance.
*
* @return \Hyperf\Utils\Filesystem\Filesystem
*/
public function getFilesystem()
{
return $this->files;
}
}

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
use Hyperf\Database\Seeders\Seeder;
class DummyClass extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
//
}
}

View File

@ -21,6 +21,8 @@ use Hyperf\Database\Commands\Migrations\ResetCommand;
use Hyperf\Database\Commands\Migrations\RollbackCommand;
use Hyperf\Database\Commands\Migrations\StatusCommand;
use Hyperf\Database\Commands\ModelCommand;
use Hyperf\Database\Commands\Seeders\GenSeederCommand;
use Hyperf\Database\Commands\Seeders\SeedCommand;
use Hyperf\Database\ConnectionResolverInterface;
use Hyperf\Database\Connectors\ConnectionFactory;
use Hyperf\Database\Connectors\MySqlConnector;
@ -49,6 +51,8 @@ class ConfigProvider
ResetCommand::class,
RollbackCommand::class,
StatusCommand::class,
GenSeederCommand::class,
SeedCommand::class,
],
'scan' => [
'paths' => [