Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unlink files after they have been attached and sent? (When using Mail::queue)

I switched from sending my mails immediately to adding them to the queue, here is my code, the $attachments is an array of temporary paths, I've commented out what I've tried, which throws errors about files not existing.

Mail::queue($view, $data, function(\Illuminate\Mail\Message $message) use($mail,$attachments){
    foreach($mail->getRecipients() as $recipient){
        $message->to($recipient);
    }
    $message->subject($mail->getSubject());
    foreach($attachments as $attachment){
        $message->attach($attachment);
        //this deletes the attachment before being sent
        //unlink($attachment);
    }
});
/* This code only works when using Mail::send() instead of Mail:queue()
foreach($attachments as $attachment){
    unlink($attachment);
}
*/

Basically I want to clean up and remove my temporary attachments after the mail was sent. I am guessing this would not work with the out of the box laravel mail solutions. How can I trigger code post-queue-mail-sent?

like image 373
Moak Avatar asked May 13 '14 17:05

Moak


3 Answers

Expanding on Deric Lima's answer a bit, you don't necessarily need a new Job class for this. You can do it with a Mailable object as well. Just override the send method.

/**
 * @param MailerContract $mailer
 */
public function send(MailerContract $mailer)
{
    parent::send($mailer);

    //$this->clearAttachments is something you can defined in your constructor,
    //making it the responsibility of whatever is applying the attachment
    //to know whether it needs to remain in tact after the email is transmitted.
    if ($this->clearAttachments) {
        foreach ($this->attachments as $attachment) {
            if (\File::exists($attachment['file'])) {
                \File::delete($attachment['file']);
            }
        }
    }
}

Personally, I'd make a BaseMailable class that all other Mailable classes extend, as opposed to the Illuminate\Mail\Mailable directly. Then you don't even have to worry about it from then on.

like image 118
kmuenkel Avatar answered Oct 21 '22 07:10

kmuenkel


You have to wait until the queue is processed before removing the file.

Without knowing the implementation details of the queue it is hard to answer your question, but if your queue is processed before the script ends, you can use register_shutdown_function http://www.php.net/manual/en/function.register-shutdown-function.php to run a cleanup function that removes the file

register_shutdown_function(function() use (filename){
    if (file_exists($filename)) {
        unlink($filename);
    }
})
like image 38
Scott Jungwirth Avatar answered Oct 21 '22 05:10

Scott Jungwirth


I had similar problem and I solved using Laravel Jobs. Basically, you can create a Job class to send the email:

class MailJob extends Job implements SelfHandling, ShouldQueue
{
  use InteractsWithQueue, SerializesModels;

  public function handle()
  {
    Mail::send($view, $data, function (\Illuminate\Mail\Message $message) use ($mail, $attachments) {
        foreach ($mail->getRecipients() as $recipient) {
            $message->to($recipient);
        }
        $message->subject($mail->getSubject());
        foreach ($attachments as $attachment) {
            $message->attach($attachment);
            unlink($attachment);
        }
    });
    foreach ($attachments as $attachment) {
        unlink($attachment);
    }
}

}

And then you just dispatch the Job inside the controller that you want to send the email:

$this->dispatch(new MailJob());

P.S: The job is running asynchronous on the background, so I used Mail::send instead of Mail::queue.

like image 1
Deric Lima Avatar answered Oct 21 '22 06:10

Deric Lima