I have some html emails that may be a bit too custom for a markdown, but I want to add the plain text versions to them. I know markdown emails do both, but can I manually add a plain text version to a mailable?
From the Laravel documentation:
If you would like to define a plain-text version of your email, you
may use the text method. Like the view method, the text method accepts
a template name which will be used to render the contents of the
email. You are free to define both a HTML and plain-text version of
your message:
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->view('emails.orders.shipped')
->text('emails.orders.shipped_plain');
}
Related
I want to get the render of an email before to send it.
I created a TemplatedEmail with htmlTemplate and context, it works fine for sending but how get the generated template with context to save it in database ? (customer needs)
I tried the getBody() but seems to work only with text template as I get A message must have a text or an HTML part or attachments.
$email = new TemplatedEmail();
$email->htmlTemplate($htmlTemplate);
$email->from($from)->to(...$to)->subject($subject);
$email->context($context);
dd($email->getBody());
I thought to use the render method but I'm in a service and not sure if it's a good way to store in database.
Symfony only renders the message when actually sending it, via an Event Listener. The class responsible from doing the rendering is BodyRenderer, from the Twig Bridge.
But nothing stops you from rendering the message yourself.
You have the template and the context variables, so you could simply inject Twig wherever you are doing the sending, render the template to a string and do whatever you need with that.
You could also register your own MessageEvent::class listener, set it with lower priority than the one registered by the Twig Bundle (it uses the default priority) so it's executed after that one, and then you could access the message body since it would have been rendered already. This is (very) slightly more complex, but you'd gain some performance since you wouldn't be rendering the template twice.
Which approach to use would depend on your application, your constraints, etc. But the important bit is to realize on what part of the process you'll find the body actually rendered, or render it yourself if you want it before that.
For future information it's possible to render the template in a service using the documentation here https://symfony.com/doc/current/templates.html#rendering-a-template-in-services
Here is solution (for logging templated email to DB, but can be easily customized to anything else) using EventListener
use App\Entity\EmailLog;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\Mailer\Event\MessageEvent;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
#[AsEventListener(event: MessageEvent::class, method: 'onEmailSent', priority: -1)]
class EmailLogListener
{
public function __construct(
private EntityManagerInterface $entityManager
) {}
public function onEmailSent(MessageEvent $event): void
{
$message = $event->getMessage();
if (!$message instanceof Email) {
return;
}
$log = new EmailLog();
$log
->setSentFrom(array_map(function (Address $address) {
return $address->getAddress();
}, $message->getFrom()))
->setSentTo(array_map(function (Address $address) {
return $address->getAddress();
}, $message->getTo()))
->setSubject($message->getSubject())
->setText($message->getHtmlBody());
$this->entityManager->persist($log);
$this->entityManager->flush();
}
}
during the conception of my web site using laravel 7 I found some troubles when I wanted to customize my laravel customize my Laravel email verification message.
first, I went to this file "C:\xampp\htdocs\clinique_juridique\vendor\laravel\framework\src\Illuminate\Auth\Notifications\VerifyEmail.php",then i translate the sentences that are there .
Here I met two issues, the first one some sentences are not in VerifyEmail.php so I couldn't translate them the second one, I didn't know how to change the style of the message, of course, also I didn't know how to include the logo of my website.
thank you in advance.
So I think you're asking about this specific part of the code (took from Laravel 8 vendor code):
/**
* Build the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
$verificationUrl = $this->verificationUrl($notifiable);
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
}
return (new MailMessage)
->subject(Lang::get('Verify Email Address'))
->line(Lang::get('Please click the button below to verify your email address.'))
->action(Lang::get('Verify Email Address'), $verificationUrl)
->line(Lang::get('If you did not create an account, no further action is required.'));
}
First, you may use this class VerifyEmail and method toMail to send this confirmation email to your client, but if you look closer, you can identify that this function is using the Lang class, which can be found at Laravel Localization.
Otherwise, if you want to make it more complex you can create your own class MyVerifyEmail which extends VerifyEmail and recreate toMail method to serve your needs, but I can't think of a scenario where you do this.
Second, if you want to include some personalized style of your mail, try creating a Mailable, with this approach you can use a custom view (of course including a logo).
And finally, I encourage to do not to modify the vendor's directory, because it stores all the libraries that laravel needs to work properly. Instead, extend any class which you may want to modify.
I need to edit template which used for emails in admin panel. Any Ideas?
I think about several ways:
Save email template in DB in the text field, edit it in the admin panel and then display the text in the blade's view.
The problem of this way's realization is I have to display php variables in blade template and then use the final code as the html for email. I think, it's so difficult for Laravel.
And the additional problem is if I store {{ $var }} in template's text in DB - it will display as the text, blade compiler doesn't process it.
Store only the static text information from email in the DB and then display it in the template. PHP variables will transfer separately.
This way will solve the problem with the php var's display, but I still don't know how to use the final code in the Mail::send, because Laravel allows using the template's name only, not a HTML, as I know...
I think about the following way:
$view = view('template')->render();
mail(..., $view, ...);
But I don't want to use it because I want use Mail::queue() for querying emails and I don't know how to use it with PHP mail().
Thanks to everybody for replies.
You can create your own variable syntax and store email template as text in your DB. Foe example, you can store each variable as ${VARIABLE_KEY} string.
Then during email preparation you should resolve all such constructions into their real values. I don't know which variables are required, but during email preparation you should execute these steps:
Load email template from DB.
Replace all ${VARIABLE_KEY} with their real values.
You can use regular expressions for the searching and replacement, but also you can use functions such str_replace. For example, if you want to paste email of the current user into your email (and your table for model User has an email field), then you can create variable: ${user.name} and then replace this manually with simple str_replace function:
$variables['${user.name}'] = Auth::user()->email;
str_replace(array_keys($variables), array_values($variables), $yourEmailTemplateBody);
Also you can do replacements by the same method not only in the email template body, but in the email subject too.
Then you have to create your own class which extends Laravel Illuminate\Mail\Mailable class. In this class you should define build method, where you can use not only the name of the view, but also some additional parameters, like in the "regular" view, for example:
class SomeClassName extends Mailable
{
public function build()
{
$email = $this->view('mail.common', [
'mail_header' => 'some header',
'mail_footer' => 'some footer',
])->subject('Your subject');
...
return $email;
}
For example, in your view you can store layout for entire email with some extra parameters: footer and header as in my example.
Also you can create more complex syntax for ${VARIABLE_NAME} constructions, for example, VARIABLE_NAME can be a method definition in PHP or Laravel notation, i.e.: SomeClass::someStaticMethod. You can detect this case and resolve SomeClass via Laravel Service Container. Also it can be an object.field notation, for example, user.email, where user is the current Auth::user().
But be carefull in this cases: if you will grant ability to edit email templates with this variables for all users, you should filter fields or available methods and classes for calling to prevent executing any method of any available class in your email template or to prevent display private information.
You can read about writing mailables in Laravel documentation
I was doing this for a project yesterday and found a good post describing Alexander's answer in more detail. The core is creating an EmailTemplate model with this method:
public function parse($data)
{
$parsed = preg_replace_callback('/{{(.*?)}}/', function ($matches) use ($data) {
list($shortCode, $index) = $matches;
if( isset($data[$index]) ) {
return $data[$index];
} else {
throw new Exception("Shortcode {$shortCode} not found in template id {$this->id}", 1);
}
}, $this->content);
return $parsed;
}
Example usage:
$template = EmailTemplate::where('name', 'welcome-email')->first();
Mail::send([], [], function($message) use ($template, $user)
{
$data = [
'firstname' => $user->firstname
];
$message->to($user->email, $user->fullname)
->subject($template->subject)
->setBody($template->parse($data));
});
For all the details (db migration, unit test, etc), see the original post at http://tnt.studio/blog/email-templates-from-database
You can simply use this awesome laravel package:
https://github.com/Qoraiche/laravel-mail-editor
Features (from readme file):
Create mailables without using command line.
Preview/Edit all your mailables at a single place.
Templates (more than 20+ ready to use email templates).
WYSIWYG Email HTML/Markdown editor.
I recently upgraded to laravel 5.4 and have been able to successfully build and send a markdown mailable email to my mailtrap.io and my personal email with no problem and it works great! My problem is I use a third party email service that requires text and html views in separate string variables. I know I can build separate templates to accomplish this, but I would like to use the convenience and efficiency of markdown mailables. I have a MailController.php where I validate and build information needed to send this request:
Mail::to('mypersonal#email.com')->send(new RequestShowing($jrequest));
This runs through my mailable class RequestShowing shown here:
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class RequestShowing extends Mailable
{
use Queueable, SerializesModels;
public $request;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($request)
{
$this->request = $request;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->from('sally#johnson.com')
->to('mypersonal#email.com')
->replyTo('sally#johnson.com')
->subject('Contact from '.$this->request->name)
->markdown('emails.request-showing');
}
}
This all works fine with mailtrap.io and testing, but in order to get the text and html views I run the following in my MailController.php
$inquiry = new RequestShowing($jrequest);
dd($jrequest, $jrequest->email, $inquiry);
I get this output with no objects for "view" or "textView"
RequestShowing {#637 ▼
+request: {#665 ▼
+"name": "Sally Johnson"
+"email": "sally#johnson.com"
+"phone": "406-333-5555"
+"comment": "This is a test email"
+"renturl": "http://localhost:8000/for-rent/apartment/3409-1st-ave-n-billings-mt-59101/178"
}
+from: []
+to: []
+cc: []
+bcc: []
+replyTo: []
+subject: null
#markdown: null
+view: null <== need html view
+textView: null <== need text view
+viewData: []
+attachments: []
+rawAttachments: []
+callbacks: []
+connection: null
+queue: null
+delay: null
}
This is where I am stuck. I have looked through forums and documentation with no answer on how to get the two views for my third party email provider.
The documentation for the mailable output is here:
https://laravel.com/api/master/Illuminate/Mail/Mailable.html
I don't understand why the $jrequest is preserved and other objects are not displayed, unless they are used and disposed of immediately. Any insights or help is appreciated. Thanks!
As usual, familiarity with documentation helps so on the Mail page under configuring the view I found my answer in the Plain Text Emails section:
https://laravel.com/docs/master/mail#configuring-the-view
Like the view method, the text method accepts a template name which will be used to render the contents of the email. You are free to define both a HTML and plain-text version of your message:
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->view('emails.orders.shipped')
->text('emails.orders.shipped_plain');
}
So it appears markdown combines the two views together in the markdown format and the non-markdown methods require building two templates for every email.
A side comment is that I checked the Analysis tab at Mailtrap.io for the markdown emails and the spam score is 0.0 - The spam score for my simple little html template tests was 1.8 which isn't bad for something simple.
I think I will look for another delivery method since that score is too good to pass up! The folks that built the Markdown method have done all the tedious work of avoiding spam ratings and I am all for that!!
it appears markdown combines the two views together in the markdown format and the non-markdown methods require building two templates for every email.
Yes, that's absolutely correct.
Mailtrap helps with debugging
when viewing a message in Mailtrap the Text tab shows the plain text version, if it exists. If it does not exist in the message, the tab is grayed out.
the Raw tab shows the whole message, and you'll see: Content-Type: text/plain heading the plain text message if it exists, and, following it, Content-Type: text/html for the html version.
You are absolutely right that before 5.4, ->view('my-email-view-here'); generates no plain text version at all (unless you add ->text('plain-text-version-here').
In contrast, ->markdown('my-markdown-email-view-here');generates a plain text version in addition to the markdown-to-html render, by default.
I've got a prestashop setup that has a small 'customization form' that currently saves the information to the products default customization text input. I did this to save time on having to write a complete custom module to add additional customization form fields and such.
Currently all the inputs are serialized (json) and entered as a long string into the text input like this:
Client Customization: %5B%5B%7B%22name%22%3A%22trophy%5B1%5D%5Bline1%5D%22%2C%22engraving%22%3A%22Test%20Trophy%22%7D%2C%7B%22name%22%3A%22trophy%5B1%5D%5Bline2%5D%22%2C%22engraving%22%3A%22test%20trophy%22%7D%2C%7B%22name%22%3A%22trophy%5B1%5D%5Bline3%5D%22%2C%22engraving%22%3A%221111111%22%7D%5D%5D
On the front end - when the customized data is displayed I can use PHP to decode & display it appropriately.
Is there a way where I can change that globally somewhere so I don't have to try and find every place where it might display and add that PHP code?
I'm running into the issue that I can't seem to find where to add the PHP code to 'decode' that string for the emails that are being sent out - so the long ugly string is being seen instead of the nice few lines of customization the user entered.
Any thoughts on how to handle this? Is there a spot where I can globally assign the decoded string to the products customization?
You could either try the PaymentModule class to decode the string just before the emails are sent, or Product's method called "getAllCustomizedDatas" for a more "global" approach.
And then test a lot, of course :)
Here's a quick draft of the second approach:
<?php
class Product extends ProductCore
{
public static function getAllCustomizedDatas($id_cart, $id_lang = null, $only_in_cart = true, $id_shop = null)
{
$datas = parent::getAllCustomizedDatas($id_cart, $id_lang, $only_in_cart, $id_shop);
/*
* Iterate over $datas, you're looking for
* [id_product][id_product_attribute][id_address_delivery][id_customization][datas]
* Datas will contain an array of fields broken by their type. You can then decode
* the ones that need to be decoded and return the result:
*/
return $datas;
}
}