16 min read

In this article by Martin Bean, author of the book Laravel 5 Essentials, we will see how Laravel’s command-line utility has far more capabilities and can be used to run and automate all sorts of tasks. In the next pages, you will learn how Artisan can help you:

  • Inspect and interact with your application
  • Enhance the overall performance of your application
  • Write your own commands

By the end of this tour of Artisan’s capabilities, you will understand how it can become an indispensable companion in your projects.

(For more resources related to this topic, see here.)

Keeping up with the latest changes

New features are constantly being added to Laravel. If a few days have passed since you first installed it, try running a composer update command from your terminal. You should see the latest versions of Laravel and its dependencies being downloaded. Since you are already in the terminal, finding out about the latest features is just one command away:

$ php artisan changes

This saves you from going online to find a change log or reading through a long history of commits on GitHub. It can also help you learn about features that you were not aware of. You can also find out which version of Laravel you are running by entering the following command:

$ php artisan --version
Laravel Framework version 5.0.16

All Artisan commands have to be run from your project’s root directory.

With the help of a short script such as Artisan Anywhere, available at https://github.com/antonioribeiro/artisan-anywhere, it is also possible to run Artisan from any subfolder in your project.

Inspecting and interacting with your application

With the route:list command, you can see at a glance which URLs your application will respond to, what their names are, and if any middleware has been registered to handle requests. This is probably the quickest way to get acquainted with a Laravel application that someone else has built.

To display a table with all the routes, all you have to do is enter the following command:

$ php artisan route:list

In some applications, you might see /{v1}/{v2}/{v3}/{v4}/{v5} appended to particular routes. This is because the developer has registered a controller with implicit routing, and Laravel will try to match and pass up to five parameters to the controller.

Fiddling with the internals

When developing your application, you will sometimes need to run short, one-off commands to inspect the contents of your database, insert some data into it, or check the syntax and results of an Eloquent query. One way you could do this is by creating a temporary route with a closure that is going to trigger these actions. However, this is less than practical since it requires you to switch back and forth between your code editor and your web browser.

To make these small changes easier, Artisan provides a command called tinker, which boots up the application and lets you interact with it. Just enter the following command:

$ php artisan tinker

This will start a Read-Eval-Print Loop (REPL) similar to what you get when running the php -a command, which starts an interactive shell. In this REPL, you can enter PHP commands in the context of the application and immediately see their output:

> $cat = 'Garfield';
> AppCat::create(['name' => $cat,'date_of_birth' => new DateTime]);
> echo AppCat::whereName($cat)->get();
[{"id":"4","name":"Garfield 2","date_of_birth":…}]
> dd(Config::get('database.default'));

Version 5 of Laravel leverages PsySH, a PHP-specific REPL that provides a more robust shell with support for keyboard shortcuts and history.

Turning the engine off

Whether it is because you are upgrading a database or waiting to push a fix for a critical bug to production, you may want to manually put your application on hold to avoid serving a broken page to your visitors. You can do this by entering the following command:

$ php artisan down

This will put your application into maintenance mode. You can determine what to display to users when they visit your application in this mode by editing the template file at resources/views/errors/503.blade.php (since maintenance mode sends an HTTP status code of 503 Service Unavailable to the client). To exit maintenance mode, simply run the following command:

$ php artisan up

Fine-tuning your application

For every incoming request, Laravel has to load many different classes and this can slow down your application, particularly if you are not using a PHP accelerator such as APC, eAccelerator, or XCache. In order to reduce disk I/O and shave off precious milliseconds from each request, you can run the following command:

$ php artisan optimize

This will trim and merge many common classes into one file located inside storage/framework/compiled.php. The optimize command is something you could, for example, include in a deployment script.

By default, Laravel will not compile your classes if app.debug is set to true. You can override this by adding the –force flag to the command but bear in mind that this will make your error messages less readable.

Caching routes

Apart from caching class maps to improve the response time of your application, you can also cache the routes of your application. This is something else you can include in your deployment process. The command? Simply enter the following:

$ php artisan route:cache

The advantage of caching routes is that your application will get a little faster as its routes will have been pre-compiled, instead of evaluating the URL and any matches routes on each request. However, as the routing process now refers to a cache file, any new routes added will not be parsed. You will need to re-cache them by running the route:cache command again. Therefore, this is not suitable during development, where routes might be changing frequently.

Generators

Laravel 5 ships with various commands to generate new files of different types. If you run $ php artisan list under the make namespace, you will find the following entries:

  • make:command
  • make:console
  • make:controller
  • make:event
  • make:middleware
  • make:migration
  • make:model
  • make:provider
  • make:request

These commands create a stub file in the appropriate location in your Laravel application containing boilerplate code ready for you to get started with. This saves keystrokes, creating these files from scratch. All of these commands require a name to be specified, as shown in the following command:

$ php artisan make:model Cat

