Using Laravel Packages

Feb. 28, 2017, 5:02 p.m.

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!

Labels: coding , laravel

No comments

Dynamic Routing in Laravel

Feb. 28, 2017, 4:47 p.m.

LaravelI was just dealing with an issue where I wanted to create routes from the database. The site has pages which are contained in a database table and I had a route which took in the name or id as a parameter and rendered the appropriate page. Of course it doesn't really look nice if you have to go to /pages/about, a more intuitive way would be just /about, so I was trying to figure out how to accomplish that. 

I tried getting the pages from the database and creating the routes dynamically, but that wasn't working because the route still needs to pass a parameter to the controller. I could have gotten the URL from within the controller and used that, but I found an easier and cleaner way.

At the very end of the web.php routes file I added:

Route::get('/{slug}', 'PagesController@show');

When Laravel has a route it goes through the file and tries to find a match. When it finds one it stops and executes it. So by having this route at the end of the file it will only match routes that haven't already been matched. So for any route that isn't already defined it will called PagesController@show and pass it $slug, which is the exact same thing that the old route did:

Route::get('/pages/{slug}', 'PagesController@show');

Except this route gives me a nice, clean URI instead of a clunky, ugly one.

Labels: coding , laravel

No comments

Structured Data and Google Rich Cards

Feb. 21, 2017, 8:59 a.m.

It's now been a couple of weeks since I started adding Structured Data to this site and Google has started to index some of the structured data. However, Google Search Console still shows no Rich Cards. I am not sure why, it may be that Googlebot hasn't yet gotten around to that. Or it could be that Google only creates Rich Cards for certain types of Structured Data. The documentation I found was mostly from when Google started to introduce the Rich Cards and it said they would only be generated for specific types of content - Recipes, Movies, Reviews, News Articles and a few other types. I do not know if Google has started to implement Rich Cards for other types of structured data or not. I don't particularly care about having Rich Cards displayed, I mostly just wanted to figure out how to use them. 

So, while I still have no Rich Cards having the Structured Data can't hurt, and Google has started to index that and it shows up in the Structured Data report. The AMP pages have also started to be indexed, although I am seeing some inconsistencies in the Search Console Reports. It could just be that Googlebot needs more time to index things. Who knows?

Labels: coding , seo

No comments

AMP Authentication

Feb. 14, 2017, 2:34 p.m.

I finally got the AMP forms working as expected. It was a bit tricky to figure out so I will outline the issues I encountered and how I solved them. The situation I was working with was making a comments form for the AMP version of my blog pages.

The first issue I had to deal with was that a user can't leave a comment unless they are logged in. In the rest of the app I use the session to determine if the user is logged in, but AMP has it's own protocol for doing that, which involves making AJAX requests to a page which returns a JSON response to determine if the user is logged in. In this case, in the controller I simply do an Auth::check() and return a JSON response depending on the results of the check(). 

The issues arose from the fact that AMP requires specific response headers, which took me a while to figure out how to set properly. I wasn't able to find much documentation on the values of these headers, but I was able to figure out the proper values.

The headers required were:

  • Access-Control-Allow-Credentials: true
  • Access-Control-Expose-Headers: AMP-Access-Control-Allow-Source-Origin
  • Access-Control-Allow-Origin
  • AMP-Access-Control-Allow-Source-Origin

The latter two headers need to have specific values, and although they ended up being the same in most cases, I set them to the separate values to make sure errors won't occur.

The value for Access-Control-Allow-Origin needs to be the "origin" header made in the request, which I get with:

$request->header('origin')

The value for the AMP-Access-Control-Allow-Source-Origin needs reflect the value passed in the URL to the request, which is a parameter named:  __amp_source_origin.

The authorization page can return a variety of values to indicate whether the user has a subscription, if they can view a specific number of free articles, and what they have access to. But in my case all I need to know is whether they are logged in or not, so I just return the JSON data:

{loggedIn: true}

To enable content being displayed differently based on authorization you need to include the following scripts:

<script async custom-element="amp-access" src="https://cdn.ampproject.org/v0/amp-access-0.1.js"></script>
<script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>

You also need to include the following in a script to tell the scripts what to do and where to get the info from:
{
    "authorization": "[Auth URI]",
    "noPingback": "true",
    "login": {
        "sign-in": "[Login URI]",
        "sign-out": "[Logout URI]"
    },
    "authorizationFallbackResponse": {
        "error": true,
        "loggedIn": false
    }
}

Where [Auth URI] is the URI detailed above which returns whether the user is logged in or not; [login URI] is the URI to allow the user to login; and [logout URI] is the URI to allow the user to logout. All URIs must either be HTTPS or // or AMP will complain about them and won't function properly.

Then the following code is included in the template:

<span amp-access="NOT loggedIn" role="button" tabindex="0" amp-access-hide>
    <button on="tap:amp-access.login-sign-in" class="btn btn-xs btn-primary comment-button">Login</button>
    Please login to comment<br><BR>
</span>
<span amp-access="loggedIn">
    @include('amp._commentForm')
</span>

The amp-access attribute in the span tells the page NOT to display the section if the user is loggedIn - presumably you could vary this to reference other data returned by the auth page. The on attribute of the button tells the page to reference the login:sign-in attribute of the amp-access script when it is tapped, so it will launch the [login URI] when the button is clicked. And finally the amp-access="loggedIn" attribute says that if the user IS logged in the commentForm will be included.

For me the most complicated part was figuring out the response headers required and their values, once I got that figured out the rest worked pretty easily. The next step was getting the actual form to submit and update the page properly. I'll write about that in the next post.

Labels: coding , laravel , amp

No comments

Archives