nested layouts in laravel emails - php

I am working with a part of my application that sends out emails the moment, I want the emails to use a base layout (branding etc) and then an individual template loaded in within that, that is specific to the email type, I know that you can do layouts in views like this when using a controller and view,
protected $layout = 'layouts.master';
however I cannot find a suitable way to use this when sending an email like this,
Mail::send('emails.organisations.admin_removed', $array, function($message) use ($array) {
$message->to("email#email.com", "Simon Ainley")
->from('sender#email.com', 'Email Sender')
->subject("Email Sender - Subject");
});
Is it possible to have a master email template and then feed in sub views?

Yeah! this is possible.
Your main template (emails.orgainsations.template_layout.blade.php)
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="description" content=""/>
<meta name="author" content=""/>
<title>{{config('app.project_name')}}</title>
<style>
//your css goes here
</style>
</head>
<body>
<table width="600" cellspacing="0" cellpadding="0" border="0" class="wrap">
<tbody>
<tr>
<td class="header">
<h1 class="logo" title="Logo"><img src="{{ asset('assets/images/logo-mail.png') }}" alt="logo" /></h1>
</td>
</tr>
<tr>
<td style="padding: 20px; border: 1px solid #8a35e1; color: #323232;">
#yield('content')
</td>
</tr>
</tbody>
</table>
</body>
Your child template (emails.organisations.admin_removed)
#extends('emails.orgainsations.template_layout')
#section('content')
<p>
your mail content goes here
</p>
#endsection
And, in mail sending code, call the child template as usual
Mail::send('emails.organisations.admin_removed', $array, function($message) use ($array) {
$message->to("email#email.com", "Simon Ainley")
->from('sender#email.com', 'Email Sender')
->subject("Email Sender - Subject");
});

What you want to do is create a master blade template and the extension is done in the child blade templates. Then you pass the child template to your Mail::send() method.
So you have your master: emails/layouts/master.blade.php
<html>
<head>
<title>Email Master</title>
</head>
<body>
Dear {{ $recipient }}
<div class="container">
#yield('content')
</div>
Kind regards,
#include('emails/signature')
</body>
</html>
Then extend in the child template: emails/layouts/organisations/admin/remove.blade.php
#extends('emails.layouts.master')
#section('content')
<p>You have been removed</p>
#endsection
Then pass the child view to the send method:
Mail::send('emails.layouts.organisations.admin.remove', $array, function($message) use ($array) {
$message->to("email#email.com", "Simon Ainley")
->from('sender#email.com', 'Email Sender')
->subject("Email Sender - Subject");
});
I've glossed over a lot of details here so I highly recommend checking out the docs for further in-depth details: https://laravel.com/docs/5.2/blade
You should also try to familiarise yourself with all the different directives blade offers to take full advantage of all its features.

Related

Laravel: Pass variable to Blade Sub Component

