Chapter 8. Artisan and Tinker

From installation onward, modern PHP frameworks expect many interactions to take place on the command line. Laravel provides three primary tools for command-line interaction: Artisan, a suite of built-in command-line actions with the ability to add more; Tinker, a REPL or interactive shell for your application; and the installer, which we’ve already covered in Chapter 2.

An Introduction to Artisan

If you’ve been reading through this book chapter by chapter, you’ve already learned how to use Artisan commands. They look something like this:

php artisan make:controller PostController

If you look in the root folder of your application, you’ll see that artisan is actually just a PHP file. That’s why you’re starting your call with php artisan; you’re passing that file into PHP to be parsed. Everything after that is just passed into Artisan as arguments.

Symfony Console Syntax

Artisan is actually a layer on top of the Symfony Console component; so, if you’re familiar with writing Symfony Console commands, you should feel right at home.

Since the list of Artisan commands for an application can be changed by a package or by the specific code of the application, it’s worth checking every new application you encounter to see what commands are available.

To get a list of all available Artisan commands, you can run php artisan list from the project root (although if you just run php artisan with no parameters, it will do the same thing).

Basic Artisan Commands

There’s not enough space here to cover all of the Artisan commands, but we’ll cover many of them. Let’s get started with the basic commands:

clear-compiled

Removes Laravel’s compiled class file, which is like an internal Laravel cache; run this as a first resort when things are going wrong and you don’t know why

down, up

Puts your application in “maintenance mode” in order for you to fix an error, run migrations, or whatever else and restore an application from maintenance mode, respectively

dump-server (5.7+)

Starts the dump server (see “Laravel Dump Server”) to collect and output dumped variables

env

Displays which environment Laravel is running in at the moment; it’s the equivalent of echoing app()->environment() in-app

help

Provides help for a command; for example, php artisan help commandName

migrate

Runs all database migrations

optimize

Clears and refreshes the configuration and route files

preset

Changes out the frontend scaffolding for another

serve

Pins up a PHP server at localhost:8000 (you can customize the host and/or port with --host and --port)

tinker

Brings up the Tinker REPL, which we’ll cover later in this chapter

Changes to the Artisan Commands List Over time

The list of Artisan commands and their names have changed in small ways over the lifetime of Laravel. I’ll try to note any time they’ve changed, but everything here is current for Laravel 5.8. If you’re not working in 5.8, the best way to see what’s available to you is to run php artisan from your application.

Options

Before we cover the rest of the commands, let’s look at a few notable options you can pass any time you run an Artisan command:

-q

Suppresses all output

-v, -vv, and -vvv

Specify the level of output verbosity (normal, verbose, and debug)

--no-interaction

Suppresses interactive questions, so the command won’t interrupt automated processes running it

--env

Allows you to define which environment the Artisan command should operate in (local, production, etc.).

--version

Shows you which version of Laravel your application is running on.

You’ve probably guessed from looking at these options that Artisan commands are intended to be used much like basic shell commands: you might run them manually, but they can also function as a part of some automated process at some point.

For example, there are many automated deploy processes that might benefit from certain Artisan commands. You might want to run php artisan config:cache every time you deploy an application. Flags like -q and --no-interaction ensure that your deploy scripts, not attended by a human being, can keep running smoothly.

The Grouped Commands

The rest of the commands available out of the box are grouped by context. We won’t cover them all here, but we’ll cover each context broadly:

app

This just contains app:name, which allows you to replace every instance of the default top-level App namespace with a namespace of your choosing; for example, php artisan app:name MyApplication. I recommend avoiding this feature and keeping your app’s root namespace as App.

auth

All we have here is auth:clear-resets, which flushes all of the expired password reset tokens from the database.

cache

cache:clear clears the cache, cache:forget removes an individual item from the cache, and cache:table creates a database migration if you plan to use the database cache driver.

config

config:cache caches your configuration settings for faster lookup; to clear the cache, use config:clear.

