Advertisement

Your One-Stop Guide to Laravel Commands

by

In this day and age, it's quite normal for a developer to have an understanding of consoles, and how to issue basic commands. But what if you could code your own custom commands to improve your workflow? If we look back to Laravel 3, you might remember that it offered tasks. Tasks were extremely helpful, but still came up short for more complex operations. Thankfully, Laravel 4 packs a beefed up Artisan that will make your life as a developer so much easier!


What is Artisan?

Artisan is the command line utility that was released in Laravel 3.

If you're not familiar with Laravel, then you might not be aware of Artisan. Artisan is the command line utility that was released in Laravel 3. If you've used other frameworks, you can compare Artisan to Oil in FuelPHP, ZFTool in Zend, or the Console in Symfony 2.

Artisan offers many useful commands that can help you perform various tasks, such as generating migrations or publishing a package's assets. In addition to the built-in commands, you can extend Artisan with your own commands.


Artisan's Foundations

This is where Artisan draws its power.

In Laravel 3, Artisan was written from scratch by Taylor Otwell (Laravel's creator), thus it was quite basic (although still awesome). Now that Laravel 4 is Composer based, it can use existing packages developed by other brilliant developers. As a result, Laravel 4 now depends on numerous packages from the Symfony framework. One of these such packages is the excellent Console Component.

If we take a look at the source of the Artisan application at Illuminate\Console\Application, we can see that the class, itself, extends Symfony\Component\Console\Application. This is where Artisan draws its power. Although Artisan makes use of Symfony's Console component, a lot of the common methods have been given more fluent Laravel-like aliases. So don't worry, it'll still feel like you're developing with Laravel!


Common Questions

Two questions typically pop up, when attempting to develop a new command.

Q Where should I put commands?

When you install a copy of Laravel, you'll find a predefined directory at app/commands. This directory is also in the classmap of your composer.json by default. This means that, once you've created a command, you'll need to run composer dump-autoload to generate an updated autoload file. If you don't, you'll receive errors, complaining that your command cannot be found.

If you're developing a package, you'll need to create a directory within your packages src/<vendor>/<package> directory to hold your commands. Throughout the Laravel 4 codebase, this directory is named Console. Remember to ensure that the directory is autoloaded in your packages composer.json.

Q How should I name commands?

Throughout the Laravel 4 codebase, all commands are suffixed with Command, and are named after the task that they perform. Let's say, for example, that you have a command that clears your cache. You might name this command, CacheClearCommand.


Basics of a Command

A command should perform a single task. Throughout the rest of this article, we're going to develop a user generator command. Let's review the basics of a command.

// app/commands/UserGeneratorCommand.php

<?php

use Illuminate\Console\Command;

class UserGeneratorCommand extends Command {

    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'user:generate';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = "Generate a new user";

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function fire()
    {
        $this->line('Welcome to the user generator.');
    }

}

Laravel can generate this boilerplate code for you! Simply run:

$ php artisan command:make UserGeneratorCommand

This will create a skeleton command for you to modify, however, for the purposes of this tutorial, we'll go through the process of building a command from scratch, so that we can learn each piece of the process.

Command Name

All commands need to provide a name. This name is used to run the command from the console, and should describe the task that the command performs. While there is no convention for how your command is named, you might consider one of the following: namespace:group/command, namespace:command, or just command.

Command Description

All commands need to provide a description. The description is used when retrieving a list of available commands from Artisan, and when viewing the help docs for a command. Descriptions should briefly describe the task that the command performs.

If we were to open our console and fetch a list of available commands, we still won't be able to see our command listed.

$ php artisan list
Laravel Framework version 4.0.0

Usage:
  [options] command [arguments]

Options:
  --help           -h Display this help message.
  --quiet          -q Do not output any message.
  --verbose        -v Increase verbosity of messages.
  --version        -V Display this application version.
  --ansi              Force ANSI output.
  --no-ansi           Disable ANSI output.
  --no-interaction -n Do not ask any interactive question.
  --env               The environment the command should run under.

Available commands:
  help               Displays help for a command
  list               Lists commands
  migrate            Run the database migrations
  serve              Serve the application on the PHP development server
  tinker             Interact with your application
  workbench          Create a new package workbench
asset
  asset:publish      Publish a package's assets to the public directory
auth
  auth:reminders     Create a migration for the password reminders table
command
  command:make       Create a new Artisan command
config
  config:publish     Publish a package's configuration to the application
controller
  controller:make    Create a new resourceful controller
db
  db:seed            Seed the database with records
key
  key:generate       Set the application key
migrate
  migrate:install    Create the migration repository
  migrate:make       Create a new migration file
  migrate:refresh    Reset and re-run all migrations
  migrate:reset      Rollback all database migrations
  migrate:rollback   Rollback the last database migration
queue
  queue:listen       Listen to a given queue
  queue:work         Process the next job on a queue
session
  session:table      Create a migration for the session database table

To register our new command, open app/start/artisan.php and have a quick read through the default comment block that's in there. When we run Artisan from the console, this file is included; we'll be using it to bootstrap our commands. In this file, we have access to an $artisan variable that was declared prior to the file being included. Remember the Artisan application class that we looked at earlier? The one that extended the Symfony Console component? Well, $artisan is an instance of that class.

Let's add our command to make it available in the console.

$artisan->add(new UserGeneratorCommand);

Or, if you prefer the static syntax:

Artisan::add(new UserGeneratorCommand);

The add method accepts a command instance. Once our command has been added, we can access it from the console.

$ php artisan user:generate
Welcome to the user generator.

The command should also be listed in the available commands, as well as the help information.

$ php artisan list
$ php artisan user:generate --help
Usage:
 user:generate

Options:
 --help (-h)           Display this help message.
 --quiet (-q)          Do not output any message.
 --verbose (-v)        Increase verbosity of messages.
 --version (-V)        Display this application version.
 --ansi                Force ANSI output.
 --no-ansi             Disable ANSI output.
 --no-interaction (-n) Do not ask any interactive question.
 --env                 The environment the command should run under.

If you receive any errors, ensure that you run composer dump-autoload from your applications root, after creating the command.


Colored Output

Outputting colored text to the console is a cinch in Artisan. There are four different helper methods to echo colored ANSI output.

$this->info("This is some information.");

$this->comment("This is a comment.");

$this->question("This is a question.");

$this->error("This is an error.");

Options and Arguments

An exciting new feature to Artisan is the ability to provide a command with options and arguments.

Arguments

Arguments are strings that you send through to a command. They must be given to a command in the order that they are defined. Consider the following command:

$ php artisan user:generate [name] [email]

The name argument must be specified before the email argument.

Arguments can be defined as optional.

Options

Options are always optional, and are prefixed with two dashes, when provided.

$ php artisan user:generate --admin

Aside from being used as boolean switches, an option can be configured to accept a value or an array of values.

$ php artisan user:generate --name=Jason
$ php artisan user:generate --role=user --role=editor

Defining Options and Arguments

Before we define our options and arguments, it's best to import the required Symfony classes (they're long and would be a pain to write out all the time). The two classes we need are Symfony\Component\Console\Input\InputOption and Symfony\Component\Console\Input\InputArgument.