There are many questions here that are similar to mine, such as:
Pass variable to a component Laravel 9
Cannot access Blade Component variables inside the view
Passing variable from component into a slot
but I have not found a resolution to my problem in any of them. I've also scoured the doc at: https://laravel.com/docs/9.x/blade#components
I would have thought this would be a simple task.
I have the following structure, in Resources/views:
start.blade.php (uses)
components/layout.blade.php (uses)
components/head.blade.php
From my controller, I call:
$data =
[
'total' => $total,
'uses_form' => true
];
return view('start', $data);
Inside start.blade.php, I have:
<x-layout>
<x-slot name="uses_form">
{{ $uses_form }}
</x-slot>
<!-- additional html -->
</x-layout>
Inside layout.blade.php:
<!DOCTYPE html>
<html lang="en">
<x-head :uses_form:$uses_form />
<!-- also tried: #include('includes.header', ['uses_form' => $uses_form])-->
<body>
<div class="container-fluid">
{{ $slot }}
</div><!--container-fluid-->
</body>
</html>
Finally, in head.blade.php:
<head>
<link type='text/css' rel='stylesheet' href='/css/vendor/bootstrap.min.css' />
#if ($uses_form === true)
<link type='text/css' rel='stylesheet' href='/css/form.css' />
#endif
</head>
This results in an:
Undefined variable $uses_form
at:
head.blade.php: line 3
How can I get the variable to be recognized in head.blade.php?
I'm open to other approaches, such as generated the conditional style include, at a higher level, but I rather keep this check in head.blade.php for maintainability.
Using Laravel version 9.48.0
Edit (Additional Notes)
If I var_dump $uses_form from the top of layout.blade.php, I get the following:
object(Illuminate\View\ComponentSlot)#301 (2) {
["attributes"]=> object(Illuminate\View\ComponentAttributeBag)#299 (1) {
["attributes":protected]=> array(0) { }
}
["contents":protected]=> string(1) "1"
}
So, it's already changed form.
You can try to pass the variable $uses_form from the parent component layout.blade.php to the child component head.blade.php using following changes and I hope it will helpful to you.
layout.blade.php look like:
<!DOCTYPE html>
<html lang="en">
<x-head :uses-form="$uses_form"/>
<body>
<div class="container-fluid">
{{ $slot }}
</div><!--container-fluid-->
</body>
</html>
head.blade.php look like:
<head>
<link type='text/css' rel='stylesheet' href='/css/vendor/bootstrap.min.css' />
#if ($usesForm === true)
<link type='text/css' rel='stylesheet' href='/css/form.css' />
#endif
</head>
I got it working. There were a few things needing adjusting, but, mainly, I needed to modify the component classes to accept the parameter data.
Another change that was needed, was to pass the data to the layout component, at the top of start.blade.php, like so:
<x-layout :hasForm="$hasForm">
Also, as indicated in another answer and in comments, I needed to place the variable in quotes.
Finally, I'm not sure if this was part of problem, but I changed the uses_form variable from snake_case to camelCase.
The changes, from the question are such:
Controller:
$data =
[
'total' => $total,
'hasForm' => true
];
return view('start', $data);
start.blade.php
<x-layout :hasForm="$hasForm">
<!-- additional html -->
</x-layout>
app/View/Components/Layout.php
class Layout extends Component
{
public $hasForm;
public function __construct($hasForm)
{
$this->hasForm = $hasForm;
}
}
layout.blade.php
<!DOCTYPE html>
<html lang="en">
<x-head :hasForm:"$hasForm" />
<body>
<div class="container-fluid">
{{ $slot }}
</div><!--container-fluid-->
</body>
</html>
app/Views/Components/Head.php
class Head extends Component
{
public $hasForm;
public function __construct($hasForm)
{
$this->hasForm = $hasForm;
}
}
head.blade.php
<head>
<link type='text/css' rel='stylesheet' href='/css/vendor/bootstrap.min.css' />
#if ($hasForm === true)
<link type='text/css' rel='stylesheet' href='/css/form.css' />
#endif
</head>
Hope this may help someone else, down the line.

How comment script tag content in blade.php?

I try build a React Laravel webpage.
The main.blade.php responsible the layout and that pass data and React components to other blade
I test my app and make few blade.php
So I try comment out several way this line:
/* const data = {{ Illuminate\Support\Js::from($data)}};*/
but not work. It still perceives it as if it were there
I have to pass data for every route for testing because I can't comment the above line
Route::get('/react', function () {
$data = User::find(1);
return view('reactpractice', compact("data"));
})->name('react');
main.blade.php
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/app.css"/>
<title>#yield('title')</title>
<title>{{env('APP_NAME')}}</title>
</head>
<body>
<header>
Motto
</header>
<nav>
Index
About
React
Users
Login
Register
</nav>
#yield('contenttop')
#if(Session::has('successmessage'))
<div class="alert alert-success my-3">
{{Session::get('successmessage')}}
</div>
#endif
#yield('example')
<script>
const data = {{ Illuminate\Support\Js::from($data)}};
const token = '{{#csrf_token()}}'
</script>
<h2> React</h2>
<script src="js/app.js"></script>
</body>
</html>
So how comment script tag content in blade.php?
Rather than comment the line in JavaScript, why not make it conditional?
#if (isset($data))
const data = {{ Illuminate\Support\Js::from($data)}};
#endif
This way, if $data is provided, the JavaScript variable will be created. If $data is missing, it will be ignored and won't break your code.
<!-- Try like this -->
// or /* */
I hope it was helpfull.

Unable to send HTML email in Laravel 5.8

