'Create csv file with php and send it in an email (laravel-9)

I have to pick up datas from a form, generate a csv file with this datas and then send this file like an attachment on a mail.

I create a service to generate my csvfile :

<?php

namespace App\Services;

class GenerateCsvService {

    /**
     * Create a csv file with the given datas
     */
    public function generateCsvFile($id, $data) {

        $array = [];
        $array[] = ['NOM', 'PRENOM', 'EMAIL', 'NB_EX'];
        $array[] = 
            [$data->nom, 
            $data->prenom, 
            $data->email,
            $data->nb_exemp
        ];
        $file = fopen('/var/www/example-app/storage/app/csv/file' . $id . '.csv', 'w+');

        foreach ($array as $fields) {
            fputcsv($file, $fields);
        }
        fclose($file);
        
        return $file;
    }
}

Then I use this service on my controller :

        // Generate the csv file 
        $generateService = new GenerateCsvService();
        $csvFile = $generateService->generateCsvFile($commandeId, $request);

Then I configure the MailerService from laravel :

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class CommandMail extends Mailable
{
    use Queueable, SerializesModels;

    public $datas = [];
    public $filename = '';

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($id, $nom, $prenom, $email, $nb_exemplaires)
    {
        $this->datas['Nom'] = $nom;
        $this->datas['Prénom'] = $prenom;
        $this->datas['Email'] = $email;
        $this->datas["Nombre d'exemplaires"] = $nb_exemplaires;
        $this->filename = 'file' . $id . '.csv';
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->from('[email protected]')
                    ->subject('Commande de catalogue')
                    ->view('emails.command')
                    ->attachFromStorage('csv', $this->filename);
    }
}

And call this service on my controller (just after the generateCsvFile Service :

        // Send it with the mailerservice
        $mailer = new MailerService();
        $mailer->sendEmail($commandeId, $nom, $prenom, $email, $nb_exemplaires);

At this point, each submit of the form generate a csv file on /storage/app and send an email with datas and the file. The file attached have the right name but is empty (0KO). On the server the file is ok (1KO) with the right name and the right content. I guess that is an asynchronous problem. I think that the mail is sending too earlier so the file isn't already fully create. How can I do to prevent that?

Maybe set a timer to let the necessary time to generate the file before sending the email? Wouldn't that be too arbitrary?

Or check the file size maybe?

I'll be very thanksfull if somebody would help me!



Solution 1:[1]

Yese, you spotted that this is an asynchronous problem. You may define two job classes: one generates a csv file based on form data, and another sends email. On form submission, dispatch a job chain of the two classes to a Laravel Queue. This could ensure the email would be sent after a csv file is generated, or in other words, they execute in sequence.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Elvis