Laravel Blog Package

Sunday 08 January 2017

I finished working on my Laravel package, which is the blog I use here (and also on my other site). I had this on my GitHub as a Laravel skeleton application, but after a few days of research and coding I now have it as a Laravel package, which can be installed via Composer. I did find a more comprehensive tutorial on writing Laravel packages, but I only just found this today after I had finished my package, so haven't really read through it.

The reason I started working on this package is because I have multiple sites that use the same code and I wanted to consolidate them so I wouldn't have to maintain two separate code bases, but the package is only in English and some of my sites are in French, so I guess my next step is adding translation to the package.

The package is on Packagist and can be installed with composer. 

composer require escuccim/larablog

A few things that I struggled with and eventually figured out since my last post on this topic:

  • How to publish files from the package to the containing application. I did this for both my views and the config.php file.

    $this->publishes([

    __DIR__.'/config/config.php' => config_path('blog.php'),

    __DIR__ . '/resources/views' => base_path('resources/views/vendor/escuccim')

             ]);

  • To default to the package config if it has not been published use:

    $this->mergeConfigFrom(__DIR__.'/config/config.php', 'blog'); where 'blog' is the key for the config array.

  • How to load database migrations:

     $this->loadMigrationsFrom(__DIR__.'/database/migrations');

  • I also updated the code so that things like caching could be turned on and off from the config. 

There is still work to be done, but I just marked my GitHub repo with a stable release version, so that's something.

Labels: coding, laravel
No comments

Laravel Packages

Thursday 05 January 2017

I've been trying to figure out how to make packages for Laravel, and there isn't as much documentation as one would hope there is. The Laravel docs aren't as helpful as they could be for someone who has never done this before, and most of the info I found on Google was either incomplete or for older versions of Laravel.

I did find a few pages with helpful information on how to do this, this one is the one I followed. It uses this CLI tool, itself a Laravel package, which will allow you to make other Laravel packages. The CLI tool creates the directory structure along with composer.json and boilerplate code that provides a good starting point.

Other tutorials I found helpful include:

 I ran into a few problems which took some research to solve, which I thought I'd put here in case anyone else is having the same issues:

  • If you are getting an error that the service provider you created can't be found check the capitalization of the autoload in your composer.json file and make sure it matches the entry in the providers array in config/app.php. I had this problem when I first started working on my package, and then I decided to rename the package and ran into the same problem again.
  • Views - if your package has it's own views you can load them from the controller as "[vendorname]::[dir]/[view]", where [vendorname] is the vendor name of your package, which you should be creating in the register function in the service provider file.
  • Creating URLs in views - my views used action() to create the URLs and when I moved my controllers out my App directory all my links broke. I've tried using the full path to the controllers in my package couldn't get it to work so ended up replacing them all with url().
  • Using Models in your package - I created a directory under my package src/ called Models and I put all my Models in there. Make sure you change the namespace and update any use statements that reference the old location.

Of course I had other issues but those are the ones that took a while to figure out. I hope to finish the package up in the next few days, I'll post updates as they come.

Update - to use action() to create URLs you in fact do use the full path to the controller and it works this time. Not sure what I did wrong last time, but it is working fine now.

Labels: coding, laravel
1 comments

SSL and Let's Encrypt

Friday 30 December 2016

The first time I ever tried to install an SSL certificate on a web server was probably around 1999. At the time there were only a couple options - Verisign and Thawte if I remember correctly, the certificates cost a couple hundred dollars each, and you had to go through a lengthy and complicated process to get the certificate approved which involved compiling a lot of documentation (I remember being asked for a Dun & Bradstreet number for one thing), multiple phone calls, and took a couple weeks to complete. Once the certificate was finally approved and issued the process of trying to install it on the server was almost as complicated.

How times have changed. Yesterday I installed a certificate on this server. It was free and took about 15 minutes, most of which was spent trying to find the documentation. At first I was just messing around and decided to install a self-signed certificate, which was quick and easy, but having to click through the page which says that "this site is insecure" was nerve wracking, even knowing that it doesn't really mean anything. A quick Google search turned up lets encrypt which offers free SSL certificates that are recognized by most browsers.

