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.

Libellés: coding, laravel
Aucun commentaire

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.

Libellés: coding, laravel
Aucun commentaire

New Localization Package

lundi 23 janvier 2017

As a result of figuring out what was going on with the session and the middleware yesterday I was able to rewrite my localization code and greatly simplify the whole project. Previously I had been calling a function to set the language in every single controller action that returned a view, I was able to eliminate all of that and consolidate everything in the middleware.

I made another package - escuccim/translate - that has two parts:

First is the middleware which does two things:

   a. Checks the subdomain to see if the subdomain corresponds to a language. If so it sets the app locale to the appropriate language.

   b. Checks to see if there is a session variable with the language in it, if so it sets the app locale accordingly.

The key for me here is that if there is a locale specified by both the subdomain and the session, the session takes precedence, thus allowing the user to display the page in whatever language they desire, irregardless of the subdomain.

The second component of the package is a route which accepts a locale as a parameter and sets a session variable to that locale, so that the middleware can then access that information.

This package is available on my GitHub and my Packagist. When I am done testing it you can install it via composer.

I'm glad I took the time to investigate the session/middleware issues because figuring that out allowed me to replace code that was unneccessary and ugly to look at with a nice, simple, elegant solution.

Libellés: coding, laravel, localization
Aucun commentaire

More Packages

mercredi 18 janvier 2017

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.

Libellés: coding, laravel
Aucun commentaire

Using Laravel Packages

dimanche 15 janvier 2017

Since I have multiple sites that use almost the same code I have been trying to consolidate shared code into Laravel packages for ease of maintenance. This weekend I did my second package which is escuccim/sitemap which contains my code for generating XML sitemaps for Google. Since I have this site available in more than one language and I use subdomains to set the default language it made for very messy and confusing hardcoded sitemaps. I was able to shrink the code for each sitemap down from hundreds of lines to about 50 by putting the subdomains and the corresponding language in a DB table and then looping through them to output the URLs and hreflang tags in the sitemap. This time the process of writing the package was quick and easy using the same method that I struggled with last time.

Once I had that working I went back to my LaraBlog package which I added translation functionality to. I had one big problem which took me a while to figure out which was that it wasn't loading the translations at all, it was just displaying the key: 'escuccim::blog.key'. I researched this and found no answers, but was able to solve it by changing the namespace or hint to larablog. I am not sure why this worked, but I suspect it may be because I was using the namespace escuccim for the views and maybe they conflicted? Anyway if anyone else is having this issue try to change the namespace/hint.

When I had the blog package translating properly I deleted the code I was using for this site for the blog and the sitemap and replaced it with the new packages. So far everything seems fine, but I will give it a day or two before to turn up any issues before I start using the packages in other places. 

I have a few other things I want to put into packages, and I just have to say that Composer makes my life so much easier! Instead of having to go through my code line by line to copy changes from one place to another while avoiding any functionality that differs from one project to the next I just update the package and then composer does the rest!

Libellés: coding, laravel
Aucun commentaire

Archives du Blogue