db

db:seed seeds your database, if you have configured database seeders.

event

event:list lists all the events and listeners in your application, event:cache caches that list, event:clear clears that cache, and event:generate builds missing event and event listener files based on the definitions in EventServiceProvider. You’ll learn more about events in Chapter 16.

key

key:generate creates a random application encryption key in your .env file.

Rerunning artisan key:generate Means Losing Some Encrypted Data

If you run php artisan key:generate more than once on your application, every currently logged-in user will be logged out. Additionally, any data you have manually encrypted will no longer be decryptable. To learn more, check out the article “APP_KEY and You” by fellow Tightenite Jake Bathman.

make

Each of the make: actions create a single item from a stub, and have parameters that vary accordingly. To learn more about any individual command’s parameters, use help to read its documentation.

For example, you could run php artisan help make:migration and learn that you can pass --create=tableNameHere to create a migration that already has the create table syntax in the file, as shown here: php artisan make:migration create_posts_table --create=posts.

migrate

The migrate command used to run all migrations was mentioned earlier, but there are several other migration-related commands. You can create the migrations table (to keep track of the migrations that are executed) with migrate:install, reset your migrations and start from scratch with migrate:reset, reset your migrations and run them all again with migrate:refresh, roll back just one migration with migrate:rollback, drop all tables and rerun all the migrations with migrate:fresh, or check the status of your migrations with migrate:status.

notifications

notifications:table generates a migration that creates the table for database notifications.

package

In versions of Laravel prior to 5.5, including a new Laravel-specific package in your app requires registering it manually in config/app.php. However, in 5.5 it’s possible for Laravel to “autodiscover” those packages so you don’t have to manually register them. package:discover rebuilds Laravel’s “discovered” manifest of the service providers from your external packages.

queue

We’ll cover Laravel’s queues in Chapter 16, but the basic idea is that you can push jobs up into remote queues to be executed one after another by a worker. This command group provides all the tools you need to interact with your queues, like queue:listen to start listening to a queue, queue:table to create a migration for database-backed queues, and queue:flush to flush all failed queue jobs. There are quite a few more, which you’ll learn about in Chapter 16.

route

If you run route:list, you’ll see the definitions of every route defined in the application, including each route’s verb(s), path, name, controller/closure action, and middleware. You can cache the route definitions for faster lookups with route:cache and clear your cache with route:clear.

schedule

We’ll cover Laravel’s cron-like scheduler in Chapter 16, but in order for it to work, you need to set the system cron to run schedule:run once a minute:

* * * * * php /home/myapp.com/artisan schedule:run >> /dev/null 2>&1

As you can see, this Artisan command is intended to be run regularly in order to power a core Laravel service.

session

session:table creates a migration for applications using database-backed sessions.

storage

storage:link creates a symbolic link from public/storage to storage/app/public. This is a common convention in Laravel apps, to make it easy to put user uploads (or other files that commonly end up in storage/app) somewhere where they’ll be accessible at a public URL.

vendor

Some Laravel-specific packages need to “publish” some of their assets, either so that they can be served from your public directory or so that you can modify them. Either way, these packages register these “publishable assets” with Laravel, and when you run vendor:publish, it publishes them to their specified locations.

view

Laravel’s view rendering engine automatically caches your views. It usually does a good job of handling its own cache invalidation, but if you ever notice it’s gotten stuck, run view:clear to clear the cache.

Writing Custom Artisan Commands

Now that we’ve covered the Artisan commands that come with Laravel out of the box, let’s talk about writing your own.

First, you should know: there’s an Artisan command for that! Running php artisan make:command YourCommandName generates a new Artisan command in app/Console/Commands/{YourCommandName}.php.

php artisan make:command

The command signature for make:command has changed a few times. It was originally command:make, but for a while in 5.2 it was console:make and then make:console.