I am having a difficult time sending HTML email through my Laravel application. I have tried to return the view and it returns well but when I send the email to an actual email, it arrives as a plain text email without all the HTML parsing.
My View layout for the email is as follows:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap"
rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Roboto&display=swap"
rel="stylesheet">
<!-- Bootstrap CSS File -->
<link href="{{ url('design-assets/lib/bootstrap/css/bootstrap.min.css')}}"
rel="stylesheet">
<!-- Main Stylesheet File -->
<link href="{{ url('design-assets/css/style.css" rel="stylesheet')}}">
<link href="{{ url('design-assets/css/prof-custom.css')}}"
rel="stylesheet">
#yield('styles')
</head>
<body id="body">
#yield('content')
<!-- JavaScript Libraries -->
<script src="{{ url('design-assets/lib/jquery/jquery.min.js')}}"></script>
<script src="{{ url('design-assets/lib/jquery/jquery-migrate.min.js')}}">
</script>
<script src="{{ url('design-assets/lib/bootstrap/js/bootstrap.bundle.min.js') }}/"></script>
<!-- Contact Form JavaScript File -->
<!-- Template Main Javascript File -->
<script src="{{ url('design-assets/js/main.js') }}"></script>
#yield('scripts')
</body>
The actual email view is as follows:
#extends('layouts.mail')
#section('content')
<div class="card montserrat-font">
<div class="card-header">
<div class="card-title">Contact Message from the Website</div>
</div>
<div class="card-body">
<div class="card-text">
Dear Sir,<br /><br />
We have a contact from our events website with the following details<br/><br />
</div>
<div class="table-responsive">
<table class="table table-striped">
<tr>
<th>Name of Sender:</th><td>{{$mail_info['name']}}</td>
</tr>
<tr>
<th>Email of Sender:</th><td>{{$mail_info['email']}}</td>
</tr>
<tr>
<th>Phone number of Sender:</th><td>{{$mail_info['phone']}}</td>
</tr>
<tr>
<th>Subject of Message:</th><td>{{$mail_info['subject']}}</td>
</tr>
{{-- <tr>
<th>Message:</th><td>{{$mail_info['message']}}</td>
</tr> --}}
</table>
</div>
<div class="card-title">Message body</div>
<div class="card-text">
{!! $mail_info['message'] !!}
</div>
</div>
</div>
#endsection
The problem is that when I check the format returned from the email view by returning the view as follows:
return new \App\Mail\ContactMail(['email' => 'testemail#gmail.com', 'name' => 'Testing Name','phone'=>'08033776502', 'subject' => 'Just a test', 'message' => "This is a message returned from testing the view email template"]);
I get a view that represents exactly what I want but when I send the email, it arrives as a plain text email
This is how I call the view through mailable class
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class ContactMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* #return void
*/
public $mail_info;
public function __construct($mail_info_array)
{
$this->mail_info = $mail_info_array;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
// return $this->view('view.name');
return $this->view('mails.web-contact', ['mail_info'=>$this->mail_info]);
}
}
and then through the controller as follows:
public function post_contact(Request $request)
{
try
{
$data = $request->all();
$this->validate_recaptcher($data['g-recaptcha-response']);
$this->validator($request->all())->validate();
\Mail::to('uchendukwe#yahoo.com')->send(new \App\Mail\ContactMail(['email' => $request->email, 'name' => $request->name,'phone'=>$request->phone, 'subject' => $request->subject, 'message' => $request->message]));
return redirect()->route('ContactForm')->with('msg',"<div class='alert alert-success'><span class='fa fa-check'></span> Message successfully sent. I will get back to you soon if necessary</div>");
// return new \App\Mail\ContactMail(['email' => $request->email, 'name'=> $request->name,'phone'=>$request->phone, 'subject' => $request->subject, 'message' => $request->message]);
}
catch(Exception $e)
{
return back()->withInputs()->with('msg',"<div class='alert alert-danger'><span class='fa fa-warning'></span> ".$e->getMessage()."</div>");
}
}
I am using smtp email driver and every other thing is working as expected.
I will appreciate any guide to resolve this
Thank you
Add your styles inline in the head portion of your email layout. Mail clients tend to ignore externally referenced css. Example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
...
<style type="text/css">
your custom styles here
</style>
...
</head>
</html>
You can accomplish this using Markdown.
Customizing The CSS
After exporting the components, the resources/views/vendor/mail/html/themes directory will contain a default.css file. You may customize the CSS in this file and your styles will automatically be in-lined within the HTML representations of your Markdown mail messages.

Passing array from view(Blade) to another view(Blade) in laravel