Above our class declaration, we'll import both classes.

// app/commands/UserGenerateCommand.php

<?php

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;

class UserGeneratorCommand extends Command {

    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'user:generate';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = "Generate a new user";

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function fire()
    {
        $this->line('Welcome to the user generator.');
    }

}

To define the options and arguments, you need to create two new methods: getArguments and getOptions. Both of these methods return an array of arguments or options. Let's make our command accept a name argument, and an age option.

// app/commands/UserGenerateCommand.php

<?php

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;

class UserGeneratorCommand extends Command {

    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'user:generate';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = "Generate a new user";

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function fire()
    {
        $this->line('Welcome to the user generator.');

        // Get the name arguments and the age option from the input instance.
        $name = $this->argument('name');

        $age = $this->option('age');

        $this->line("{$name} is {$age} years old.");
    }

    /**
     * Get the console command arguments.
     *
     * @return array
     */
    protected function getArguments()
    {
        return array(
            array('name', InputArgument::REQUIRED, 'Name of the new user'),
        );
    }

    /**
     * Get the console command options.
     *
     * @return array
     */
    protected function getOptions()
    {
        return array(
            array('age', null, InputOption::VALUE_REQUIRED, 'Age of the new user')
        );
    }

}

Remember: Laravel can generate all of this boilerplate code for you. We're simply doing it manually for the purposes of reviewing each line in the class.

