laravelexceptionlaravel-8

Laravel 8: render exception as HTML for email


I upgraded my Laravel app to the latest version, Laravel 8. My problem now is that I cannot figure out how to render an exception thrown by the app to HTML in order to send that html as an email.

My current code (that worked for Laravel 5) to render and send an exception within the App\Exceptions\Handler class:

public function sendEmail(Exception $exception)
{
    try {
        $e = FlattenException::create($exception);

        $handler = new SymfonyExceptionHandler();

        $html = $handler->getHtml($e);

        $routeName = URL::full();

        Mail::send(new ExceptionEmail($html, $routeName));

    } catch (Exception $ex) {
        if (env("APP_DEBUG") == true) {
            dd($ex);
        }
    }
}

The problem is that class \Symfony\Component\Debug\Exception\FlattenException does not exist anymore in my upgraded app.

What is the appropriate way to render exceptions as HTML now in Laravel 8?


Solution

  • Alright i managed to receive email, here is the code

    Composer requirement

    "require": {
        "php": "^7.3|^8.0",
        .....
        "jeremykenedy/laravel-exception-notifier": "^2.2",
        "laravel/framework": "^8.40",
        .....
    },
    

    app/Exceptions/Handler.php

    <?php
    
    namespace App\Exceptions;
    
    use App\Mail\ExceptionOccurred;
    use Exception;
    use Throwable;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Log;
    use Illuminate\Support\Facades\Mail;
    use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
    use Symfony\Component\ErrorHandler\Exception\FlattenException;
    use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
    use Symfony\Component\HttpFoundation\Response;
    
    class Handler extends ExceptionHandler
    {
        /**
         * A list of the exception types that are not reported.
         *
         * @var array
         */
        protected $dontReport = [
            //
        ];
    
        /**
         * A list of the inputs that are never flashed for validation exceptions.
         *
         * @var array
         */
        protected $dontFlash = [
            'current_password',
            'password',
            'password_confirmation',
        ];
    
        /**
         * Register the exception handling callbacks for the application.
         *
         * @return void
         */
        public function register()
        {
            $this->reportable(function (Throwable $e) {
                //
            });
        }
    
        /**
         * Report or log an exception.
         *
         * @param Exception $e
         * @return void
         * @throws Throwable
         */
        public function report(Throwable $e)
        {
            if ($this->shouldReport($e)) {
                $this->sendEmail($e); // sends an email
            }
            parent::report($e);
        }
        /**
         * Render an exception into an HTTP response.
         *
         * @param Request $request
         * @param Throwable $e
         * @return Response
         *
         * @throws Throwable
         */
        public function render($request, Throwable $e): Response
        {
            return parent::render($request, $e);
        }
    
        public function sendEmail(Throwable $exception)
        {
            try {
                $e = FlattenException::createFromThrowable($exception);
                $handler = new HtmlErrorRenderer(true);
                $css = $handler->getStylesheet();
                $content = $handler->getBody($e);
                Mail::to('your_email_address_here')->send(new ExceptionOccurred($content,$css));
            } catch (Throwable $exception) {
                Log::error($exception);
            }
        }
    }
    

    app/Mail/ExceptionOccurred.php

    <?php
    
    namespace App\Mail;
    
    use Illuminate\Bus\Queueable;
    use Illuminate\Mail\Mailable;
    use Illuminate\Queue\SerializesModels;
    
    class ExceptionOccurred extends Mailable
    {
        use Queueable, SerializesModels;
    
        private $content;
        private $css;
    
        /**
         * Create a new message instance.
         *
         * @return void
         */
        public function __construct($content,$css)
        {
            $this->content = $content;
            $this->css = $css;
        }
    
        /**
         * Build the message.
         *
         * @return $this
         */
        public function build()
        {
            $emailsTo = str_getcsv(config('exceptions.emailExceptionsTo'), ',');
            $ccEmails = str_getcsv(config('exceptions.emailExceptionCCto'), ',');
            $bccEmails = str_getcsv(config('exceptions.emailExceptionBCCto'), ',');
            $fromSender = config('exceptions.emailExceptionFrom');
            $subject = config('exceptions.emailExceptionSubject');
    
            if ($emailsTo[0] === null) {
                $emailsTo = config('exceptions.emailExceptionsToDefault');
            }
    
            if ($ccEmails[0] === null) {
                $ccEmails = config('exceptions.emailExceptionCCtoDefault');
            }
    
            if ($bccEmails[0] === null) {
                $bccEmails = config('exceptions.emailExceptionBCCtoDefault');
            }
    
            if (! $fromSender) {
                $fromSender = config('exceptions.emailExceptionFromDefault');
            }
    
            if (! $subject) {
                $subject = config('exceptions.emailExceptionSubjectDefault');
            }
    
            return $this->from($fromSender)
                        ->to($emailsTo)
                        ->cc($ccEmails)
                        ->bcc($bccEmails)
                        ->subject($subject)
                        ->view(config('exceptions.emailExceptionView'))
                        ->with('content', $this->content)
                        ->with('css', $this->css);
        }
    }
    

    resources/views/emails/exception.blade.php

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8" />
        <style>{!! $css ?? '' !!}</style>
    </head>
    <body>
    {!! $content ?? '' !!}
    </body>
    </html>
    

    Please let me know if that works.