I have master layout of laravel blade template main.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>#yield('title','My Site')</title>
</head>
<body>
#yield('container','Content area')
{{ HTML::script('assets/js/jquery-1.9.0.js') }}
</body>
</html>
I extended it for another view index.blade.php
#extend('main')
I want to add javascript links to master layout(main.blade.php) if exists in child layout(index.blade.php)
added code in index.blade.php
$extraJs = array('plugins1.js','plugins2.js');
#section('extraJs')
{{ $extraJs }}
#stop
So, How can I add javascript to master layout if exists in child blade?
In php:
<?php
if(isset($extraJs)):
foreach($extraJs as $js):
?>
<script src="assets/js/<?php echo $js ?>" type="text/javascript" />
<?php endforeach; endif; ?>
You can just add a yield in your main.blade.php, like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>#yield('title','My Site')</title>
</head>
<body>
#yield('container','Content area')
{{ HTML::script('assets/js/jquery-1.9.0.js') }}
#yield('javascripts')
</body>
</html>
Then, in your child layout, you add a section for it, like you did. For example:
#section('javascripts')
#foreach($scripts as $script)
{{ HTML::script('ets/js/' . $script) }}
#endforeach
#stop
If you add another child view which doesn't need to use extra javascripts, Laravel won't complain because the section is optional.
I wouldn't really encourage this method but since you asked. It is definitely possible to pass something "up" in the view tree. You can just define a PHP variable in your view and it will be available anywhere in the layout.
index.blade.php
<?php $extraJs = array('plugins1.js','plugins2.js'); ?>
main.blade.php
#if(isset($extraJs))
#foreach($extraJs as $js)
<script src="{{ asset('assets/js/'.$js) }}" type="text/javascript" />
#endforeach
#endif
As you can see I also converted your code in the layout to blade and made use of the asset() function to generate absolute URLs
Note Instead of doing it this way there are other methods. For example View composers or defining the plugins in the controller.

Mustache partials and code reuse

I'm getting the hang of mustache for a project I've started during the weekend.
I'm using the PHP implementation. I have, however a couple of inquiries as I'm not used to the system.
How do you handle template inheritance, or reuse?
I know of partials, but how should I use them? I'm doing something like this, ala include:
top.mustache:
<!DOCTYPE html>
<html lang='es'>
<head>
<meta charset=utf-8" />
<link rel="stylesheet" href="/media/style.css" type="text/css" media="screen" />
</head>
<body>
<header><h1>Top</h1>
</header>
<section>
bottom.mustache:
</section>
<footer>potaje</footer>
</body>
</html>
And a view to render this template:
{{>top}}
<form action="/album/" method="post">
<p><label for="name">Name</label> <input type="text" name="name" value=""/></p>
<p><label for="description">Description</label> <textarea name="description" rows="8" cols="40"></textarea></p>
<p><input type="submit" value="Save" /></p>
</form>
{{>bottom }}
Is this the right approach?
Here is an example of how the php implementation of Mustache works. Of note is that Mustache.php will not expand the included partials/templates, so you have to hand them to mustache as seen below. This example was pieced together on an older cakephp framework.
<?
# php cannot recognize multiple acceptable file extensions for partials,
# so toggle it to mustache's extension
$this->ext = '.mustache';
# Mustache says it's logic-less, but that's not exactly true.
# Render out the basic header which calls the logo partial and has
# a {{# logged_in? }} condition which dictates which user box we
# show. Thus, we need to render out the html for both the logged in
# and logged out user boxes
$basic_header_html = $this->renderElement('basic_header');
$logo = $this->renderElement('shared/logo');
$logged_in = $this->renderElement('shared/logged_in_user_box');
$logged_out = $this->renderElement('shared/logged_out_user_box');
$m = new Mustache($basic_header_html,
array('logged_in?' => !empty($this->Auth->userData),
'cache_buster' => time(),
'display_name' => 'StackOverflow Customer'),
array('shared/logo' => $logo,
'shared/logged_in_user_box' => $logged_in,
'shared/logged_out_user_box' => $logged_out));
?>
<!DOCTYPE html>
<html>
<head>
<title>Mustache Test</title>
</head>
<body>
<?= $m->render(); ?>
</body>
</html>
basic_header.mustache
<div id="header" class="basic">
{{> shared/logo }}
{{# logged_in? }}
{{> shared/logged_in_user_box }}
{{/ logged_in? }}
{{^ logged_in? }}
{{> shared/logged_out_user_box }}
{{/ logged_in? }}
</div>
shared/logo.mustache
<a class="logo" href="/foo/bar"><img alt="" src="/images/logo.png?{{ cache_buster }}" /></a>
shared/logged_in_user_box.mustache
Hello {{display_name}}, you are logged in.
shared/logged_out_user_box.mustache
Hello. You are not logged in.
ynkr's answer is right for older versions, but I just upgraded to version 2.4.1 and there your approach should work if you're using the filesystemloader.
See https://github.com/bobthecow/mustache.php/wiki/Template-Loading#partials-loading for details.

Categories