2
votes

I'm trying to call in a controller a very time-consuming Artisan command (it executes in 20-90 s ), but I have two problems. First of all it seems like the command does not execute at all (if I return the output it just returns "0").

Secondly the other part (returning the file) does not wait for the command to execute (but it can be related to the first part). Here's my code:

public function returnZip()
{
    // just a failsafe, in case if scheduled command did not created the file yet
    if( ! file_exists( storage_path( '/app/exports/' . date('Y_m_d') . '.zip' ) ) ){
        Artisan::call('maximus:export');
    }

    return response()->file( storage_path( '/app/exports/' . date('Y_m_d') . '.zip' ) );
}

How can I properly execute the Artisan command from route/controller and wait for it to finish it's task?

EDIT

I tried to debug this problem a little more and found out that the command is not being executed at all when called from a route/controller.

Tried this:

Route::get('/test', function(){
    Artisan::call('maximus:export');
    return ['ok'];
});

And my command is supposed to create a file:

public function handle()
{
    exec('touch /some/path/storage/app/exports/test');
 }

When I run this command in terminal, the file is being created, but when I hit the route, it isn't. Any ideas?

4

4 Answers

1
votes

I'm pretty sure that the artisan command is being handled asynchronously, so it isn't going to wait for the command to finish. Therefore your response is likely to be empty/malformed.

You may want to look into Events and Listeners to ensuring your order of operations is correct (https://laravel.com/docs/5.4/events).

For example, in your maximus:export command you could fire an event immediately after the file has been created.

Example:

Create an event called ZipCreated and a listener called SendZip. Then in your artisan command handler call the event:

event(new ZipCreated($file));

Then link it to a listener in your EventServiceProvider.php:

protected $listen = [
    Events\Repository\ZipCreated::class => [
        Listeners\Repository\SendZip::class,
    ],
];

This way ZipCreated will provide SendZip with the zipped file (or a filepath if you want) and SendZip can handle returning the file to the user.

Now whenever the command is run, the creation of the file and the handling of response will always happen in the correct order.

1
votes

Okay, I fired up Laravel and tested it. My command is:

public function handle()
{
    exec('touch ' . storage_path(str_random(16) . '.txt'));
}

It works perfectly both in terminal and in a route by calling Artisan::call().

A wild guess: does the www-data user (or whatever user the PHP used by your webserver is running as) have sufficient privilege to write the file?

0
votes

if I return the output it just returns "0"

Let me point out just in case that the call method doesn't return the output of the command, but its exit code ("0" meaning success). Instead, Artisan::output() will return the output.

I'd say check the logs to see what's going on, also check you are in fact useing the Artisan facade. Otherwise, try a debugger or alternatively insert informative dd() statements ;) (the entry point being Illuminate\Foundation\Console\Kernel::call).

-1
votes

try this: dd(Artisan::output());