Search code examples
phpsymfonysymfony6symfony-mailergotenberg

How can I attach a PDF generated using Gotenberg Bundle to a Symfony email?


I want to create a PDF with Symfony and Gotenberg like this:

$doc = $gotenbergPdf
             ->html()
             ->content('rechnungen/pdf/rechnung.pdf.html.twig', [])
             ->paperStandardSize(PaperSize::A4)
             ->generate()
             ->stream();

After that, I want to send it out via email attachment:

$email = (new Email())
           ->from('[email protected]')
           ->to('[email protected]')
           ->subject('subject')
           ->text('text')
           ->addPart(new DataPart(new File(stream_get_contents($dokument)), 'Contract.pdf', 'application/pdf'));

$this->mailer->send($email);

Result is:

stream_get_contents(): Argument #1 ($stream) must be of type resource, Symfony\Component\HttpFoundation\StreamedResponse given

What have I to do to attach the in memory pdf to the email?


Solution

  • As you noticed, generate() returns a GotenbergFileResult, and GotenbergFileResult::stream() will get you a StreamedResponse, that you cannot use directly.

    Since you want to instantiate Symfony\Component\Mime\Part\File() which expects a string with the path for a file, passing the file contents (even if getting the contents of the stream worked as you intended), wouldn't work.

    If you want to get the file on the disk, to pass as an attachment to File, the Gotenberg bundle gives you a processor you can use for that.

    You should do something like this:

    use Symfony\Component\Filesystem\Filesystem;
    use Sensiolabs\GotenbergBundle\Processor\FileProcessor;
    
    $someTmpPath = '/tmp/some-tmp-path';
            
    $result = $this->gotenberg->url()
                ->url('https://sparksuite.github.io/simple-html-invoice-template/')
                ->processor(new FileProcessor(new Filesystem(), $someTmpPath))
                ->generate();
            
            
    $filename = $someTmpPath . '/'. $result->getFilename();
    
    // this is the step where the processor dumps the file into the filesystem
    $result->process();  
    

    After doing this, you could do:

    use Symfony\Component\Mime\Email;
    use Symfony\Component\Mime\Part\DataPart;
    use Symfony\Component\Mime\Part\File;
    
    $email = (new Email())
              ->from('[email protected]')
              ->to('[email protected]')
              ->subject('subject')
                        ->text('text')
                        ->addPart(new DataPart(new File($filename),'Contract.pdf','application/pdf'));
    
    $this->mailer->send($email);
    

    Ideally, you'd delete the generated file afterward. Either immediately, or in a decoupled clean-up process.


    Alternatively, if you want to send it from memory directly, you could create your own processor to hold the PDF in a variable:

    use Sensiolabs\GotenbergBundle\Processor\ProcessorInterface;
    
    $processor = new class implements ProcessorInterface {
                private string $content = '';
                public function __invoke(?string $fileName): \Generator
    
                {
                    do {
                        $chunk = yield;
                        $this->content .= $chunk->getContent();
                    } while (!$chunk->isLast());
    
                    return $this->content;
                }
    
            };
    
    
    $pdfAsString = $this->gotenberg->url()
             ->url('https://sparksuite.github.io/simple-html-invoice-template/')
             ->processor($processor)
             ->generate()
             ->process();
    

    Now, $pdfAsString would be a variable holding the PDF in memory.

    You could now use the Email::attach() method to get that as an attachment to the email:

    $email = (new Email())
              ->from('[email protected]')
              ->to('[email protected]')
              ->subject('subject')
                        ->text('text')
                        ->attach($pdfAsString,'Contract.pdf','application/pdf'));