Select Page
Laravel Queue: archiving PDF Files

Laravel Queue: archiving PDF Files

I’ve been working with Laravel Queues lately and it is indeed a pleasant experience. If you are not yet familiar with Laravel Queue you can read more about it here.

Queues are typically used in tasks that require an intensive amount of resources. For example, generating a large CSV report might take a while to finish. You can delegate the task in a queue that runs as a background task (managed by Supervisor) and just give the users feedback over time. This way, end-users will not wait for a long time for the report to finish. Most, users will even think that there your application might be crashing.

In one of my projects I need to create a Zip Archive of PDF Files for about hundreds of users. Generating PDF Files usually takes time and resource. As such, I do it via Queue. Each Job not only generates PDF File it also updates the Zip Archive.

GeneratePDFResult.php:

<?php

namespace App\Jobs;

use App\Models\Result;
use App\ResultCheckers\NISResultChecker;
use Illuminate\Bus\Batchable;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;
use PDF;
use ZipArchive;

// Supports local driver, for now.
class GeneratePDFResult implements ShouldQueue
{
    use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    private const CACHE_TTL = 31556926;
    public $tries = 1;
    public $result;

    public function __construct(Result $result)
    {
        $this->result = $result;
    }

    public function handle()
    {
        $payload = [
            'scores' => $this->result->scores,
            'details' => $this->result->details,
        ];
        
        $batchId = $this->batch()->id;
        $pdf = PDF::loadView('neuro_report', [ 'payload' => $payload ])->setPaper('legal', 'portrait');
        $fileName =  sprintf('%s %s %s.pdf',
            $payload['details']['exam_date'],
            $payload['details']['first_name'],
            $payload['details']['last_name'],
        );
        
        $pdfFilePath = '/results/' . $batchId . '/' . $fileName;
        Storage::disk('local')->put($pdfFilePath, $pdf->output());

        $this->updateZipArchive($batchId, $pdfFilePath, $fileName);
    }
    
    private function updateZipArchive($batchId, $pdfFilePath, $fileName)
    {
        $zip = new ZipArchive();
        $zipFilePath = Storage::disk('local')->path('/results/' . $this->batchId . '/' . $this->batchId . '.zip');
        $pdfLocalFilePath = Storage::disk('local')->path($pdfFilePath);

        if ($zip->open($zipFilePath, ZipArchive::CREATE) === TRUE) {
            $zip->addFromString($fileName, Storage::disk('local')->get($pdfFilePath));
            $zip->close();
        } else {
            logger('Failed opening zip file:' . $zipFilePath);
        }
    }
}

As you’ve noticed, I used a 3rd party library called laravel-dompdf to generate PDF Files from HTML. I’ve used the ZipArchive class of PHP for creating and updating the Zip Archives. I’ve batched the jobs so that they are logically grouped and I can monitor them as a whole. Also you can notice how the Storage face works perfectly with these two.

That’s it! I hope you learn something new that you can apply on your backend applications.

Javascript optional chaining

Javascript optional chaining

2020 is a hell of a year for most of us but not with Javascript as it has introduced a lot of new cool features including one of my favorites: Optional Chaining.

If you haven’t tried Typescript before, this might be new to you but this functionality is very much used in Typescript. Let’s dive into the code, shall we?

Suppose you have the following code:

const human = { job: null }

What if you want to access a particular property of job like so:

const salary = human.job.salary

This code will throw an error telling us that it can’t read salary of null. Typically devs will use ternary operator to avoid the error like so:

const salary = human.job ? human.job.salary : undefined

While this will work the code will be easily repetitive when you are trying to read lots of properties from a huge object that might or might not exist. Thanks for Javascript, you can now do it elegantly in your code like so:

const salary = human.job?.salary

The code will not produce an error. If the job property happened to not exist, then it will just return an undefined.

So where’s the chaining part? Suppose human might or might not exist then your code can look as following:

const salary = human?.job?.salary // outputs undefined

That’s it for Optional Chaining.  2020 for Javscript have been awesome there are other of cool stuffs that have been introduced like Object.fromEntries, matchAll(), dynamic import,  Promise.allSettled, Null Coalescing and others.

Let’s keep rockin with Javascript!

Javascript optional chaining

TF is noop and what’s the use of it (JS)?

Have you encountered a function or a variable that is called noop in any source code you are reading? Have you asked yourself what the hell is that for?