Now, we can provide a name argument and an age option from our console.

$ php artisan user:generate Jason --age=22
Jason is 22 years old.

Both arguments and options are defined as multi-dimensional arrays. Let's take a closer look at the definitions for each of them.

Argument Definitions

The array definition for an argument accepts four keys, with only the first one (the arguments name) being required. The second key is the arguments mode and should be either InputArgument::OPTIONAL or InputArgument::REQUIRED. The third is the description, and the fourth key is a default value, if the mode is set to InputArgument::OPTIONAL.

Here's an argument using all the array keys.

array('name', InputArgument::OPTIONAL, 'Name of the new user', 'Jason')

Option Definitions

The array definition for an option accepts five keys with only the first (the options name) being required. The second key is a shortcut for the option (-a for example). The third is the options mode and can be one of the following values: InputOption::VALUE_NONE, InputOption::VALUE_REQUIRED, InputOption::VALUE_OPTIONAL, or InputOption::VALUE_IS_ARRAY. The fourth key is the options description, and the fifth key is a default value, if the mode is not InputOption::VALUE_NONE or InputOption::VALUE_REQUIRED.

Here's an option, using all of the array keys.

array('age', 'a', InputOption::VALUE_OPTIONAL, 'Age of the new user', 22)

You can also combine the InputOption::VALUE_IS_ARRAY mode with InputOption::VALUE_REQUIRED or InputOption::VALUE_OPTIONAL.

