#Dealing with external programs in laravel

7 messages · Page 1 of 1 (latest)

dry mortar
#

I want to bulk generate certificates for my students, to do so I've created an external script (not in Laravel) that gets a JSON file with student data as input and outputs a PDF file containing all certificates, my goal is to integrate this script into my Laravel application

  1. Retrieve students as an array and convert it to JSON
  2. Create a timestamp-ed json file containing that array
  3. Run the script and pass it the JSON file as well as a filename for the PDF that it's going to output
  4. Download the PDF file for the user
  5. Delete the JSON & PDF files

Here's what I came up with

    $students = [
        'students' => [
            [
                'name' => 'John Doe',
                'course' => 'Horse Nail Clipping',
                'date' => 'January 12th, 2025',
                'hours' => 13,
            ],
            [
                'name' => 'Jane Doe',
                'course' => 'Video Editing',
                'date' => 'January 17th, 2025',
                'hours' => 40,
            ],
        ],
    ];

    // Save JSON file
    $filename = 'cert-data-'.now()->timestamp.'.json';
    Storage::disk('local')->put($filename, json_encode($students, JSON_PRETTY_PRINT));

    $jsonPath = 'app/'.$filename;
    $pdfName = pathinfo($filename, PATHINFO_FILENAME).'.pdf';
    $pdfPath = storage_path('app/'.$pdfName);

    // Run Typst
    $command = 'typst compile '.storage_path('cert.typ')." $pdfPath "."--input data=$jsonPath";
    $proc = Process::run($command);

Now this works when put in a handle method of a command but when I move it to a HTTP route and try to download the resulting file it fails to generate the PDF and thus download

I also feel like the way I've done this whole thing is wrong and not how Laravel is designed to handle external scripts (especially the constant creation and deletion of files feels flaky) so if you know a better way to do this let me know

dry mortar
#

I've done some debugging so now it works even in the web routes

#

but I'd still like to know if there is a better way to handle external programs for future reference

tacit grove
#

That's pretty much how you do it. I would just store the input file in a temp folder, or pass the content via stdin if the typst command supports that. And defining the command as an array of arguments would make it a bit cleaner, but it doesn't really matter here.

green swift
#

I would create an artisan command, move the script logic of gathering the student information and certificate generation into it and then run it on a schedule. Probably add a default # of certificates to process each run, so that it doesn't bog down, but eventually it will process them all.

#

or if you still wanted to do it off a JSON file, generate a json file with one scheduled task and read it to make the certificates with a different scheduled task. So generate the input, consume it. Repeat.

icy gust
#

You're basically doing it right
The clean way in Laravel is:

  • Put the JSON in storage/app
  • Run your script with Symfony Process
  • Delete the JSON right after
  • Return the PDF with ->deleteFileAfterSend(true) so it auto-cleans