This will create an Eloquent model class called Cat at app/Cat.php, as well as a corresponding migration to create a cats table. If you do not need to create a migration when making a model (for example, if the table already exists), then you can pass the –no-migration option as follows:

$ php artisan make:model Cat --no-migration

A new model class will look like this:

From here, you can define your own properties and methods.

The other commands may have options. The best way to check is to append –help after the command name, as shown in the following command:

$ php artisan make:command --help

You will see that this command has –handler and –queued options to modify the class stub that is created.

Rolling out your own Artisan commands

At this stage you might be thinking about writing your own bespoke commands. As you will see, this is surprisingly easy to do with Artisan. If you have used Symfony’s Console component, you will be pleased to know that an Artisan command is simply an extension of it with a slightly more expressive syntax. This means the various helpers will prompt for input, show a progress bar, or format a table, are all available from within Artisan.

The command that we are going to write depends on the application we built. It will allow you to export all cat records present in the database as a CSV with or without a header line. If no output file is specified, the command will simply dump all records onto the screen in a formatted table.

Creating the command

There are only two required steps to create a command. Firstly, you need to create the command itself, and then you need to register it manually.

We can make use of the following command to create a console command we have seen previously:

$ php artisan make:console ExportCatsCommand

This will generate a class inside app/Console/Commands. We will then need to register this command with the console kernel, located at app/Console/Kernel.php:

protected $commands = [
'AppConsoleCommandsExportCatsCommand',
];

If you now run php artisan, you should see a new command called command:name. This command does not do anything yet. However, before we start writing the functionality, let’s briefly look at how it works internally.

The anatomy of a command

Inside the newly created command class, you will find some code that has been generated for you. We will walk through the different properties and methods and see what their purpose is.

The first two properties are the name and description of the command. Nothing exciting here, this is only the information that will be shown in the command line when you run Artisan. The colon is used to namespace the commands, as shown here:

protected $name = 'export:cats';
 
protected $description = 'Export all cats';

Then you will find the fire method. This is the method that gets called when you run a particular command. From there, you can retrieve the arguments and options passed to the command, or run other methods.

public function fire()

Lastly, there are two methods that are responsible for defining the list of arguments or options that are passed to the command:

protected function getArguments() { /* Array of arguments */ }
protected function getOptions() { /* Array of options */ }

Each argument or option can have a name, a description, and a default value that can be mandatory or optional. Additionally, options can have a shortcut.

To understand the difference between arguments and options, consider the following command, where options are prefixed with two dashes:

$ command --option_one=value --option_two -v=1 argument_one 
argument_two

In this example, option_two does not have a value; it is only used as a flag. The -v flag only has one dash since it is a shortcut. In your console commands, you’ll need to verify any option and argument values the user provides (for example, if you’re expecting a number, to ensure the value passed is actually a numerical value).

Arguments can be retrieved with $this->argument($arg), and options—you guessed it—with $this->option($opt). If these methods do not receive any parameters, they simply return the full list of parameters. You refer to arguments and options via their names, that is, $this->argument(‘argument_name’);.

Writing the command

We are going to start by writing a method that retrieves all cats from the database and returns them as an array:

protected function getCatsData() {
$cats = AppCat::with('breed')->get();
foreach ($cats as $cat) {
   $output[] = [
     $cat->name,
     $cat->date_of_birth,
     $cat->breed->name,
   ];
}
return $output;
}

There should not be anything new here. We could have used the toArray() method, which turns an Eloquent collection into an array, but we would have had to flatten the array and exclude certain fields.

Then we need to define what arguments and options our command expects:

protected function getArguments() {
return [
   ['file', InputArgument::OPTIONAL, 'The output file', null],
];
}

To specify additional arguments, just add an additional element to the array with the same parameters:

return [
['arg_one', InputArgument::OPTIONAL, 'Argument 1', null],
['arg_two', InputArgument::OPTIONAL, 'Argument 2', null],
];

The options are defined in a similar way:

protected function getOptions() {
return [
   ['headers', 'h', InputOption::VALUE_NONE, 'Display headers?',
   null], ]; }

The last parameter is the default value that the argument and option should have if it is not specified. In both the cases, we want it to be null.

Lastly, we write the logic for the fire method:

public function fire() {
$output_path = $this->argument('file');
 
$headers = ['Name', 'Date of Birth', 'Breed'];
$rows = $this->getCatsData();
 
if ($output_path) {
   $handle = fopen($output_path, 'w');
     if ($this->option('headers')) {
       fputcsv($handle, $headers);
     }
     foreach ($rows as $row) {
       fputcsv($handle, $row);
     }
     fclose($handle);
 
} else {
       $table = $this->getHelperSet()->get('table');
       $table->setHeaders($headers)->setRows($rows);
       $table->render($this->getOutput());
   }
}

While the bulk of this method is relatively straightforward, there are a few novelties. The first one is the use of the $this->info() method, which writes an informative message to the output. If you need to show an error message in a different color, you can use the $this->error() method.

Further down in the code, you will see some functions that are used to generate a table. As we mentioned previously, an Artisan command extends the Symfony console component and, therefore, inherits all of its helpers. These can be accessed with $this->getHelperSet(). Then it is only a matter of passing arrays for the header and rows of the table, and calling the render method.

To see the output of our command, we will run the following command:

$ php artisan export:cats
$ php artisan export:cats --headers file.csv

Scheduling commands

Traditionally, if you wanted a command to run periodically (hourly, daily, weekly, and so on), then you would have to set up a Cron job in Linux-based environments, or a scheduled task in Windows environments. However, this comes with drawbacks. It requires the user to have server access and familiarity with creating such schedules. Also, in cloud-based environments, the application may not be hosted on a single machine, or the user might not have the privileges to create Cron jobs. The creators of Laravel saw this as something that could be improved, and have come up with an expressive way of scheduling Artisan tasks.

Your schedule is defined in app/Console/Kernel.php, and with your schedule being defined in this file, it has the added advantage of being present in source control.

If you open the Kernel class file, you will see a method named schedule. Laravel ships with one by default that serves as an example:

$schedule->command('inspire')->hourly();

If you’ve set up a Cron job in the past, you will see that this is instantly more readable than the crontab equivalent:

0 * * * * /path/to/artisan inspire

Specifying the task in code also means we can easily change the console command to be run without having to update the crontab entry.

By default, scheduled commands will not run. To do so, you need a single Cron job that runs the scheduler each and every minute:

* * * * * php /path/to/artisan schedule:run 1>> /dev/null 2>&1

When the scheduler is run, it will check for any jobs whose schedules match and then runs them. If no schedules match, then no commands are run in that pass.

You are free to schedule as many commands as you wish, and there are various methods to schedule them that are expressive and descriptive:

$schedule->command('foo')->everyFiveMinutes();
$schedule->command('bar')->everyTenMinutes();
$schedule->command('baz')->everyThirtyMinutes();
$schedule->command('qux')->daily();

You can also specify a time for a scheduled command to run:

$schedule->command('foo')->dailyAt('21:00');

Alternatively, you can create less frequent scheduled commands:

$schedule->command(‘foo’)->weekly(); $schedule->command(‘bar’)->weeklyOn(1, ’21:00′);

The first parameter in the second example is the day, with 0 representing Sunday, and 1 through 6 representing Monday through Saturday, and the second parameter is the time, again specified in 24-hour format. You can also explicitly specify the day on which to run a scheduled command:

$schedule->command('foo')->mondays();
$schedule->command('foo')->tuesdays();
$schedule->command('foo')->wednesdays();
// And so on
$schedule->command('foo')->weekdays();

If you have a potentially long-running command, then you can prevent it from overlapping:

$schedule->command('foo')->everyFiveMinutes() 
         ->withoutOverlapping();

Along with the schedule, you can also specify the environment under which a scheduled command should run, as shown in the following command:

$schedule->command('foo')->weekly()->environments('production');

You could use this to run commands in a production environment, for example, archiving data or running a report periodically.

By default, scheduled commands won’t execute if the maintenance mode is enabled. This behavior can be easily overridden:

$schedule->command('foo')->weekly()->evenInMaintenanceMode();

Viewing the output of scheduled commands

For some scheduled commands, you probably want to view the output somehow, whether that is via e-mail, logged to a file on disk, or sending a callback to a pre-defined URL. All of these scenarios are possible in Laravel.

To send the output of a job via e-mail by using the following command:

$schedule->command('foo')->weekly()
         ->emailOutputTo('someone@example.com');

If you wish to write the output of a job to a file on disk, that is easy enough too:

$schedule->command('foo')->weekly()->sendOutputTo($filepath);

You can also ping a URL after a job is run:

$schedule->command('foo')->weekly()->thenPing($url);

This will execute a GET request to the specified URL, at which point you could send a message to your favorite chat client to notify you that the command has run.

Finally, you can chain the preceding command to send multiple notifications:

$schedule->command('foo')->weekly()
         ->sendOutputTo($filepath)
         ->emailOutputTo('someone@example.com');

However, note that you have to send the output to a file before it can be e-mailed if you wish to do both.

Summary

In this article, you have learned the different ways in which Artisan can assist you in the development, debugging, and deployment process. We have also seen how easy it is to build a custom Artisan command and adapt it to your own needs.

If you are relatively new to the command line, you will have had a glimpse into the power of command-line utilities. If, on the other hand, you are a seasoned user of the command line and you have written scripts with other programming languages, you can surely appreciate the simplicity and expressiveness of Artisan.

Resources for Article:


Further resources on this subject:



Subscribe to the weekly Packt Hub newsletter. We'll send you the results of our AI Now Survey, featuring data and insights from across the tech landscape.

LEAVE A REPLY

Please enter your comment!
Please enter your name here