array('role', 'r', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Roles of the new user', 'user')

Confirmations and Questions

Yet another exciting new feature to Artisan is its ability to request confirmation, or even ask the user a question. This makes developing interactive commands as simple as possible.

Confirmations

Using confirm, we can ask a user a question, and make them confirm with either "yes" or "no." Let's confirm that the user has entered their age correctly.

// app/commands/UserGenerateCommand.php

<?php

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;

class UserGeneratorCommand extends Command {

    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'user:generate';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = "Generate a new user";

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function fire()
    {
        $this->line('Welcome to the user generator.');

        // Get the name arguments and the age option from the input instance.
        $name = $this->argument('name');

        $age = $this->option('age');

        if ( ! $this->confirm("Are you really {$age} years old? [yes|no]", true))
        {
            $this->comment('Then why did you say you were!?');

            return;
        }

        $this->comment("{$name} is {$age} years old.");
    }

    /**
     * Get the console command arguments.
     *
     * @return array
     */
    protected function getArguments()
    {
        return array(
            array('name', InputArgument::REQUIRED, 'Name of the new user'),
        );
    }

    /**
     * Get the console command options.
     *
     * @return array
     */
    protected function getOptions()
    {
        return array(
            array('age', null, InputOption::VALUE_REQUIRED, 'Age of the new user', null)
        );
    }

}

The first parameter is the question that you'd like to ask, and the second is the default value, if a user hits enter without typing anything.

Questions

Using ask, we can ask a user a question and expect a response from them. Instead of killing our command if the user specifies that they entered their age incorrectly, let's instead ask them to enter it again.

// app/commands/UserGenerateCommand.php

<?php

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;

class UserGeneratorCommand extends Command {

    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'user:generate';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = "Generate a new user";

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function fire()
    {
        $this->line('Welcome to the user generator.');

        // Get the name arguments and the age option from the input instance.
        $name = $this->argument('name');

        $age = $this->option('age');

        // Confirm the user entered their age correctly and if they haven't we'll
        // ask them to enter it again.
        if ( ! $this->confirm("Are you really {$age} years old? [yes|no]", true))
        {
            $age = $this->ask('So how old are you then?');
        }

        $this->comment("{$name} is {$age} years old.");
    }

    /**
     * Get the console command arguments.
     *
     * @return array
     */
    protected function getArguments()
    {
        return array(
            array('name', InputArgument::REQUIRED, 'Name of the new user'),
        );
    }

    /**
     * Get the console command options.
     *
     * @return array
     */
    protected function getOptions()
    {
        return array(
            array('age', null, InputOption::VALUE_REQUIRED, 'Age of the new user', null)
        );
    }

}

Command Dependencies

Dependency injection is an important step to ensure that your code is testable and future proof. Let's take our command a step further by injecting a model instance, so that we can generate the user. We'll begin by creating a user interface and user model.

// app/models/UserInterface.php

<?php

interface UserInterface {

}

Our UserInterface doesn't actually define any method implementations, as this is just an example. For a real world application, you'd define the methods you'd expect on your model.

// app/models/User.php

<?php

class User extends Eloquent implements UserInterface {

    /**
     * The database table used by the model.
     *
     * @var string
     */
    protected $table = 'users';

}

Now that we have our User model implementing our UserInterface, we can continue to setup our dependency in our command. I'm going to add a bit more to our generate command, and interact with the User model that's injected.

// app/commands/UserGenerateCommand.php

<?php

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;

class UserGeneratorCommand extends Command {

    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'user:generate';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = "Generate a new user";

    /**
     * User model instance.
     * 
     * @var UserInterface
     */
    protected $user;

    /**
     * Create a new UserGeneratorCommand instance.
     * 
     * @param  UserInterface  $user
     * @return void
     */
    public function __construct(UserInterface $user)
    {
        $this->user = $user;
    }

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function fire()
    {
        $this->line('Welcome to the user generator.');

        // Get the name arguments and the age option from the input instance.
        $this->user->name = $this->argument('name');

        $this->user->age = $this->option('age');

        // Confirm the user entered their age correctly and if they haven't we'll
        // ask them to enter it again. We'll continue asking them until they are
        // sure they've entered the right age.
        $correctAge = false;

        while ( ! $correctAge)
        {
            if ( ! $this->confirm("Are you really {$this->user->age} years old? [yes|no]", true))
            {
                $this->user->age = $this->ask('So how old are you then?');
            }
            else
            {
                $correctAge = true;
            }
        }

        $this->user->framework = $this->ask('What is your favorite framework?', 'Laravel');

        $this->user->website = $this->ask('What is your website address?');

        // Save the user to the database.
        $this->user->save();

        // Report that the user has been saved.
        $this->info("{$this->user->name} has been generated and saved.");
    }

    /**
     * Get the console command arguments.
     *
     * @return array
     */
    protected function getArguments()
    {
        return array(
            array('name', InputArgument::REQUIRED, 'Name of the new user'),
        );
    }

    /**
     * Get the console command options.
     *
     * @return array
     */
    protected function getOptions()
    {
        return array(
            array('age', null, InputOption::VALUE_REQUIRED, 'Age of the new user', null)
        );
    }

}

The first thing you should notice is that the command now has a constructor. This constructor accepts a single parameter and we've type hinted the UserInterface, so we know that the class we get implements the methods defined on the interface. Command constructors should also call the parent constructor.

In the fire method of the command, we're directly assigning the properties on the User model instance. We're also using a loop to continue asking the user if they have entered their age correctly. Lastly, the user is saved to the database and we output to the console that the user was generated and saved.

But wait! Before we can use the command, we need to inject an instance of our User model.

// app/start/artisan.php

$user = new User;

$artisan->add(new UserGeneratorCommand($user));

If you have a database setup and configured correctly, you should now be able to run the command and have a new user saved to the database!


Package Commands

If you're developing a package in Laravel, you might want to include commands. Registering commands from packages is mostly the same process, except you don't (or can't) add the command in app/start/artisan.php. Instead, you resolve them with Artisan from within your packages service provider.

// path/to/your/PackageServiceProvider.php

/**
 * Register the service provider.
 *
 * @return void
 */
public function register()
{
    $this->app['command.package.command'] = $this->app->share(function($app)
    {
        return new PackageCommand($app['dependency']);
    });

    $this->commands('command.package.command');
}

The commands method can accept any number of arguments, and will resolve the command out of the application container when Artisan is started.


Conclusion

When you compare Artisan in Laravel 4 to its Laravel 3 counterpart, you'll quickly find that the improvements are monumental. Commands can now be bound to the IoC container and include dependency injection, provide colored ANSI output, use arguments and options, and request user interaction.

The power of Artisan, thanks to Symfony's Console component, is unbelievable. Commands are going to play a huge role, as we move forward - so get on board early!