Active Record

March 20, 2017, 7:27 a.m.

When a friend first told me about Doctrine many, many years ago I thought it sounded like a terrible idea. I have also had a love affair with SQL and the thought of having to use objects and functions to interact with the database instead of writing queries and getting the data you needed was overly complicated and didn't add anything. When I first started using frameworks to code I felt the same way about them - it seemed to add unneeded layers of complexity to have to incorporate code written by someone else to do something I could very easily do on my own.

It wasn't until I started using Laravel that I came to appreciate frameworks - I could write a simple CRUD that would have taken me days writing from scratch in a couple of hours thanks to the functionality built into the framework. And the ORM made things a lot easier as far as database interaction, but only within certain parameters. I realized this when I tried to normalize a table in my database by separating out a field into another related table. It became very complicated and convoluted to do simple things like sort the query by a value in the related table. I ended up denormalizing the database and getting rid of the extra table to keep the code clean. 

At the time I assumed that when you set up a relationship between models when you queried one Laravel would join the other tables in to get all the needed data in one query. It wasn't until I installed debugbar that I realized that laravel would do n+1 queries to get n rows of data with one relationship. This issue is avoidable using Laravel's "eager loading" - which will get the same data in 2 queries. However to get data from multiple tables in one query - using a JOIN - and specifying WHEREs and ORDER BYs on the joined tables, the Query Builder syntax gets quite ugly. 

In my opinion this is largely because Eloquent is an implementation of the Active Record pattern, which represents one row of one table in the database as an object as far as the code is concerned. In Eloquent, if you query multiple rows you are returned a Collection of these model objects. While Active Record is great for dealing with simple databases where one row contains usable data, if you are dealing with a highly relational database where you need data from multiple tables it doesn't hold up so well.

When I started out programming we were using MS SQL Server and the programmers were not allowed to write any queries - we were to use stored procedures written by the DBA. At the time I didn't understand the reason for this, but now I realize that it allows the database structure to be separate from the code - so that database changes won't result in needing to rewrite large sections of code. This, in my opinion, is the main advantage of using an ORM. So what do you do when you need to write actual SQL queries for your code to work properly and efficiently?

One option I investigated was adding a Repository layer to the code. With the repository pattern the models handle the reading from and writing to the database, but the repository interacts with the code. For my needs, the repository would basically act the part of the SPs - the queries would be written in them and the code would call the methods in the Repository to get the data they needed. Basically it just put all of the queries in one place so that if the database was ever changed the queries that needed to be rewritten would all be in one place. I tried implementing this, and it worked, but it added another level of abstraction and complexity. And the way I implemented it was basically no different from writing the queries directly into the models, which works just as well, but for some reason it bothers me to have complex and bloated models.

I am still working through this issue and do not have a solution yet. Dealing with this has made me remember the many issues I had with ORMs and frameworks in general back when I first started using them. Using the tools of the ORM the issues can be addressed - but not in a simple, clean and elegant manner. And in my opinion, the main problem is the Active Record pattern itself - it is great if you need to work with a single row in a single table, but if you need to span multiple tables to get your data, it doesn't hold up so well.

Labels: coding , laravel

No comments

AMP Form

March 5, 2017, 8:07 a.m.

Once I had the authorization working and could display the comment form properly the next step was to get the form submitting properly. First of all, to use forms in AMP you need to include the following script:

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

AMP forms work mostly the same way as normal HTML5 forms, but there are a few differences. With AMP you have to either use GET forms or you can use POST forms but you need to use action-xhr instead of action. For POST forms you also need to include a target of either _blank or _top. Using action-xhr means that the page will basically post an AJAX request which expects a JSON response instead of reloading the whole page. If you want to reload the whole page you should use method="get". 

The response from the request needs to include the same headers as the authorization request, which are detailed in this post. The response doesn't need to contain any specific data, you can put whatever you want in there. You can use the response data to update the page after a successful post, but I have not done that yet.

In this case, the controller function action for my new comment just adds the comment and then returns the id and body of the new comment in JSON along with the necessary headers. I would like to display the new comment on the page, but the syntax to display the JSON data is {{ var }} which is the same syntax blade uses. I know there is a workaround for this, but I haven't looked for it yet.

The two big issues I had with this form were how to update the page after a successful post. I wanted to do two things: hide the form to leave a comment and display a success message. There are AMP components to accomplish both of these tasks.

To hide the form after a successful post you can add the following CSS:

form.amp-form-submit-success > input {
  display: none
}

And set the form's class to "hide-inputs." When the success message comes back the form's class is updated to included amp-form-submit-success which will cascade down to hide any child inputs. I had my inputs in a panel inside the form and the inputs were not hidden because the input was not a direct child of the form. This was fixed by rearranging the elements so that the form was inside the divs so that the inputs were children of the form. Before I arrived at this solution I first tried to hide the entire form, which worked great, but also hid the success message. Since my form field was a textarea I had to add another item to the CSS for textareas, otherwise identical to that above.

The next step was displaying a success message, which I did by following the instructions here. The block of sample code from this page is:

 

  <form class="hide-inputs" method="post" action-xhr="https://ampbyexample.com/components/amp-form/submit-form-input-text-xhr" target="_top">
    <input type="text" class="data-input" name="name" placeholder="Name..." required>
    <input type="submit" value="Subscribe" class="button button-primary">
    <div submit-success>
    <template type="amp-mustache">
    Success! Thanks {{name}} for trying the
    <code>amp-form</code> demo! Try to insert the word "error" as a name input in the form to see how
    <code>amp-form</code> handles errors.
    </template>
    </div>
    <div submit-error>
    <template type="amp-mustache">
    Error! Thanks {{name}} for trying the
    <code>amp-form</code> demo with an error response.
    </template>
    </div>
    </form>
The div submit-success is hidden until the form submission comes back with a success message at which point it is display, and likewise with the submit-error block. To use the amp-mustache templates you need to include the following in a script tag:

script custom-template="amp-mustache" src="https://cdn.ampproject.org/v0/amp-mustache-0.1.js"

In the submit-success section the {{ name }} will substitute in the "name" element from the JSON data returned from the post. In my case I have left this out for now and just display a success message and hide the form. 

To see this in action you can look at the AMP version of this blog here. When you submit a comment the form disappears and is replaced by a success message. Ideally the new comment would show up, but I'll get to that at some point in the future.

Update - the blade syntax to display the "{{ }}" for Javascript is "@{{ whatever }}". So I updated my code to actually display the comment after it is posted.

Labels: coding , laravel , amp

25 comments

Laravel Packages

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

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 comment

Laravel Blog Package

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

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

Archives