Finally, in 5.3, it was settled: all of the generators are under the make: namespace, and the command to generate new Artisan commands is now make:command.

Your first argument should be the class name of the command, and you can optionally pass a --command parameter to define what the terminal command will be (e.g., appname:action). So, let’s do it:

php artisan make:command WelcomeNewUsers --command=email:newusers

Take a look at Example 8-1 to see what you’ll get.

Example 8-1. The default skeleton of an Artisan command
<?php

namespace AppConsoleCommands;

use IlluminateConsoleCommand;

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

    /**
     * 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 mixed
     */
    public function handle()
    {
        //
    }
}

As you can see, it’s very easy to define the command signature, the help text it shows in command lists, and the command’s behavior on instantiation (__construct()) and on execution (handle()).

Manually Binding Commands Prior to Laravel 5.5

In projects running versions of Laravel prior to 5.5, commands had to be manually bound into appConsoleKernel.php. If your app is running an older version of Laravel, just add the fully qualified class name for your command to the $commands array in that file and it’ll be registered:

protected $commands = [
    AppConsoleCommandsWelcomeNewUsers::class,
];

A Sample Command

We haven’t covered mail or Eloquent yet in this chapter (see Chapter 15 for mail and Chapter 5 for Eloquent), but the sample handle() method in Example 8-2 should read pretty clearly.

Example 8-2. A sample Artisan command handle() method
...
class WelcomeNewUsers extends Command
{
    public function handle()
    {
        User::signedUpThisWeek()->each(function ($user) {
            Mail::to($user)->send(new WelcomeEmail);
        });
    }

Now every time you run php artisan email:newusers, this command will grab every user that signed up this week and send them the welcome email.

If you would prefer injecting your mail and user dependencies instead of using facades, you can typehint them in the command constructor, and Laravel’s container will inject them for you when the command is instantiated.

Take a look at Example 8-3 to see what Example 8-2 might look like using dependency injection and extracting its behavior out to a service class.

Example 8-3. The same command, refactored
...
class WelcomeNewUsers extends Command
{
    public function __construct(UserMailer $userMailer)
    {
        parent::__construct();

        $this->userMailer = $userMailer
    }