Well, noop just means NO OPERATION. Meaning do not do something, return undefined or return an empty function. It is literally simple as that.

So what is the need for this noop at all?

Backup callback / Legacy code

Supposed you have a function like that accepts a function like so:

function displayInfo(callbackFn) {
  // execute callbackFn and and perform other complex things
}

and supposed your code will run in different environments like desktop, web, and mobile.

Now you need to provide displayInfo() a valid callback function that might only be available for a particular environment. Typically developers will create an empty function that will do nothing, thus noop. Like so:

 const callbackFn = environmentFn ? environmentFn : () => {} 

But writing an empty function every time you need it is not good in the eyes right? As such, developers separate it on a separate function like so:

const noop = () => {} 

That’s it! If ever you think of another usage of the noop function feel free to comment it below. 

Javascript optional chaining

call vs apply vs bind

 

If you started your career as a javascript developer using frameworks such as vue and angular,  javascript native functions might be unfamiliar to you. If you stumbled upon functions like call, apply, and bind and seems you can’t wrap your head around them, this article is for you.

All these 3 methods allow you to change the this context of a function.

Suppose you have the following code:

function sayName() {
  console.log(this.name)
}

sayName()

The output of this call is undefined. Because this refers to Window(if you are running the code in a browser) object and it has no name property. Thus, the undefined.

The call() method

call() basically calls a function with the given lexical context as parameter like so:

let human = { name: ‘JC’ }
function sayName() {

  console.log(this.name)

}

sayName.call(human); // Outputs JC

Another thing you can do is to supply argument/s to the function like so:

let human = { name: ‘JC’ }
function sayName(greeting) {

  console.log(greeting + ‘ ‘ + this.name)

}

sayName.call(human, ‘Hi!) // Outputs Hi! JC

The apply() method

apply() is just like call() except that it accepts arguments in array like so:

let human = { name: ‘JC’ }
function sayName(greeting, city) {

  console.log(greeting + ‘ ‘ + this.name + ‘ from ‘ + city)

}

sayName.apply(human, [‘Hi’, ‘Taguig’]) // Outputs Hi! JC from Taguig

with call() you have to supply the arguments individually like so:

console.log(sayName.call(human, ‘Hi!’, ‘Taguig’)) // Outputs Hi! JC from Taguig

The bind() method

The bind() is somewhat special. It is a higher-order function which means it returns another function when you invoke it. The function returned is curried, meaning you can call it multiple times to provide different arguments in each call. It is a complex topic you can read more about it on the internet. For now, let’s apply it on our example:

let human = { name: ‘JC’ }
function sayName(greeting, city) {

  console.log(greeting + ‘ ‘ +     this.name + ‘ from ‘ + city)

}

 

 

let greet = sayName.bind(human)

greet(‘Hi!’, ‘Taguig’) // Outputs Hi! JC from Taguig

greet(‘Hello!’, ‘Makati’) // Outputs Hello! JC from Makati

That’s it! I hope I was able to demistified these 3 methods for you.  Thanks for reading my article!

Laravel Queue: archiving PDF Files

Laravel S3 upload with Sail and Minio

Are you planning to use S3 as your file storage but want to test it first in your local machine avoiding the hassle of AWS setup?

Enter Minio (min.io, MinIO, whatever). Basically, Minio is a storage that works like S3 and you can use S3 Adapters to use it on your local machine. If you decided to push through production you just have to replace your credentials.

This tutorials assumed that you already installed Sail in your project. If not, read more about it here.

 

Update docker-compose.yml

Add minio service:

Add named volume:

Update .env

Finally, run

 

./vendor/bin/sail build --no-cache
./vendor/bin/sail up -d --force-recreate

You can view the Minio console, a web app where you can see your uploaded files using by checking the logs in the container like so:

 

docker-compose logs -f minio

And just like that, you can now use Minio to mimic S3 behavior. In fact, you can just use Minio as your dedicated file storage, but make sure you understand its documentation first.

I discovered an awesome cool chrome plugin!

I discovered an awesome cool chrome plugin!

I don’t really know if this plugin is around the internet for a long time. But I just discovered it this day!

It will give your new tab a new function in which development news/articles will be made available to you in a nice and sleek UI. Meet dailydev.

PLUS! they have this awesome devcard! Which is embeddable! and I just embedded it below laughing

JC Frane's Dev Card

I have never been excited like this when reading or surfing articles. 

Come on! Let’s learn new things!