laravel custom command to generate file

Viewed 37

I came across this article on Spatie Actions https://freek.dev/1371-refactoring-to-actions and I would like to make a command to generate the files for me, similar how you can generate Model or livewire components etc.

php artisan make:action PublishPostAction Post

This is as far as I got

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class GenerateActionTemplate extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'make:action {name} {model}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Create a new action file.';

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $name = $this->argument('name');
        $model = $this->argument('model');

        $fileContents = <<<'EOT'
        <?php

        namespace App\Actions;

        class PublishPostAction // name
        {
            private $post;

            public function execute(Post $post) // model
            {
                $this->post = $post;
            }

        }

        EOT;

        $this->info($name . 'has been created successfully!');
    }
}
  • How would I do the logic, so if I do like make:action post.PublishPostAction would create a new folder called post in App\Actions\Post\
  • In the EOT how would I pass in the variables?

Any help or link to an example would be great! I have looked through a few tutorials and was able to scrap this up and laravel docs don't really show any example to generate a new file.

1 Answers

Here is the full code to get this working. You need to create 2 files.

// action.stub

<?php

namespace {{ namespace }};

use {{ namespacedModel }};

class {{ class }}
{
    private ${{ modelVariable }};

    public function execute({{ m }} ${{ modelVariable }})
    {
        $this->{{ modelVariable }} = ${{ modelVariable }};
    }
}

// makeAction.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\GeneratorCommand;
use InvalidArgumentException;
use Symfony\Component\Console\Input\InputOption;

class makeAction extends GeneratorCommand
{
    protected $signature = 'make:action {name} {--m=}';

    protected $description = 'Create action file';

    protected $type = 'Action file';

    protected function getNameInput()
    {
        return str_replace('.', '/', trim($this->argument('name')));
    }
    /**
     * Build the class with the given name.
     *
     * @param  string  $name
     * @return string
     */
    protected function buildClass($name)
    {
        $stub = parent::buildClass($name);

        $model = $this->option('m');

        return $model ? $this->replaceModel($stub, $model) : $stub;
    }

    /**
     * Replace the model for the given stub.
     *
     * @param  string  $stub
     * @param  string  $model
     * @return string
     */
    protected function replaceModel($stub, $model)
    {
        $modelClass = $this->parseModel($model);

        $replace = [
            '{{ namespacedModel }}' => $modelClass,
            '{{ m }}' => class_basename($modelClass),
            '{{ modelVariable }}' => lcfirst(class_basename($modelClass)),
        ];

        return str_replace(
            array_keys($replace), array_values($replace), $stub
        );
    }

    /**
     * Get the fully-qualified model class name.
     *
     * @param  string  $model
     * @return string
     *
     * @throws \InvalidArgumentException
     */
    protected function parseModel($model)
    {
        if (preg_match('([^A-Za-z0-9_/\\\\])', $model)) {
            throw new InvalidArgumentException('Model name contains invalid characters.');
        }

        return $this->qualifyModel($model);
    }

    /**
     * Get the stub file for the generator.
     *
     * @return string
     */
    protected function getStub()
    {
        return  app_path().'/Console/Commands/Stubs/action.stub';
    }



    /**
     * Get the default namespace for the class.
     *
     * @param  string  $rootNamespace
     * @return string
     */
    protected function getDefaultNamespace($rootNamespace)
    {
        return $rootNamespace.'\Actions';
    }

    /**
     * Get the console command arguments.
     *
     * @return array
     */
    protected function getOptions()
    {
        return [
            ['model', 'm', InputOption::VALUE_OPTIONAL, 'Model'],
        ];
    }
}


Command to run

PHP artisan make:action task.AddTaskAction --m=Task

it will create the following file

<?php

namespace App\Actions\task;

use App\Models\Task;

class AddTaskAction
{
    private $task;

    public function execute(Task $task)
    {
        $this->task = $task;
    }
}
Related