Running Laravel Artisan Commands from your Admin Dashboard / GUI

22nd January 2014

Sometimes you may wish to run Artisan commands from your admin dashboard / application, without using the command line. Perhaps you don't have access to the command line (in which case perhaps it's time to switch hosting!), or more likely perhaps you want to manually run tasks right from your application. Here's a quick guide to how you might set that up.

Running a command

To run a command programmatically, you simply do this:

Artisan::call('my-command', array());

The first argument is the name of the command, the second your options, which I'm going to ignore for brevity.

It's probably more useful if you can get a hold of the command's output; you can do this by passing a third argument, which should be a class that implements Symfony\Component\Console\Output\OutputInterface. Perhaps the most useful of these classes is StreamOutput. So, for example, you can write the output to a file:

$stream = fopen('log.txt', 'w');
Artisan::call('my-command', array(), new StreamOutput($stream));

Or php://output:

$stream = fopen('php://output', 'w');
Artisan::call('my-command', array(), new StreamOutput($stream));

Armed with this, we can proceed with the admin interface.

The Admin Interface

Now to set up a route for the main admin page.

The first thing we probably want to do is set up a white-list of commands you're permitted to run from the back-end:

// @file app/config/commands.php
return array(
    /**
    * An array of commands which are available to run from the admin area.
    */
    'whitelist' => array(
        'my-command',
        'another-command',
        'do-something-else',
    ),
);

You'll notice the white-list just contains the commands' names; we can get the descriptions as defined in the Command classes themselves.

Here's an example route:

use Symfony\Component\Console\Output\StreamOutput,
use Symfony\Component\Console\Descriptor\ApplicationDescription;

Route::get('/commands', function()
{   
        $app = App::make('app');

        $app->loadDeferredProviders();

        $console_app = \Illuminate\Console\Application::start($app);

        $description = new ApplicationDescription($console_app, null);

        foreach ($description->getCommands() as $command) {
            if (in_array($command->getName(), Config::get('commands.whitelist'))) {
                $commands[$command->getName()] = $command->getDescription();
            }
        }

        return View::make('commands.index', array('commands' => $commands));
});

This code explained:

  1. We get a reference to the Laravel application from the IoC container
  2. We load deferred providers - without this, we won't have access to our own commands
  3. We grab a Console application
  4. We get an instance of Symfony\Component\Console\Descriptor\ApplicationDescription, which will provide us with information about the commands
  5. We take a subset of the commands - cross-referencing them against the white-list - and pass their names and descriptions to the view.

Now the view:

<html>
<body>
<ul id="commands">
    @foreach ($commands as $name => $description)
    <li><a href="/commands/run/{{ $name }}">{{ $description }}</a></li>
    @endforeach
</ul>

<div id="output">

</div>
</body>
</html>

This is simply a list of links and an empty DIV to hold the output, which we'll populate (by running the appropriate command) via AJAX.

The Javascript:

$('#commands li a').click(function(){
    $('#output').html('<p>Running...</p>');
    $('#output').load($(this).attr('href'));
    return false;
});

Finally, the route to actually run the command:

Route::get('/commands/run/{command}', function($command)
{
    print '<pre>';
    $stream = fopen('php://output', 'w');
    Artisan::call($command, array(), new StreamOutput($stream));
    print '</pre>';
    print '<p>DONE</p>';
});

It's a simplistic example, with some crucial omissions - arguments / options and security - but it's a starting point.