-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
460 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
<?php | ||
|
||
namespace Illuminate\Database\Console\Factories; | ||
|
||
use Illuminate\Console\GeneratorCommand; | ||
use Illuminate\Support\Str; | ||
use Symfony\Component\Console\Attribute\AsCommand; | ||
use Symfony\Component\Console\Input\InputOption; | ||
|
||
#[AsCommand(name: 'make:factory')] | ||
class FactoryMakeCommand extends GeneratorCommand | ||
{ | ||
/** | ||
* The console command name. | ||
* | ||
* @var string | ||
*/ | ||
protected $name = 'make:factory'; | ||
|
||
/** | ||
* The console command description. | ||
* | ||
* @var string | ||
*/ | ||
protected $description = 'Create a new model factory'; | ||
|
||
/** | ||
* The type of class being generated. | ||
* | ||
* @var string | ||
*/ | ||
protected $type = 'Factory'; | ||
|
||
/** | ||
* Get the stub file for the generator. | ||
* | ||
* @return string | ||
*/ | ||
protected function getStub() | ||
{ | ||
return $this->resolveStubPath('/stubs/factory.stub'); | ||
} | ||
|
||
/** | ||
* Resolve the fully-qualified path to the stub. | ||
* | ||
* @param string $stub | ||
* @return string | ||
*/ | ||
protected function resolveStubPath($stub) | ||
{ | ||
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/'))) | ||
? $customPath | ||
: __DIR__.$stub; | ||
} | ||
|
||
/** | ||
* Build the class with the given name. | ||
* | ||
* @param string $name | ||
* @return string | ||
*/ | ||
protected function buildClass($name) | ||
{ | ||
$factory = class_basename(Str::ucfirst(str_replace('Factory', '', $name))); | ||
|
||
$namespaceModel = $this->option('model') | ||
? $this->qualifyModel($this->option('model')) | ||
: $this->qualifyModel($this->guessModelName($name)); | ||
|
||
$model = class_basename($namespaceModel); | ||
|
||
$namespace = $this->getNamespace( | ||
Str::replaceFirst($this->rootNamespace(), 'Database\\Factories\\', $this->qualifyClass($this->getNameInput())) | ||
); | ||
|
||
$replace = [ | ||
'{{ factoryNamespace }}' => $namespace, | ||
'NamespacedDummyModel' => $namespaceModel, | ||
'{{ namespacedModel }}' => $namespaceModel, | ||
'{{namespacedModel}}' => $namespaceModel, | ||
'DummyModel' => $model, | ||
'{{ model }}' => $model, | ||
'{{model}}' => $model, | ||
'{{ factory }}' => $factory, | ||
'{{factory}}' => $factory, | ||
]; | ||
|
||
return str_replace( | ||
array_keys($replace), array_values($replace), parent::buildClass($name) | ||
); | ||
} | ||
|
||
/** | ||
* Get the destination class path. | ||
* | ||
* @param string $name | ||
* @return string | ||
*/ | ||
protected function getPath($name) | ||
{ | ||
$name = (string) Str::of($name)->replaceFirst($this->rootNamespace(), '')->finish('Factory'); | ||
|
||
return $this->laravel->databasePath().'/factories/'.str_replace('\\', '/', $name).'.php'; | ||
} | ||
|
||
/** | ||
* Guess the model name from the Factory name or return a default model name. | ||
* | ||
* @param string $name | ||
* @return string | ||
*/ | ||
protected function guessModelName($name) | ||
{ | ||
if (str_ends_with($name, 'Factory')) { | ||
$name = substr($name, 0, -7); | ||
} | ||
|
||
$modelName = $this->qualifyModel(Str::after($name, $this->rootNamespace())); | ||
|
||
if (class_exists($modelName)) { | ||
return $modelName; | ||
} | ||
|
||
if (is_dir(app_path('Models/'))) { | ||
return $this->rootNamespace().'Models\Model'; | ||
} | ||
|
||
return $this->rootNamespace().'Model'; | ||
} | ||
|
||
/** | ||
* Get the console command options. | ||
* | ||
* @return array | ||
*/ | ||
protected function getOptions() | ||
{ | ||
return [ | ||
['model', 'm', InputOption::VALUE_OPTIONAL, 'The name of the model'], | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
<?php | ||
|
||
namespace Aerni\Factory\Commands; | ||
|
||
use PhpParser\BuilderFactory; | ||
use Archetype\Facades\PHPFile; | ||
use Archetype\Support\Snippet; | ||
use Statamic\Facades\Taxonomy; | ||
use Statamic\Fields\Blueprint; | ||
use Illuminate\Console\Command; | ||
|
||
use Statamic\Facades\Collection; | ||
|
||
use function Laravel\Prompts\alert; | ||
use function Laravel\Prompts\info; | ||
use Statamic\Console\RunsInPlease; | ||
use function Laravel\Prompts\select; | ||
use function Laravel\Prompts\error; | ||
use function Laravel\Prompts\confirm; | ||
use function Laravel\Prompts\note; | ||
|
||
use Illuminate\Support\Facades\File; | ||
use Illuminate\Support\Facades\Process; | ||
use Aerni\Factory\Factories\DefinitionGenerator; | ||
|
||
class MakeFactory extends Command | ||
{ | ||
use RunsInPlease; | ||
|
||
/** | ||
* The name and signature of the console command. | ||
* | ||
* @var string | ||
*/ | ||
protected $signature = 'statamic:make:factory'; | ||
|
||
/** | ||
* The console command description. | ||
* | ||
* @var string | ||
*/ | ||
protected $description = 'Generate a factory from a blueprint'; | ||
|
||
public function handle() | ||
{ | ||
$type = select( | ||
label: 'Select the type of factory you want to create.', | ||
options: [ | ||
'entry' => 'Entry', | ||
'term' => 'Term', | ||
], | ||
validate: fn (string $value) => match ($value) { | ||
'entry' => Collection::all()->isEmpty() | ||
? 'You need to create at least one collection to use the factory.' | ||
: null, | ||
'term' => Taxonomy::all()->isEmpty() | ||
? 'You need to create at least one taxonomy to use the factory.' | ||
: null, | ||
}, | ||
); | ||
|
||
$model = $this->getModelData($type); | ||
|
||
$classNamespace = 'Database\\Factories\\Statamic\\' . collect([$model['repository'], $model['type']])->map(ucfirst(...))->implode('\\'); | ||
$className = ucfirst($model['blueprint']); | ||
$definition = new DefinitionGenerator($model['blueprint']); | ||
|
||
$stub = preg_replace( | ||
['/\{{ classNamespace \}}/', '/\{{ className \}}/', '/\{{ definition \}}/'], | ||
[$classNamespace, $className, $definition], | ||
File::get(__DIR__.'/stubs/factory.stub') | ||
); | ||
|
||
$classPath = $this->generatePathFromNamespace($classNamespace)."{$className}Factory.php"; | ||
|
||
if (File::exists($classPath) && ! confirm(label: 'This factory already exists. Do you want to override the class?', default: false)) { | ||
return; | ||
} | ||
|
||
// TODO: Make it possible to update the definition of an existing factory class. | ||
|
||
File::ensureDirectoryExists(dirname($classPath)); | ||
File::put($classPath, $stub); | ||
Process::run("./vendor/bin/pint $classPath"); | ||
|
||
info("The factory was successfully created: <comment>{$this->getRelativePath($classPath)}</comment>"); | ||
} | ||
|
||
protected function getModelData(string $type): array | ||
{ | ||
$models = match ($type) { | ||
'entry' => Collection::all(), | ||
'term' => Taxonomy::all(), | ||
}; | ||
|
||
$selectedModel = select( | ||
label: 'Select the collection of the factory.', | ||
options: $models->mapWithKeys(fn ($model) => [$model->handle() => $model->title()]), | ||
); | ||
|
||
$model = $models->firstWhere('handle', $selectedModel); | ||
|
||
$blueprints = match (true) { | ||
($type === 'entry') => $model->entryBlueprints(), | ||
($type === 'term') => $model->termBlueprints(), | ||
}; | ||
|
||
$selectedBlueprint = select( | ||
label: 'Select the blueprint of the factory.', | ||
options: $blueprints->mapWithKeys(fn ($blueprint) => [$blueprint->handle() => $blueprint->title()]), | ||
); | ||
|
||
$blueprint = $blueprints->firstWhere('handle', $selectedBlueprint); | ||
|
||
return [ | ||
'repository' => ($type === 'entry') ? 'collections' : 'taxonomies', | ||
'type' => $selectedModel, | ||
'blueprint' => $blueprint, | ||
]; | ||
} | ||
|
||
protected function generatePathFromNamespace(string $namespace): string | ||
{ | ||
$name = str($namespace)->finish('\\')->replaceFirst(app()->getNamespace(), '')->lower(); | ||
return base_path(str_replace('\\', '/', $name)); | ||
} | ||
|
||
protected function getRelativePath(string $path): string | ||
{ | ||
return str_replace(base_path().'/', '', $path); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
namespace {{ classNamespace }}; | ||
|
||
use Aerni\Factory\Factories\Factory; | ||
|
||
class {{ className }}Factory extends Factory | ||
{ | ||
/** | ||
* Define the model's default state. | ||
* | ||
* @return array<string, mixed> | ||
*/ | ||
public function definition(): array | ||
{ | ||
{{ definition }} | ||
} | ||
} |
Oops, something went wrong.