Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

eithed's avatar

Blade layouts nesting issue

Hello,

I'm creating an email management system - emails are blade templates which are assembled in a controller, and then are passed to view, to display the content in the backend. As such I've:

Email template:

@extends('emails/layouts/default')

@section('content')
Test
@endsection

Model that assembles the content, with function render:

    public function render($with = [])
    {
        return View::make("emails.".$this->template_file)->with($with);
    }

Controller that passes given content of the email, with show method:

    public function show(Model $model)
    {
        return view('admin.emails.show')->with(['content' => $model->render()]);
    }

Backend template to present the email:

@extends('admin.layouts.master')

@section('content')
    <iframe class='rendered-content' srcdoc="{{$content}}"></iframe>
@stop 

The result is... unexpected - even though the views are separated, because they both use the content section, the backend template will display Test instead of <iframe class='rendered-content' srcdoc="Test"></iframe>.

I've to do, in Model:

    public function render($with = [])
    {
        return View::make("emails.".$this->template_file)->with($with)->render();
    }

to separate the layouts (scopes) of templates.

My questions is - is this a valid behaviour? Shouldn't layouts in this instance create their own scopes (as we're dealing with, de-facto, nested layouts) or does Blade layouts not support nesting and everything is stored in global scope (as is shown above) and there's no way to change this.

0 likes
4 replies
eithed's avatar

I'm sorry, but this doesn't answer my question - I'm not using @include here, nor I want to.

36864's avatar

You're telling your model to render a view called "emails.{{template_file}}". Assuming your first template is an example of one of these templates, you've defined it to extend a layout and then add some content to a section.

In your admin controller, you're trying to put an email view template into the content section of another template. I'm fairly sure this results in mashing the templates together before rendering, so you end up with 2 'content' sections being defined. You also end up with 2 'extends' directives in the template, which is just invalid syntax.

What you could do is split your email templates like this:

@extends('emails/layouts/default')

@section('content')
@include('emails.test')
@endsection

then you could change your model render to only render the actual content, and have your mailer fetch the full template.

Alternatively, I think you can use conditional extensions, but you'd still need to rename one of the sections and you might end up making more work for yourself down the line.

eithed's avatar

Yes, I'm telling the model to get the view email.{{template_file}} (template_file is attribute of the model, and in this instance indeed is example of one of those templates) and then to store it in a variable, then pass that variable to second view and output the result to the screen.

In the admin controller I'm displaying a separate template, with its own layout. These two layouts don't have anything to do with each other (except first outputting the content of the other). Yes, indeed I'd assume the end result is not mashing, but nesting of templates, like this:

@extends('admin.layouts.master')

@section('content')
    <iframe class='rendered-content' srcdoc="

@extends('emails/layouts/default')

@section('content')
Test
@endsection

"></iframe>
@stop 

but given that rendering of the templates should happen when passing and using the inner layout as variable and the other when outputting to the browser, I'd think the scopes of layouts would be separated.

The email template that has to be included is dynamic, and is a property of Model. I could do this:

@section('content')
@include($model->template_file)
@endsection

which I assume, would be interesting when caching (I truly don't know how Blade would deal with this). It made more sense to pass a variable.

Please bear in mind that it's a solved problem - as I've stated in OP that I've to explicitly make view render() and then I can pass given string (and not View object) into my backend view, but that on the other hand requires Blade to call render twice (instead having render be called only on output)

Please or to participate in this conversation.