I've just updated from 5.3. to 5.4 and #16325 has introduced an undocumented breaking change.
Consider
// layout.blade.php
@push('js')
<script src="jquery.js"></script>
@endpush
@stack('js')
// child.blade.php
@extends('layout')
@push('js')
md5-ba96753bd29a42d37a3a8ba81578e159
@endpush
In 5.3, this would work. In 5.4, it does not.
The 5.3 functionality is obviously what is desired: adding common scripts to the layout that are always required, such as jquery, and then pushing to the stack in the called child view to add scripts specific to that view.
With this broken in 5.4, I'm not sure what the utility of the stack actually is.
Please clear your view cache, and if it's still broken, please submit a PR to our upgrading guide.
Thank you. :)
@GrahamCampbell The thing is, #16325 specifically discusses this change and then comes to the conclusion that the 5.4 way is correct, but I don't see how it is. The template inheritance system conventionally allows default in the parent and the overrides/extensions in the child. I'm not sure why it was changed, because it was working as intended in 5.3, and now pushing onto the stack gives you an outcome that is useless for what the stack was created for: assets.
To be honest, if this change remains, the documentation will need a note on stacks saying "don't use these with @extends and @section because you'll get unexpected results".
I think it'd be good to get @jbrooksuk and @mark86092's thoughts on this, and maybe even @taylorotwell's.
Looks like this has went back and forth a few times with the addition then removal of array_reverse. I've always thought of it the way it worked in 5.3 but as Taylor points out, the fact that it is "pushing" onto a stack, it seems to make more sense in the 5.4 usage. Since there are other ways to handle script inheritance in blade, it would probably be best to note this in the documentation and leave as-is. I'm guessing, due to the lack of responses, that not many apps are actually affected by this. However it is early in the release so there will probably be more in the coming weeks.
[...] To me push should render its contents in the order they were pushed. Otherwise its not a push its just a prepend.
Source: https://github.com/laravel/framework/pull/16325#issuecomment-260718331
The 5.4 ordering is correct. Whatever called push first will be displayed first, etc... Many just don't truly understand the ordering of when layouts / sections, etc. are rendered which can be make it seem like its not functioning this way.
In the example at the top of this page... Children are rendered before layouts. So, the childs stuff is first in the stack.
Using Components and slots can sometimes help this make more sense since they provide a more natural ordering of rendering.
@taylorotwell Thank you for your reply. I understand what you are saying about the ordering of rendering of parent/children dictating the order that pushing to the stack is actually happening, but I believe that the example of adding jquery dependent scripts in the child is a (the?) common use for the stack. CSS file order is also important.
Would adding the ability to output the stack in reverse be a solution e.g. @stack('js', true) or @stackReverse('js') or something along those lines? I'm just trying to think how to keep this functionality available for users of @extends/@section as well as @component/@slot. As it is, I'm going to have to convert back @section/@parent, which does the job fine, albeit not quite as elgantly or semantically.
Either way, I think information about this needs adding to the @stack documentation, and it definitely needs adding to the 5.4 upgrade guide (I would open a PR on the documentation, but I worry I'd write it too tersely/not match the tone).
Why even use "push" in your layout? It makes no sense. Just put the scripts in your header tag directly.
@ockle Are you adding the jquery.js script-tag from your layout? The layout file is executed after the view file, and anything pushed in the layout file will be appended after whatever the view or subviews pushes.
If your layout always outputs a jquery.js script-tag, just add it as a normal tag in your header.
// layout.blade.php
<script src="jquery.js"></script>
@stack('js')
@taylorotwell Yes, perhaps that example was too simple. Here's a better one:
// top.blade.php
<script src="jquery.js"></script>
<link href="main.css" rel="stylesheet">
@stack('test')
// middle.blade.php
@extends('top')
@push('test')
md5-ba96753bd29a42d37a3a8ba81578e159
<link href="css-that-overrides-main.css" rel="stylesheet">
@endpush
md5-3166ba0e8f9b341a58ace3797baa7528
// bottom.blade.php
@extends('middle')
@push('test')
md5-b704c3d12d1cbca3790345077391f541
<link href="css-that-overrides-middle.css" rel="stylesheet">
@endpush
md5-3166ba0e8f9b341a58ace3797baa7528
view('bottom')
md5-fcc584fd864bb6139ecd1bce1ac39875
// top.blade.php
md5-e2164d81a7afdf714f354229dcc3460b
<link href="main.css" rel="stylesheet">
@yield('test')
md5-3166ba0e8f9b341a58ace3797baa7528
// middle.blade.php
@extends('top')
@section('test')
md5-ba96753bd29a42d37a3a8ba81578e159
<link href="css-that-overrides-main.css" rel="stylesheet">
@endsection
md5-3166ba0e8f9b341a58ace3797baa7528
// bottom.blade.php
@extends('middle')
@section('test')
@parent
md5-b704c3d12d1cbca3790345077391f541
<link href="css-that-overrides-middle.css" rel="stylesheet">
@endsection
which is fine, works, and is what we were doing before @stack was introduced. I just feel that this inconsistency in the way @stack works depending on whether you are using @layout/@section or @component/@slot is a bit awkward. Perhaps it has to be too many things to too many people: to those using each type of inheritance model, as well as to those expecting it to facilitate the example above and to those expecting it to rigidly push in the order it is called.
Hopefully you can see where I'm coming from with this, and apologies for confusing what I'm trying to exhibit here with my over-simplified initial example.
The @extends indicate that the views is the "root-view" (for the lack off better word) and that the file mentioned in the @extends is the layout. As previously mentioned, layouts are rendered after the view. It looks like you're chaining several layout files, which will render every layout in the reverse order compare to what you expect (bottom, middle, top)
5.4 is the _correct_ and expected behaviour IMHO.
If we have a choice to decide either append or prepend on stack/push will be the best!
Most helpful comment
If we have a choice to decide either append or prepend on stack/push will be the best!