Apparently Google doesn't like it if all of your pages have the same title and meta description tags. So yesterday I decided to write unique titles and description tags for all of my pages. At first I did this by setting two variables - $title and $description - in my controllers and then passing them to the views, where I displayed them in my layout/app.blade.php. Since I have multiple languages in this site I ended up setting them like this:

$title = trans('whatever.pagetitle');
$description = trans('whatever.pagedescription');

This seemed a bit inelegant and I thought I could come up with a better way, which I did this morning. What I did was set up a file in my lang directory I called metadata.php. A sample of this is here. This file contains for each page a key for title and description as follows:

'/home-title' => 'Title';
'/home-description' => 'Description';

By using the URI appended with the value I want I was able to consolidate all of the values into one file for ease of use, and I was also able to make a helper function to get those values from the translation files and display it, so that the same exact code could be run on every single page and return the data I need. 

The helper function I used is on my GitHub here, and if it doesn't find data for the page it is looking for it has a default title and description it uses. For pages like blog articles and individual records I use the same title and description, but I still specify $title in the Controller, and if the value exists it is appended to the title in the layout file.

I like this solution because it allowed me to delete the redundant and ugly code in the controllers where I specified a title and description for each page with a function that pulls the data from one location, and if the data doesn't exist it substitutes a default in, instead of either failing or not doing anything. The code I used is on my GitHub Gist.

Labels: coding , laravel

No comments

More Packages

March 4, 2017, 2:04 p.m.

After having written a few packages I believe I now have it down cold. I started another one just a few hours ago and am already finished with it and had no problems at all this time. The latest package is escuccim/recordcollection which is a package with the code I use for my searchable database of vinyl records. Yesterday I did a package with the code for my online CV in it, so at this point this site is basically just the static pages and four Laravel packages. While it's a bit more complicated to make changes now because I need to go to the package code, alter it, then push it up to git and then composer update, I think the extra couple steps is well worth it in terms of maintainability and portability. My packages are largely self-contained, with their own views, controllers, models, etc. so I can just add them to a project and everything will (almost) magically work.

I don't really see much demand for a package like this for other people so I haven't thoroughly tested this one in projects other than my own, so it may not work properly out of the box. If you have a large record collection that you want to store online I would recommend Discogs.com. It has a lot more features and functions than my package does, but I don't have the time to go through each of my 2,000 or so records and add them to my Discogs collection and I've had most of mine in a database for about 15 years, so I'm sticking with my own for now. If I was starting over from scratch I'd probably put it in Discogs and then pull the data from their API to display it here.

My other packages are all tested and working on fresh installs of Laravel, so feel free to use them if you want. 

Update - I ended up testing the recordcollection package on an almost fresh install of Laravel and I fixed the issues I found, so it should be in mostly usable shape.

Labels: coding , laravel

No comments

Laravel Session Variables and Middleware

March 4, 2017, 2:04 p.m.

Back when I wrote the code to localize this site I ran into some unexpected behavior that I couldn't figure out. I have two ways to set the language here - first you can do it by subdomain, fr defaults to French. Then regardless of the subdomain you can use the drop-down menu in the navbar to set the language, which sets a session variable. To handle the subdomain I have a middleware which runs on every request and sets the locale to the language specified by the subdomain, if a subdomain is used. I was confused by why the subdomain could be overwritten by the session var set with the drop-down menu, but I ended up leaving it that way because it worked better than the way I had originally envisioned.

This weekend I decided to try to get to the bottom of why the behavior was different than what I would have expected and I discovered something a bit bizarre about Laravel sessions. In the middleware the session is always empty, but I can set a variable and access it from within the middleware. By the time I get to the controller the values put in session in the middleware are gone, and replaced with the values previously set in the session. I haven't looked at Laravel's session code yet, but I assume that however it stores session variables is initialized somewhere between the middleware and the controller. Before I started with Laravel, I used to keep session variables in $_SESSION, so the way it works now is a bit confusing to me.

To explain, I have the following in my middleware, which is registered to run on every request:

public function handle($request, Closure $next)
{
  echo "1: " . session('foo') . "
";
  session(['foo' => 'bar']);
  echo "2: " . session('foo') . "
";
  return $next($request);
}

When I load any page, it outputs:

1: 

2: bar

If I then in a controller execute:

session(['foo' => 'baz']);

And load another page which just contains:

echo "3. " . session('foo');

The output, with the middleware is:

1. 

2. bar

3. baz

So, in the middleware you can set and access the session, but the session doesn't persist past the request, and by the time the controller is executed that session has been replaced with a session that does persist from the previous request. I can think of a few ways around this, but it doesn't seem worth the effort involved. For me, the result of this issue is that I have to include a call to a helper function in every single page I want to translate - if I want to keep the drop-down menu to translate. My other option would be to just do the localization based on subdomain and have the drop-down menu link to the same page on a different subdomain instead of just setting a variable and reloading the same page, which may in fact be a better solution, but again maybe not worth the effort.

I don't know if anyone else has run into this behavior in Laravel, I also don't know if this behavior is intentional or not, but if you are trying to access or set session variables in a middleware with no luck this is likely the reason.

Labels: coding , laravel

No comments

I figured out how to resolve the session and middleware issues I mentioned in the previous post. I previously had the middleware in the $middleware array in /app/Http/Kernel.php. The Laravel session is started with the middleware StartSession, which is in the $middlewareGroups array under web. I moved my middleware to the web $middlewareGroups and put it after StartSession, and now the Middleware can access the session. The only difference is that the Middleware will only be run on requests that are part of the 'web' group instead of on every request, but this actually makes more sense in this case.

After having figured this out the unexpected behavior I was witnessing before makes sense now.

Labels: coding , laravel

No comments

Archives