Classic Logic

Laravel Artisan Command to Create Custom Classes

Here are the steps to create an artisan command that will generate custom classes and interfaces that follow a pre-determined template.

For Laravel projects I write utility classes and interfaces to abstract away the logic; these files go into subdirectories within app/Utilities/ (of course it can be anything). Let’s say you want to write FooUtility for something. My usual workflow is this:

  1. Create a directory Foo inside app/Utilities

  2. Create two files FooUtility.php and FooUtilityInterface.php inside app/Utilities/Foo/.

  3. Add the few lines of boilerplane needed for classes and interfaces to work. For instance, something like:

       <?php
    
       namespace app/Utilities/Foo;
    
       class FooUtility implements FooUtilityInterface
       {
           //
       }
       
    

    A similar boilerplate is needed for the interface also.

This repetitive manual work should be avoided, especially when you need dozens of utility classes and interfaces. I have run into troubles before with namespace declarations, or the odd hard-to-spot mismatch between the filename, directory name, interface name, class name etc that makes PSR-4 yell. To avoid such mistakes I created an artisan command to create such utility classes and interfaces, and load them with the initial boilerplate. Steps to create an artisan command that would generate files wasn’t immediately obvious from the excellent Laravel documentation and hence this article. These are the general steps:

  1. Create stub files (templates) for the newly generated class
  2. Create an artisan command.
  3. By default, the artisan command class extends Command; replace that with GeneratorCommand which can do all the heavy lifting of generating the files and populating the initial contents.

For my use-case I had to create both an interface as well as a regular class, I created two artisan commands – one to create the interface and one to create the class. From within the command to create the class, I programmatically call the command to create the interface, so that in practice I only have to call a single artisan command to create both the class as well as the interface.

Create stubs for the files

Stub file is the template based on which our artisan command generates whatever it is that need to be generated. I created two stubs – one for the interface and one for the class, and put them in app/CustomStubs.

foo/app/CustomStubs/utilityInterface.stub

<?php

// custom stub

namespace DummyNamespace;

interface DummyClass {
    //
}

foo/app/CustomStubs/utility.stub

<?php

// custom stub

namespace DummyNamespace;

class DummyClass implements DummyClassInterface {
    //
}

DummyClass, DummyNamespace, and DummyClassInterface might seem random; later you’ll see how the GeneratorCommand replaces those specific words with actual class names.

There’s a way to publish built-in stubs (the ones for models, controllers etc) into stubs/ directory in your project, and use that same directory to hold custom stubs. I chose to maintain a separate directory (CustomStubs/ in this case) because it’s unclear to me whether putting our own stubs inside the published directory is idiomatic usage.

Create Artisan Command

This part is easy:

$ php artisan make:command MakeUtilityInterface
Console command created successfully.

This creates a file in app/Console/Commands/MakeUtilityInterface.php with the following contents:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

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

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        return 0;
    }
}

Here are the modifications to be done to this file:

  1. Extend GeneratorCommand instead of Command.
  2. Assign proper values to $signature and $description. The former specifies the syntax of the command, and the latter is a description that shows up when you, for example, run php artisan list.
  3. Declare a protected variable $type – the given value will show up in the console after running the artisan command.
  4. Implement getStub() method – this should return the path to the stub file.
  5. Implement getDefaultNamespace() method – this should return the namespace for the file being generated. The generated file will be placed here.
  6. Remove the handle() function so that the parent class’s handle() function would do all the work.
  7. Remove the constructor because it’s not needed.

After making these changes you should have something like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php

namespace App\Console\Commands;

use Illuminate\Console\GeneratorCommand;
use Illuminate\Support\Str;

class MakeUtilityInterface extends GeneratorCommand
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'make:utilityinterface {name}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Create interface for custom utilities';


    protected $type = 'utility interface'; // shows up in console

    /**
     * Get the stub file for the generator.
     *
     * @return string
     */
    protected function getStub() : string {
        return Str::finish(app_path(), '/') . 'CustomStubs/utilityInterface.stub';
    }
    
    /**
     * Get the default namespace for the class.
     *
     * @param string $rootNamespace
     * @return string
     */
    protected function getDefaultNamespace($rootNamespace) : string {
        // $rootNamespace is "App" usually
        return "$rootNamespace/Utilities";
    }
}

