Search code examples
phplaravellaravel-5task-queue

Laravel - Turn part of a function into a queued statement


I want to queue part of a function within my controller, mainly because it accesses a 3rd party API and calculates certain information from said request. I also want to do this to increase my knowledge of queues!

The code which I want queueing is:

The only variables that will need pushing with this if statement is $postcode and $clinic ID (which is figured out above the statement).

if($clinic->postcode != $postcode)
    {

     $client = new Client([ 'base_uri' => 'https://api.postcodes.io/','timeout' => 2.0, 'verify' => false ]);
     $response = $client->get('postcodes/'.$postcode)->getBody();

     $input = json_decode($response);

     $clinic->latitude   = $input->result->latitude;
     $clinic->longitude  = $input->result->longitude;
     $clinic->save();
}

So far I have created the queue table and migrated it.

I then ran the command: php artisan make:job GetClinicLatAndLongPoints --queued

My question is, how can I put this function inside the GetClinicLatAndLongPoints including passing the two variables over to do so?

I have so far:

public function handle(Clinic $clinic, $postcode)
    {

    }

But I'm unsure how to lay things out! Any guidance would be hugely appreciated.


Solution

  • You can pass an instance of your Clinic model and the postal code to the constructor of your job, which may look along the lines of

    namespace App\Jobs;
    
    use App\Clinic;
    use App\Jobs\Job;
    use GuzzleHttp\Client;
    use Illuminate\Queue\SerializesModels;
    use Illuminate\Queue\InteractsWithQueue;
    use Illuminate\Contracts\Bus\SelfHandling;
    use Illuminate\Contracts\Queue\ShouldQueue;
    
    class GetClinicLatAndLongPoints extends Job implements SelfHandling, ShouldQueue
    {
        use InteractsWithQueue, SerializesModels;
    
        private $clinic;
        private $postcode;
    
        public function __construct(Clinic $clinic, $postcode)
        {
            $this->clinic = $clinic; // Laravel will automatically deserialize the model instance
            $this->postcode = $postcode;
        }
    
        public function handle()
        {
            $coordinates = $this->getCoordinates($this->postcode);
    
            if (! is_null($coordinates)) {
                // You may want to add error handling
                $this->clinic->latitude   = $coordinates['latitude'];
                $this->clinic->longitude  = $coordinates['longitude'];
                $this->clinic->save();
            }
        }
    
        private function getCoordinates($postcode)
        {
            $client = new Client(['base_uri' => 'https://api.postcodes.io/','timeout' => 2.0, 'verify' => false]);
            $response = json_decode($client->get('postcodes/'.$postcode)->getBody()->getContents());
            if (! $response || json_last_error() !== JSON_ERROR_NONE) {
                return null;
            }
            if ($response->status == 200 &&
                isset($response->result->latitude) &&
                isset($response->result->longitude)) {
                return [
                    'latitude' => $response->result->latitude,
                    'longitude' => $response->result->longitude
                ];
            }
            return null;
        }
    }
    

    In your controller you dispatch your job

    if ($clinic->postcode != $postcode) {
        $this->dispatch(new GetClinicLatAndLongPoints($clinic, $postcode));
    }
    

    On a side note: although Laravel comes with the database queue driver using it in production is not a very good idea. Better make use of one of the job queues i.e. beanstalkd.