As easy as installing an SSL certificate for Apache is, I then found CertBot which makes it even easier. The main page has instructions for different OSes and servers. For Ubuntu I just installed the certbot package and ran it, it asked me what domains I wanted the certificate to cover and for my email address and then generated it.

I was a bit wary of allowing CertBot to change my Apache config so I just had it generate the certificates and did the config myself. After I had no problems, on my other server I let CertBot do the config as well and had no problems at all. And when it was done SSL just worked, I didn't have to touch the config or even restart Apache, much less provide a DUNS number. I'd like to thank the EFF and Lets Encrypt for CertBot and for making this so easy.

Labels: coding
No comments

Update on Localization

Thursday 29 December 2016

After I had everything written and working I decided to go back and try to figure out why I couldn't run my function to get the language out of session and put it into the app config globally. It didn't make sense that I needed to cut and paste the same function into every single controller. So I tried it again as a helper function and this time it works perfectly. I have no idea why it didn't work before, but it's working now. I took the function out of the controllers and replaced it with a call to the helper, which is much better because I don't need to have the same exact code repeated in 10 different places, although it is a bit frustrating that I don't know why it didn't work at first.

I also added a call to setlocale() in the helper function which allows dates to be localized using strftime() instead of date(). I spent a while trying to get this working - I had to add the locales to the server using:

dpkg-reconfigure locales

And select the locales you want to use and then restart Apache. I wasn't able to get the date localization working on my local dev environment, for which I am using Homestead. I am still not sure why, the main difference between my production server and my dev environment is that the former uses Apache and the latter Nginx, so maybe it has something to do with that. As much as I hate not knowing why things don't work that should work, I'm not going to spend more time trying to figure it out since it is working here.

Labels: coding, localization
No comments

Laravel Localization

Sunday 18 December 2016

I decided to try to translate this site into French, given that I live in the French-speaking part of Switzerland. Laravel has a lot of great tools for localization built-in, but there were a few things sorely lacking. Laravel, by default, has localization files in /resources/lang/en. Each file is just an array with a key and the translated text as the value. If you want to add a new language you just copy the files over into a new directory, in this case /fr, and translate the text directly in there. In the views instead of typing in the text directly you call trans('file.key') and it pulls the text for 'key' out of the 'file.php' in the appropriate language directory. This couldn't be any easier.

The hard part was when I started trying to figure out how to set the language to be displayed automatically. Laravel pulls this value from config/app.php, and you can change this value easily, but because Laravel is RESTful it has to be done on every request. So I decided to stick the actual language in a session variable and then change the value in the config array if needed.

I tried to make a middleware to do this on each request, but this didn't work because it seemed as if the session either wasn't saving from middleware, or possibly wasn't initialized yet in the middleware. More on this below. So I abandoned the middleware route and added a function in my controller that sets the language thusly:

App::setLocale( session('locale') ? session('locale') : config('app.locale'));

This worked fine, I just need to make sure to call the function everytime a page may need to be translated. My next step was to try to add a subdomain 'fr.' that would automatically set the language to French. You can do this in the web.php routes file, but from what I can tell it needs to be called on every single route, which seemed like an awful lot of work for something that should be pretty easy. 

So I went back to the middleware and created a middleware called SetLanguage that I added to app\Http\Kernel.php so it runs on every request. The middleware is quite simply this:

$pieces = explode('.', $request->getHost());
if($pieces[0] == 'fr'){
    session(['locale' => 'fr']);
}
return $next($request);

And this works fine. I think the problems I had with the middleware the first time I tried it was that Laravel has changed how sessions are handled in new releases, and the Session facade no longer works or works differently. Instead you now use the session() helper function or call $request->session() to modify the session. I had been trying to use the Session facade.

One thing that seems a bit odd is that since the middleware runs on every request it should translate every page that is called from the fr. subdomain to French. In actuallity it initially sets the language to French, but if you change the language using the drop-down menu it keeps the selection. This doesn't seem right, but in this case the actual behavior makes more sense than the expected behavior, so I am ignoring this bug.

Once I got this figured out, the translation was a simple matter of replacing text in my views with references to the lang files, which went smoothly, although I did have to spend some time trying to figure out how to translate some technical terms into French, and still am not sure I have them all translated properly.  

Labels: coding, laravel, localization
No comments

Archives