If you run php artisan list, you should see an entry like below:

make:utilityinterface  Create interface for custom utilities

Let’s take it for a spin and create our utility interface:

$ php artisan make:utilityinterface Foo/BazUtilityInterface
utility interface created successfully.

After this command runs, this is what we expect:

  • A new file BazUtilityInterface.php would be created at app/Utilities/Foo/BazUtilityInterface.php
  • The newly created interface would have the name BazUtilityInterface
  • Declared namespace of the BazUtilityInterface would be App\Utilities\Foo.

A new interface would actually have been created at app/Utilities/Foo/BazUtilityInterface.php:

1
2
3
4
5
6
7
8
9
<?php

// custom stub

namespace App\Utilities\Foo;

interface BazUtilityInterface {
    //
}
The namespace, interface-name, filename, and filepath are as expected.

Artisan command to create a utility classe can be written similarly. To avoid having to manually run two artisan commands to separately create the class and interface (which, aside from being an inconvenience, can lead to mistakes), I’m calling the command to make interface from within the handle() method (which we omitted for the previous command).

Assuming you named your command MakeUtility, here’s how MakeUtility.php should look like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php
// app/Console/Commands/MakeUtility.php

namespace App\Console\Commands;

use Illuminate\Console\GeneratorCommand;
use Illuminate\Support\Str;

class MakeUtility extends GeneratorCommand
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'make:utility {name}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Create class for custom utilities';


    protected $type = 'utility class'; // shows up in console

    /**
     * Get the stub file for the generator.
     *
     * @return string
     */
    protected function getStub() : string {
        return Str::finish(app_path(), '/') . 'CustomStubs/utility.stub';
    }
    
    /**
     * Get the default namespace for the class.
     *
     * @param string $rootNamespace
     * @return string
     */
    protected function getDefaultNamespace($rootNamespace) : string {
        // $rootNamespace is "App" usually
        return "$rootNamespace/Utilities";
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle() {
        // call make:utilityinterface with the appropriate name argument
        $this->call('make:utilityinterface', [
            'name' => $this->argument('name') . 'Interface',
        ]);

        parent::handle(); // don't forget to call superclass method
    }
}

Let’s take it for a spin:

$ php artisan make:utility Foo/BazUtility
utility interface created successfully.
utility class created successfully

This would:

  1. Create an interface BazUtilityInterface at app/Utilities/Foo/BazUtilityInterface.php
  2. Create a class BazUtility at app/Utilities/Foo/BazUtility.php

The newly created BazUtility class would have the following content:

1
2
3
4
5
6
7
8
9
<?php

// custom stub

namespace App\Utilities\Foo;

class BazUtility implements BazUtilityInterface {
    //
}


How did I know the keywords to use in stub files?

If you examine the source code of GeneratorCommand class (located at vendor/laravel/framework/src/Illuminate/Console/GeneratorCommand.php), you’ll find:

    /**
     * Replace the namespace for the given stub.
     *
     * @param  string  $stub
     * @param  string  $name
     * @return $this
     */
    protected function replaceNamespace(&$stub, $name)
    {
        $searches = [
            ['DummyNamespace', 'DummyRootNamespace', 'NamespacedDummyUserModel'],
            ['{{ namespace }}', '{{ rootNamespace }}', '{{ namespacedUserModel }}'],
            ['{{namespace}}', '{{rootNamespace}}', '{{namespacedUserModel}}'],
        ];

        foreach ($searches as $search) {
            $stub = str_replace(
                $search,
                [$this->getNamespace($name), $this->rootNamespace(), $this->userProviderModel()],
                $stub
            );
        }

        return $this;
    }

    /**
     * Replace the class name for the given stub.
     *
     * @param  string  $stub
     * @param  string  $name
     * @return string
     */
    protected function replaceClass($stub, $name)
    {
        $class = str_replace($this->getNamespace($name).'\\', '', $name);

        return str_replace(['DummyClass', '{{ class }}', '{{class}}'], $class, $stub);
    }

As you see GeneratorCommand looks for those keywords and replaces them; the replacement works even if DummyClass is within another word like DummyClassInterface.

There exists further possibilities. For instance, I could have set an optional flag that would tell the make:utility command whether or not the interface should be created.

See also