    public function handle()
    {
        $this->userMailer->welcomeNewUsers();
    }

Arguments and Options

The $signature property of the new command looks like it might just contain the command name. But this property is also where you’ll define any arguments and options for the command. There’s a specific, simple syntax you can use to add arguments and options to your Artisan commands.

Before we dig into that syntax, take a look at an example for some context:

protected $signature = 'password:reset {userId} {--sendEmail}';

Arguments—required, optional, and/or with defaults

To define a required argument, surround it with braces:

password:reset {userId}

To make the argument optional, add a question mark:

password:reset {userId?}

To make it optional and provide a default, use:

password:reset {userId=1}

Options—required values, value defaults, and shortcuts

Options are similar to arguments, but they’re prefixed with -- and can be used with no value. To add a basic option, surround it with braces:

password:reset {userId} {--sendEmail}

If your option requires a value, add an = to its signature:

password:reset {userId} {--password=}

And if you want to pass a default value, add it after the =:

password:reset {userId} {--queue=default}

Array arguments and array options

Both for arguments and for options, if you want to accept an array as input, use the * character:

password:reset {userIds*}

password:reset {--ids=*}

Using array arguments and parameters looks a bit like Example 8-4.

Example 8-4. Using array syntax with Artisan commands
// Argument
php artisan password:reset 1 2 3

// Option
php artisan password:reset --ids=1 --ids=2 --ids=3

Array Arguments Must Be the Last Argument

Since an array argument captures every parameter after its definition and adds them as array items, an array argument has to be the last argument within an Artisan command’s signature.

Input descriptions

Remember how the built-in Artisan commands can give us more information about their parameters if we use artisan help? We can provide that same information about our custom commands. Just add a colon and the description text within the curly braces, like in Example 8-5.

Example 8-5. Defining description text for Artisan arguments and options
protected $signature = 'password:reset
                        {userId : The ID of the user}
                        {--sendEmail : Whether to send user an email}';

Using Input

Now that we’ve prompted for this input, how do we use it in our command’s handle() method? We have two sets of methods for retrieving the values of arguments and options.

argument() and arguments()

$this->arguments() returns an array of all arguments (the first array item will be the command name). $this->argument() called with no parameters returns the same response; the plural method, which I prefer, is just available for better readability, and is only available after Laravel 5.3.

To get just the value of a single argument, pass the argument name as a parameter to $this->argument(), as shown in Example 8-6.

Example 8-6. Using $this->arguments() in an Artisan command
// With definition "password:reset {userId}"
php artisan password:reset 5

// $this->arguments() returns this array
[
    "command": "password:reset",
    "userId": "5",
]

// $this->argument('userId') returns this string
"5"

option() and options()

$this->options() returns an array of all options, including some that will by default be false or null. $this->option() called with no parameters returns the same response; again, the plural method, which I prefer, is just available for better readability and is only available after Laravel 5.3.

To get just the value of a single option, pass the argument name as a parameter to $this->option(), as shown in Example 8-7.

Example 8-7. Using $this->options() in an Artisan command
// With definition "password:reset {--userId=}"
php artisan password:reset --userId=5

// $this->options() returns this array
[
    "userId" => "5",
    "help" => false,
    "quiet" => false,
    "verbose" => false,
    "version" => false,
    "ansi" => false,
    "no-ansi" => false,
    "no-interaction" => false,
    "env" => null,
]

// $this->option('userId') returns this string
"5"

Example 8-8 shows an Artisan command using argument() and option() in its handle() method.

Example 8-8. Getting input from an Artisan command
public function handle()
{
    // All arguments, including the command name
    $arguments = $this->arguments();

    // Just the 'userId' argument
    $userid = $this->argument('userId');

    // All options, including some defaults like 'no-interaction' and 'env'
    $options = $this->options();

    // Just the 'sendEmail' option
    $sendEmail = $this->option('sendEmail');
}

Prompts

There are a few more ways to get user input from within your handle() code, and they all involve prompting the user to enter information during the execution of your command:

ask()

Prompts the user to enter freeform text:

$email = $this->ask('What is your email address?');
secret()

Prompts the user to enter freeform text, but hides the typing with asterisks:

$password = $this->secret('What is the DB password?');
confirm()

Prompts the user for a yes/no answer, and returns a Boolean:

if ($this->confirm('Do you want to truncate the tables?')) {
    //
}

All answers except y or Y will be treated as a “no.”

anticipate()

Prompts the user to enter freeform text, and provides autocomplete suggestions. Still allows the user to type whatever they want:

$album = $this->anticipate('What is the best album ever?', [
    "The Joshua Tree", "Pet Sounds", "What's Going On"
]);
choice()

Prompts the user to choose one of the provided options. The last parameter is the default if the user doesn’t choose:

$winner = $this->choice(
    'Who is the best football team?',
    ['Gators', 'Wolverines'],
    0
);

Note that the final parameter, the default, should be the array key. Since we passed a nonassociative array, the key for Gators is 0. You could also key your array, if you’d prefer:

$winner = $this->choice(
    'Who is the best football team?',
    ['gators' => 'Gators', 'wolverines' => 'Wolverines'],
    'gators'
);

Output

During the execution of your command, you might want to write messages to the user. The most basic way to do this is to use $this->info() to output basic green text:

$this->info('Your command has run successfully.');

You also have available the comment() (orange), question() (highlighted teal), error() (highlighted red), and line() (uncolored) methods to echo to the command line.

Please note that the exact colors may vary from machine to machine, but they try to be in line with the local machine’s standards for communicating to the end user.

Table output

The table() method makes it simple to create ASCII tables full of your data. Take a look at Example 8-9.

Example 8-9. Outputting tables with Artisan commands
$headers = ['Name', 'Email'];

$data = [
    ['Dhriti', '[email protected]'],
    ['Moses', '[email protected]'],
];

// Or, you could get similar data from the database:
$data = AppUser::all(['name', 'email'])->toArray();

$this->table($headers, $data);

Note that Example 8-9 has two sets of data: the headers, and the data itself. Both contain two “cells” per “row”; the first cell in each row is the name, and the second is the email. That way the data from the Eloquent call (which is constrained to pull only name and email) matches up with the headers.

Take a look at Example 8-10 to see what the table output looks like.

Example 8-10. Sample output of an Artisan table
+---------+--------------------+
| Name    | Email              |
+---------+--------------------+
| Dhriti  | [email protected]   |
| Moses   | [email protected] |
+---------+--------------------+

Progress bars

If you’ve ever run npm install, you’ve seen a command-line progress bar before. Let’s build one in Example 8-11.

Example 8-11. Sample Artisan progress bar
$totalUnits = 350;
$this->output->progressStart($totalUnits);

for ($i = 0; $i < $totalUnits; $i++) {
    sleep(1);

    $this->output->progressAdvance();
}

$this->output->progressFinish();

What did we do here? First, we informed the system how many “units” we needed to work through. Maybe a unit is a user, and you have 350 users. The bar will then divide the entire width it has available on your screen by 350, and increment it by 1/350th every time you run progressAdvance(). Once you’re done, run progressFinish() so that it knows it’s done displaying the progress bar.

Writing Closure-Based Commands

If you’d prefer to keep your command definition process simpler, you can write commands as closures instead of classes by defining them in routes/console.php. Everything we discuss in this chapter will apply the same way, but you will define and register the commands in a single step in that file, as shown in Example 8-12.

Example 8-12. Defining an Artisan command using a closure
// routes/console.php
Artisan::command(
    'password:reset {userId} {--sendEmail}',
    function ($userId, $sendEmail) {
        $userId = $this->argument('userId');
        // Do something...
    }
);

Calling Artisan Commands in Normal Code

While Artisan commands are designed to be run from the command line, you can also call them from other code.

The easiest way is to use the Artisan facade. You can either call a command using Artisan::call() (which will return the command’s exit code) or queue a command using Artisan::queue().

Both take two parameters: first, the terminal command (password:reset); and second, an array of parameters to pass it. Take a look at Example 8-13 to see how it works with arguments and options.

Example 8-13. Calling Artisan commands from other code
Route::get('test-artisan', function () {
    $exitCode = Artisan::call('password:reset', [
        'userId' => 15,
        '--sendEmail' => true,
    ]);
});

As you can see, arguments are passed by keying to the argument name, and options with no value can be passed true or false.

Tip

In Laravel 5.8+, you can call Artisan commands much more naturally from your code. Just pass the same string you’d call from the command line into Artisan::call():

Artisan::call('password:reset 15 --sendEmail')

You can also call Artisan commands from other commands using $this->call(), (which is the same as Artisan::call()) or $this->callSilent(), which is the same but suppresses all output. See Example 8-14 for an example.

Example 8-14. Calling Artisan commands from other Artisan commands
public function handle()
{
    $this->callSilent('password:reset', [
        'userId' => 15,
    ]);
}

Finally, you can inject an instance of the IlluminateContractsConsoleKernel contract, and use its call() method.

Tinker

Tinker is a REPL, or read–eval–print loop. If you’ve ever used IRB in Ruby, you’ll be familiar with how a REPL works.

REPLs give you a prompt, similar to the command-line prompt, that mimics a “waiting” state of your application. You type your commands into the REPL, hit Return, and then expect what you typed to be evaluated and the response printed out.

Example 8-15 provides a quick sample to give you a sense of how it works and how it might be useful. We start the REPL with php artisan tinker and are then presented with a blank prompt (>>>); every response to our commands is printed on a line prefaced with =>.

Example 8-15. Using Tinker
$ php artisan tinker

>>> $user = new AppUser;
=> AppUser: {}
>>> $user->email = '[email protected]';
=> "[email protected]"
>>> $user->password = bcrypt('superSecret');
=> "$2y$10$TWPGBC7e8d1bvJ1q5kv.VDUGfYDnE9gANl4mleuB3htIY2dxcQfQ5"
>>> $user->save();
=> true

As you can see, we created a new user, set some data (hashing the password with bcrypt() for security), and saved it to the database. And this is real. If this were a production application, we would’ve just created a brand new user in our system.

This makes Tinker a great tool for simple database interactions, for trying out new ideas, and for running snippets of code when it’d be a pain to find a place to put them in the application source files.

Tinker is powered by Psy Shell, so check that out to see what else you can do with Tinker.

Laravel Dump Server

One common method of debugging the state of your data during development is to use Laravel’s dump() helper, which runs a decorated var_dump() on anything you pass to it. This is fine, but it can often run into view issues.

In projects running Laravel 5.7 and later, you can now enable the Laravel dump server, which catches those dump() statements and displays them in your console instead of rendering them to the page.

To run the dump server in your local console, navigate to your project’s root directory and run php artisan dump-server:

$ php artisan dump-server

Laravel Var Dump Server
=======================

 [OK] Server listening on tcp://127.0.0.1:9912

 // Quit the server with CONTROL-C.

Now, try using the dump() helper function in your code somewhere. To test it out, try this code in your routes/web.php file:

Route::get('/', function () {
    dump('Dumped Value');

    return 'Hello World';
});

Without the dump server, you’d see both the dump and your “Hello World.” But with the dump server running, you’ll only see “Hello World” in the browser. In your console, you’ll see that the dump server caught that dump(), and you can inspect it there:

GET http://myapp.test/
--------------------

 ------------ ---------------------------------
  date         Tue, 18 Sep 2018 22:43:10 +0000
  controller   "Closure"
  source       web.php on line 20
  file         routes/web.php
 ------------ ---------------------------------

"Dumped Value"

Testing

Since you know how to call Artisan commands from code, it’s easy to do that in a test and ensure that whatever behavior you expected to be performed has been performed correctly, as in Example 8-16. In our tests, we use $this->artisan() instead of Artisan::call() because it has the same syntax but adds a few testing-related assertions.

Example 8-16. Calling Artisan commands from a test
public function test_empty_log_command_empties_logs_table()
{
    DB::table('logs')->insert(['message' => 'Did something']);
    $this->assertCount(1, DB::table('logs')->get());

    $this->artisan('logs:empty'); // Same as Artisan::call('logs:empty');
    $this->assertCount(0, DB::table('logs')->get());
}

In projects running Laravel 5.7 and later, you can chain on a few new assertions to your $this->artisan() calls that make it even easier to test Artisan commands—not just the impact they have on the rest of your app, but also how they actually operate. Take a look at Example 8-17 to see an example of this syntax.

Example 8-17. Making assertions against the input and output of Artisan commands
public function testItCreatesANewUser()
{
    $this->artisan('myapp:create-user')
        ->expectsQuestion("What's the name of the new user?", "Wilbur Powery")
        ->expectsQuestion("What's the email of the new user?", "[email protected]")
        ->expectsQuestion("What's the password of the new user?", "secret")
        ->expectsOutput("User Wilbur Powery created!");

    $this->assertDatabaseHas('users', [
        'email' => '[email protected]'
    ]);
}

TL;DR

Artisan commands are Laravel’s command-line tools. Laravel comes with quite a few out of the box, but it’s also easy to create your own Artisan commands and call them from the command line or your own code.

Tinker is a REPL that makes it simple to get into your application environment and interact with real code and real data, and the dump server lets you debug your code without stopping the code’s execution.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset