Matthew Daly's Blog

I'm a web developer in Norfolk. This is my blog...

19th August 2017 3:40 pm

Run Your Tests Locally With Sismo

Continuous integration is a veritable boon when working on any large software project. However, the popularity of distributed version control systems like Git over the older, more centralised ones like Subversion means that when you commit your changes, they don’t necessarily get pushed up to a remote repository immediately. While this is a good thing because it means you can commit at any stage without worrying about pushing up changes that break everyone else’s build, it has the downside that the tests aren’t automatically run on every commit, just every push, so if you get sloppy about running your tests before every commit you can more easily get caught out. In addition, a full CI server like Jenkins is a rather large piece of software that you don’t really want to run locally if you can help it, and has a lot of functionality you don’t need.

Sismo is a small, simple continuous integration server, implemented in PHP, that’s ideal for running locally. You can set it up to run your tests on every commit, and it has an easy-to-use web interface. Although it’s a PHP application, there’s no reason why you couldn’t use it to run tests for projects in other languages, and because it’s focused solely on running your test suite without many of the other features of more advanced CI solutions, it’s a good fit for local use. Here I’ll show you how I use it.

Setting up Sismo

Nowadays I don’t generally install a web server on a computer directly, preferring to use Vagrant or the dev server as appropriate, so Sismo generally doesn’t have to coexist with anything else. I normally install PHP7’s FastCGI implementation and Nginx, along with the SQLite bindings (which Sismo needs):

$ sudo apt-get install nginx php7.0-fpm php7.0-sqlite3

Then we can set up our Nginx config at /etc/nginx/sites-available/default:

server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
fastcgi_param HTTP_PROXY "";
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
root /var/www/html;
index sismo.php index.html index.htm;
server_name server_domain_or_IP;
location / {
try_files $uri $uri/ /sismo.php?$query_string;
}
location ~ \.php$ {
try_files $uri /sismo.php =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
fastcgi_index sismo.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SISMO_DATA_PATH "/home/matthew/.sismo/data";
fastcgi_param SISMO_CONFIG_PATH "/home/matthew/.sismo/config.php";
include fastcgi_params;
}
}

You’ll probably want to adjust the paths as appropriate. Then set up the required folders:

$ mkdir ~/.sismo
$ mkdir ~/.sismo/data
$ touch ~/.sismo/config.php
$ chmod -R a+w ~/.sismo/

Then, download Sismo and put it in your web root (here it’s at /var/www/html/sismo.php).

Now, say you have a project you want to test (I’m using my Laravel ETag middleware for this example). We need to specify the projects we want to test in ~/.sismo/config.php:

<?php
$projects = array();
$notifier = new Sismo\Notifier\DBusNotifier();
Sismo\Project::setDefaultCommand('if [ -f composer.json ]; then composer install; fi && vendor/bin/phpunit');
$projects[] = new Sismo\GithubProject('Laravel ETag Middleware', '/home/matthew/Projects/laravel-etag-middleware', $notifier);
return $projects;

Hopefully this shouldn’t be too difficult to understand. We create an array of projects, then specify a notifier (this is Linux-specific - refer to the documentation for using Growl on Mac OS). Next, we specify that by default the tests should run composer install followed by vendor/bin/phpunit. We then specify this project is a Github project - it also supports Bitbucket, or plain SSH, or the default Project, but in general it shouldn’t be a problem to use it with any repository as you can just run it against the local copy. Finally we return the list of projects.

Now, we should be able to run our tests as follows:

$ php /var/www/html/sismo.php build
Building Project "Laravel ETag Middleware" (into "68a087")

That should be working, but it doesn’t get us anything we don’t get by running the tests ourselves. To trigger the build, we need to set up a post-commit hook for our project in .git/hooks/post-commit:

#!/bin/sh
php /var/www/html/sismo.php --quiet --force build laravel-etag-middleware `git log -1 HEAD --pretty="%H"` &>/dev/null &

You should now be able to view your project in the Sismo web interface at http://localhost:

Sismo

Clicking on the project should take you through to its build history:

Sismo project page

From here on, it should be straightforward to add new projects as and when necessary. Because you can change the command on a per-project basis, you can quite happily use it to run tests for Python or Node.js projects as well as PHP ones, and it’s not hard to configure it.

I personally find it very useful to have something in place to run my tests on every commit like this, and while you could just use a post-commit hook for that, this approach is less obtrusive because it doesn’t force you to wait around for your test suite to finish.

14th August 2017 12:40 pm

Profiling Your Laravel Application With Clockwork

If you’re building any non-trivial application, it’s always a good idea to profile it to find performance problems. Laravel Debugbar is the usual solution for profiling Laravel web applications, but it isn’t really much use for REST API’s or single-page web apps that consume them.

Recently I was introduced to Clockwork, which is a server-side extension for profiling PHP applications. It’s made it a whole lot easier to track down issues like excessive numbers of queries when building an API, and as a result I’ve been able to dramatically improve the performance of an API I’ve been working on. Here I’ll show you how you can use it on a project.

Installing Clockwork

Clockwork is available via Composer:

$ composer require itsgoingd/clockwork

You also need to register the service provider in config/app.php:

   Clockwork\Support\Laravel\ClockworkServiceProvider::class,

And register the middleware globally in app/Http/Kernel.php:

protected $middleware = [
\Clockwork\Support\Laravel\ClockworkMiddleware::class,
]

Note that it only works when APP_DEBUG is set to true in your .env file. This means that you can keep it in your application without worrying about exposing too much data in production, as long as debug mode is not active on your production server (which it shouldn’t be anyway).

You will also need to install the Chrome extension in order to actually work with the returned data. Clockwork works by adding its own route to your Laravel application, and this extension makes sure that it makes the appropriate request on loading a page, and then displays the data in the dev tools.

Once it’s all installed and your application is running, open the dev tools and you should see the new Clockwork tab in there. On the left of this tab is a list of requests - if you make a request, you’ll see it added to the list. When you click on each request, you’ll see the following tabs, where applicable:

Request

Request tab

This is similar to Chrome’s network tab in that it shows all of the headers for a given request. It’s not anything you can’t get using Chrome’s existing dev tools, but because it doesn’t show any static content it’s arguably a bit easier to navigate.

Timeline

Timeline tab

This shows how long the response takes to respond, which can be helpful in identifying slower requests.

In addition, you can create your own events using the clock() helper, which will appear in the timeline, as in this example:

clock()->startEvent('email_sent', 'Email sent.');
clock()->endEvent('email_sent');

Log

Log tab

The log tab is only displayed if you use the clock() helper to log data. You can log text or JSON objects as appropriate:

clock('Message text.'); // 'Message text.' appears in Clockwork log tab
clock(['hello' => 'world']); // logs json representation of the array

This is arguably more convenient than using the Log facade to write to the application log, since it’s kept in the browser and you can easily see what request caused what message to be logged.

Database

Database tab

The database tab displays details of the queries made by a request. This is useful for identifying things such as:

  • Repeated queries that should be cached
  • The n+1 problem (which can be resolved by use of eager loading)
  • Slow queries that need to be optimised

Note that if a particular endpoint does not trigger a query, this tab will not be visible.

Cookies

Cookies tab

For a REST API, you shouldn’t really have much use for cookies, but if you do, this tab lets you view the cookies set on the request.

Session

Session tab

As with cookies, the session isn’t normally something you’d use for an API, but this tab lets you view it.

Views

Views tab

This tab shows the views used on the page, and all of the data passed to them.

Routes

Routes tab

This tab shows all of the routes defined within your application.

Clockwork isn’t limited to Laravel - you can also use it with Lumen, Slim 2, and CodeIgniter 2.1, and it’s possible to write your own integration for other frameworks. It’s still fundamentally browser-based, so it’s difficult to use it if your API doesn’t have at least some kind of web front end (whether that’s a single page web app or Phonegap app that consumes the API, or that the API is itself browseable and returns HTML in a web browser), but I’ve found it to be superior to Laravel Debugbar for most of what I do.

17th June 2017 2:12 pm

Snapshot Test Your Vue Components With Jest

At work I’ve recently started using Vue as my main front-end framework instead of Angular 1. It has a relatively shallow learning curve and has enough similarities with both React and Angular 1 that if you’re familiar with one or both of them it feels quite familiar. We’re a Laravel shop and Laravel comes out of the box with a basic scaffolding for using Vue, so not only is it the path of least resistance, but many of my colleagues knew it already and it’s used on some existing projects (one of which I’ve been helping out on this week), so it made sense to learn it. Add to that the fact that the main alternative is Angular 2, which I vehemently dislike, and learning Vue was a no-brainer.

Snapshot tests are a really useful way of making sure your user interface doesn’t change unexpectedly. Facebook introduced them to their Jest testing framework last year, and they’ve started to appear in other testing frameworks too. In their words…

A typical snapshot test case for a mobile app renders a UI component, takes a screenshot, then compares it to a reference image stored alongside the test. The test will fail if the two images do not match: either the change is unexpected, or the screenshot needs to be updated to the new version of the UI component.

This makes it easy to make sure than a UI component, such as a React or Vue component, does not unexpectedly change how it is rendered. In the event that it does change, it will fail the test, and it’s up to the developer to confirm whether or not that’s expected - if so they can generate a new version of the snapshot and be on their way. Without it, you’re stuck manually testing that the right HTML tags get generated, which is a chore.

Jest’s documentation is aimed pretty squarely at React, but it’s not hard to adapt it to work with Vue components. Here I’ll show you how I got it working with Vue.

Setting up a new project

I used the Vue CLI boilerplate generator to set up my initial dependencies for this project. I then had to install some further packages:

$ npm install --save-dev jest babel-jest jest-vue-preprocessor

After that, I had to configure Jest to work with Vue. The finished package.json looked like this:

{
"name": "myproject",
"version": "1.0.0",
"description": "A project",
"author": "Matthew Daly <matthew@matthewdaly.co.uk>",
"private": true,
"scripts": {
"dev": "node build/dev-server.js",
"start": "node build/dev-server.js",
"build": "node build/build.js",
"lint": "eslint --ext .js,.vue src",
"test": "jest __test__/ --coverage"
},
"dependencies": {
"vue": "^2.3.3",
"vue-router": "^2.3.1"
},
"devDependencies": {
"autoprefixer": "^6.7.2",
"babel-core": "^6.22.1",
"babel-eslint": "^7.1.1",
"babel-jest": "^20.0.3",
"babel-loader": "^6.2.10",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"babel-register": "^6.22.0",
"chalk": "^1.1.3",
"connect-history-api-fallback": "^1.3.0",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.0",
"eslint": "^3.19.0",
"eslint-config-standard": "^6.2.1",
"eslint-friendly-formatter": "^2.0.7",
"eslint-loader": "^1.7.1",
"eslint-plugin-html": "^2.0.0",
"eslint-plugin-promise": "^3.4.0",
"eslint-plugin-standard": "^2.0.1",
"eventsource-polyfill": "^0.9.6",
"express": "^4.14.1",
"extract-text-webpack-plugin": "^2.0.0",
"file-loader": "^0.11.1",
"friendly-errors-webpack-plugin": "^1.1.3",
"html-webpack-plugin": "^2.28.0",
"http-proxy-middleware": "^0.17.3",
"jest": "^20.0.4",
"jest-vue-preprocessor": "^1.0.1",
"opn": "^4.0.2",
"optimize-css-assets-webpack-plugin": "^1.3.0",
"ora": "^1.2.0",
"rimraf": "^2.6.0",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"url-loader": "^0.5.8",
"vue-loader": "^12.1.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.3.3",
"webpack": "^2.6.1",
"webpack-bundle-analyzer": "^2.2.1",
"webpack-dev-middleware": "^1.10.0",
"webpack-hot-middleware": "^2.18.0",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 4.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
],
"jest": {
"testRegex": "spec.js$",
"moduleFileExtensions": [
"js",
"vue"
],
"transform": {
"^.+\\.js$": "<rootDir>/node_modules/babel-jest",
".*\\.(vue)$": "<rootDir>/node_modules/jest-vue-preprocessor"
}
}
}

I won’t include things like the Webpack config, because that’s all generated by Vue CLI. Note that we need to tell Jest what file extensions it should work with, including .vue, and we need to specify the appropriate transforms for different types of files. We use jest-vue-preprocessor for .vue files and babel-jest for .js files.

With that done, we can create a basic component. We’ll assume we’re writing a simple issue tracker here, and our first component will be at src/components/Issue.vue:

<template>
<div>
<h1>An Issue</h1>
</div>
</template>
<script>
export default {
data () {
return {}
}
}
</script>
<style scoped>
</style>

Next, we create a simple test for this component. Save this as __test__/components/issue.spec.js:

import Issue from '../../src/components/Issue.vue'
import Vue from 'vue'
const Constructor = Vue.extend(Issue)
const vm = new Constructor().$mount()
describe('Issue', () => {
it('should render', () => {
expect(vm.$el.querySelector('h1').textContent).toEqual('An Issue')
});
it('should match the snapshot', () => {
expect(vm.$el).toMatchSnapshot()
});
});

Constructor is what creates our Vue component, while vm is our actual newly-mounted Vue component. We can refer to the HTML inside the component through vm.$el, so we can then work with the virtual DOM easily.

In the first test we use the more traditional method of verifying our UI component has worked as expected - we fetch an HTML tag inside it and verify that the content inside is what we expect. This is fine for a small component, but as the components get larger we’ll find it more of a chore.

The second test is much simpler and more concise. We simply assert that it matches the snapshot. Not only is that easier, but it can scale to components of any size because we don’t have to check every little element.

Let’s run our tests:

$ npm test
> myproject@1.0.0 test /home/matthew/Projects/myproject
> jest __test__/ --coverage
PASS __test__/components/issue.spec.js
Issue
✓ should render (46ms)
✓ should match the snapshot (14ms)
Snapshot Summary
› 1 snapshot written in 1 test suite.
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 1 added, 1 total
Time: 8.264s
Ran all test suites matching "__test__/".
-----------------------------------------------------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
-----------------------------------------------------------|----------|----------|----------|----------|----------------|
All files | 96.15 | 50 | 100 | 96 | |
root | 100 | 100 | 100 | 100 | |
unknown | 100 | 100 | 100 | 100 | |
root/home/matthew/Projects/myproject/__test__/components | 100 | 100 | 100 | 100 | |
issue.spec.js | 100 | 100 | 100 | 100 | |
root/home/matthew/Projects/myproject/src/components | 94.44 | 50 | 100 | 94.12 | |
Issue.vue | 94.44 | 50 | 100 | 94.12 | 39 |
-----------------------------------------------------------|----------|----------|----------|----------|----------------|

Note this section:

Snapshot Summary
› 1 snapshot written in 1 test suite.

This tells us that the snapshot has been successfully written. If we run the tests again we should see that it checks against the existing snapshot:

$ npm test
> myproject@1.0.0 test /home/matthew/Projects/myproject
> jest __test__/ --coverage
PASS __test__/components/issue.spec.js
Issue
✓ should render (40ms)
✓ should match the snapshot (12ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 1 passed, 1 total
Time: 3.554s
Ran all test suites matching "__test__/".
-----------------------------------------------------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
-----------------------------------------------------------|----------|----------|----------|----------|----------------|
All files | 96.15 | 50 | 100 | 96 | |
root | 100 | 100 | 100 | 100 | |
unknown | 100 | 100 | 100 | 100 | |
root/home/matthew/Projects/myproject/__test__/components | 100 | 100 | 100 | 100 | |
issue.spec.js | 100 | 100 | 100 | 100 | |
root/home/matthew/Projects/myproject/src/components | 94.44 | 50 | 100 | 94.12 | |
Issue.vue | 94.44 | 50 | 100 | 94.12 | 39 |
-----------------------------------------------------------|----------|----------|----------|----------|----------------|

Great stuff. Now, if we make a minor change to our component, such as changing the text from An Issue to My Issue, does it pick that up?

$ npm test
> myproject@1.0.0 test /home/matthew/Projects/myproject
> jest __test__/ --coverage
FAIL __test__/components/issue.spec.js (5.252s)
● Issue › should render
expect(received).toEqual(expected)
Expected value to equal:
"An Issue"
Received:
"My Issue"
at Object.<anonymous> (__test__/components/issue.spec.js:9:52)
at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)
● Issue › should match the snapshot
expect(value).toMatchSnapshot()
Received value does not match stored snapshot 1.
- Snapshot
+ Received
<div>
<h1>
- An Issue
+ My Issue
</h1>
</div>
at Object.<anonymous> (__test__/components/issue.spec.js:13:20)
at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)
Issue
✕ should render (48ms)
✕ should match the snapshot (25ms)
Snapshot Summary
› 1 snapshot test failed in 1 test suite. Inspect your code changes or run with `npm test -- -u` to update them.
Test Suites: 1 failed, 1 total
Tests: 2 failed, 2 total
Snapshots: 1 failed, 1 total
Time: 7.082s
Ran all test suites matching "__test__/".
-----------------------------------------------------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
-----------------------------------------------------------|----------|----------|----------|----------|----------------|
All files | 96.15 | 50 | 100 | 96 | |
root | 100 | 100 | 100 | 100 | |
unknown | 100 | 100 | 100 | 100 | |
root/home/matthew/Projects/myproject/__test__/components | 100 | 100 | 100 | 100 | |
issue.spec.js | 100 | 100 | 100 | 100 | |
root/home/matthew/Projects/myproject/src/components | 94.44 | 50 | 100 | 94.12 | |
Issue.vue | 94.44 | 50 | 100 | 94.12 | 39 |
-----------------------------------------------------------|----------|----------|----------|----------|----------------|

Yes, we can see that it’s picked up on the change and thrown an error. Note this line:

 › 1 snapshot test failed in 1 test suite. Inspect your code changes or run with `npm test -- -u` to update them.

Jest is telling us that our snapshot has changed, but if we expect that, we can just run npm test -- -u to replace the existing one with our new one. Then, our tests will pass again.

Now, this component is pretty useless. It doesn’t accept any external input whatsoever, so the response is always going to be the same. How do we test a more dynamic component? Amend the component to look like this:

<template>
<div>
<h1>{{ issue.name }}</h1>
</div>
</template>
<script>
export default {
props: {
issue: Object
},
data () {
return {}
}
}
</script>
<style scoped>
</style>

We’re now passing the issue object into our component as a prop, and getting the name from that. That will break our test, so we need to amend it to pass through the props:

import Issue from '../../src/components/Issue.vue'
import Vue from 'vue'
const Constructor = Vue.extend(Issue)
const issue = {
name: 'My Issue'
}
const vm = new Constructor({
propsData: { issue: issue }
}).$mount()
describe('Issue', () => {
it('should render', () => {
expect(vm.$el.querySelector('h1').textContent).toEqual('My Issue')
});
it('should match the snapshot', () => {
expect(vm.$el).toMatchSnapshot()
});
});

Here we pass our prop into the constructor for the component. Now, let’s run the tests again:

$ npm test
> myproject@1.0.0 test /home/matthew/Projects/myproject
> jest __test__/ --coverage
FAIL __test__/components/issue.spec.js
● Issue › should match the snapshot
expect(value).toMatchSnapshot()
Received value does not match stored snapshot 1.
- Snapshot
+ Received
<div>
<h1>
- An Issue
+ My Issue
</h1>
</div>
at Object.<anonymous> (__test__/components/issue.spec.js:18:20)
at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)
Issue
✓ should render (39ms)
✕ should match the snapshot (25ms)
Snapshot Summary
› 1 snapshot test failed in 1 test suite. Inspect your code changes or run with `npm test -- -u` to update them.
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 1 failed, 1 total
Time: 3.717s
Ran all test suites matching "__test__/".
-----------------------------------------------------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
-----------------------------------------------------------|----------|----------|----------|----------|----------------|
All files | 96.3 | 50 | 100 | 96.15 | |
root | 100 | 100 | 100 | 100 | |
unknown | 100 | 100 | 100 | 100 | |
root/home/matthew/Projects/myproject/__test__/components | 100 | 100 | 100 | 100 | |
issue.spec.js | 100 | 100 | 100 | 100 | |
root/home/matthew/Projects/myproject/src/components | 94.44 | 50 | 100 | 94.12 | |
Issue.vue | 94.44 | 50 | 100 | 94.12 | 39 |
-----------------------------------------------------------|----------|----------|----------|----------|----------------|

Jest has picked up on our changes and thrown an error. However, because we know the UI has changed, we’re happy with this situation, so we can tell Jest to replace the prior snapshot with npm test -- -u as mentioned earlier:

$ npm test -- -u
> myproject@1.0.0 test /home/matthew/Projects/myproject
> jest __test__/ --coverage "-u"
PASS __test__/components/issue.spec.js
Issue
✓ should render (39ms)
✓ should match the snapshot (14ms)
Snapshot Summary
› 1 snapshot updated in 1 test suite.
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 1 updated, 1 total
Time: 3.668s
Ran all test suites matching "__test__/".
-----------------------------------------------------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
-----------------------------------------------------------|----------|----------|----------|----------|----------------|
All files | 96.3 | 50 | 100 | 96.15 | |
root | 100 | 100 | 100 | 100 | |
unknown | 100 | 100 | 100 | 100 | |
root/home/matthew/Projects/myproject/__test__/components | 100 | 100 | 100 | 100 | |
issue.spec.js | 100 | 100 | 100 | 100 | |
root/home/matthew/Projects/myproject/src/components | 94.44 | 50 | 100 | 94.12 | |
Issue.vue | 94.44 | 50 | 100 | 94.12 | 39 |
-----------------------------------------------------------|----------|----------|----------|----------|----------------|

Great, we now have a passing test suite again! That’s all we need to make sure that any regressions in the generated HTML of a component get caught.

Of course, this won’t help with the actual functionality of the component. However, Jest is pretty easy to use to write tests for the actual functionality of the application. If you prefer another testing framework, it’s possible to do the same with them, although I will leave setting them up as an exercise for the reader.

15th March 2017 9:37 pm

Enforcing a Coding Standard With PHP Codesniffer

We all start new projects with the best of intentions - it’ll be clean, fully tested and work perfectly. Sadly as deadlines loom, it’s all too easy to find yourself neglecting your code quality, and once it starts to degrade, getting it back becomes much harder. Many development teams try to adhere to a coding standard, but it can be hard to enforce on other people - it puts you in the uncomfortable position of nagging others all the time.

Fortunately, there’s an easy solution that doesn’t force everyone to use the same IDE. PHP CodeSniffer is a useful package that lets you specify a coding standard and then validate your code against it. That way, you can set up continuous integration and use that to remind people of errors. Better still, it also allows many errors to be fixed automatically.

To use it on your PHP project, run the following command:

$ composer require --dev squizlabs/php_codesniffer

As this will only ever be used in development, you should use the --dev flag. We also need to specify the settings for our project. This example is for a module to be used with a Laravel application and should be saved as phpcs.xml:

<?xml version="1.0"?>
<ruleset name="PHP_CodeSniffer">
<description>The coding standard for our project.</description>
<file>app</file>
<file>tests</file>
<exclude-pattern>*/migrations/*</exclude-pattern>
<arg value="np"/>
<rule ref="PSR2"/>
</ruleset>

Note the <rule /> tag - this specifies that this project should be validated as PSR2. Also, note the <file /> and <exclude-pattern /> tags - these specify what files should and should not be checked.

With this in place, we’re ready to run PHP CodeSniffer:

$ vendor/bin/phpcs
......................
Time: 45ms; Memory: 6Mb

In this case, our code validated successfully. However, if it doesn’t, there’s an easy way to tidy it up. Just run this command:

$ vendor/bin/phpcbf

That will fix many of the most common problems, and any others should be straightforward to fix.

PHP CodeSniffer makes it extremely straightforward to enforce a coding style. You can write custom rulesets or just use an existing one as you prefer, and it’s easy to fix many common problems automatically. In fact, it makes it so easy that there’s very little excuse not to meet the coding standard.

1st March 2017 11:16 pm

Decorating Laravel Repositories

As mentioned previously, when building any nontrivial Laravel application, it’s prudent to decouple our controllers from the Eloquent ORM (or any other ORM or data source we may be using) by creating an interface, and then writing a repository that implements that interface. We can then resolve the interface to our repository, and use the repository to interact with our data source. Should we need to switch to a different implementation, we then need only create the new repository and amend how Laravel resolves that interface.

The same principle applies when it comes to caching. Database queries are typically a major bottleneck in a web application, and so it’s prudent to implement some form of caching for your queries. However, it’s a bad idea to do so in your controllers, because just as with Eloquent models, you’re tying yourself to one particular implementation and won’t be able to switch without rewriting a good chunk of your controllers, as well as possibly having to maintain large amounts of duplicate code for when a query is made in several places.

Alternatively, you could implement caching within the methods of your repository, which might make sense for smaller projects. However, it means that your repository is now dependent on both the ORM and cache you chose. If you decide you want to change your ORM but retain the same caching system, or vice versa, you’re stuck with writing a new repository to handle both, duplicating work you’ve already done.

Fortunately, there’s a more elegant solution. Using the decorator pattern, we can create a second repository that implements the same interface and “wraps” the original repository. Each of its methods will call its counterpart in the original, and if appropriate cache the response. That way, our caching is implemented separately from our database interactions, and we can easily create a repository for a new data source without affecting the caching in the slightest.

Say we have the following interface for our User model:

<?php
namespace App\Repositories\Interfaces;
interface UserRepositoryInterface {
public function all();
public function findOrFail($id);
public function create($input);
}

And the following repository implements that interface:

<?php
namespace App\Repositories;
use App\User;
use App\Repositories\Interfaces\UserRepositoryInterface;
use Hash;
class EloquentUserRepository implements UserRepositoryInterface {
private $model;
public function __construct(User $model)
{
$this->model = $model;
}
public function all()
{
return $this->model->all();
}
public function findOrFail($id)
{
return $this->model->findOrFail($id);
}
public function create($input)
{
$user = new $this->model;
$user->email = $input['email'];
$user->name = $input['name'];
$user->password = Hash::make($input['password']);
$user->save();
return $user;
}
}

We might implement the following repository class to handle caching:

<?php
namespace App\Repositories\Decorators;
use App\Repositories\Interfaces\UserRepositoryInterface;
use Illuminate\Contracts\Cache\Repository as Cache;
class CachingUserRepository implements UserRepositoryInterface {
protected $repository;
protected $cache;
public function __construct(UserRepositoryInterface $repository, Cache $cache)
{
$this->repository = $repository;
$this->cache = $cache;
}
public function all()
{
return $this->cache->tags('users')->remember('all', 60, function () {
return $this->repository->all();
});
}
public function findOrFail($id)
{
return $this->cache->tags('users')->remember($id, 60, function () use ($id) {
return $this->repository->findOrFail($id);
});
}
public function create($input)
{
$this->cache->tags('users')->flush();
return $this->repository->create($input);
}
}

Note how each method doesn’t actually do any querying. Instead, the constructor accepts an implementation of the same interface and the cache, and we defer all interactions with the database to that implementation. Each call that queries the database is wrapped in a callback so that it’s stored in Laravel’s cache when it’s returned, without touching the original implementation. When a user is created, the users tag is flushed from the cache so that stale results don’t get served.

To actually use this implementation, we need to update our service provider so that it resolves the interface to an implementation of our decorator:

<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->singleton('App\Repositories\Interfaces\UserRepositoryInterface', function () {
$baseRepo = new \App\Repositories\EloquentUserRepository(new \App\User);
$cachingRepo = new \App\Repositories\Decorators\CachingUserRepository($baseRepo, $this->app['cache.store']);
return $cachingRepo;
});
}
}

We instantiate the base repository, passing it the appropriate model. Then we instantiate the decorator, passing it the base repository and the cache, and return it. Now our controllers will start using the new decorator.

Testing the decorator

Now that we have a working decorator, how do we test it? Just as with the decorator itself, we want our tests to be completely decoupled from any particular implementation of the dependencies. If in future we’re asked to migrate the database to MongoDB, say, we’ll have plenty of work writing our new database repositories, so we don’t want to have to rewrite the tests for our decorator as well. Fortunately, using Mockery we can just mock the interface for the repository, and pass that mock into the constructor of the decorator in our test. That way we can have the mock return a known response and not involve either the database repository or the underlying models in any way.

We will also want to mock the cache itself, as this is a unit test and so as far as possible it should not be testing anything outside of the repository class. Here’s an example of how we might test the above decorator.

<?php
namespace Tests\Repositories\Decorators;
use Tests\TestCase;
use App\Repositories\Decorators\CachingUserRepository;
use Mockery as m;
class UserTest extends TestCase
{
/**
* Test fetching all items
*
* @return void
*/
public function testFetchingAll()
{
// Create mock of decorated repository
$repo = m::mock('App\Repositories\Interfaces\UserRepositoryInterface');
$repo->shouldReceive('all')->andReturn([]);
// Create mock of cache
$cache = m::mock('Illuminate\Contracts\Cache\Repository');
$cache->shouldReceive('tags')->with('users')->andReturn($cache);
$cache->shouldReceive('remember')->andReturn([]);
// Instantiate the repository
$repository = new CachingUserRepository($repo, $cache);
// Get all
$items = $repository->all();
$this->assertCount(0, $items);
}
/**
* Test fetching a single item
*
* @return void
*/
public function testFindOrFail()
{
// Create mock of decorated repository
$repo = m::mock('App\Repositories\Interfaces\UserRepositoryInterface');
$repo->shouldReceive('findOrFail')->with(1)->andReturn(null);
// Create mock of cache
$cache = m::mock('Illuminate\Contracts\Cache\Repository');
$cache->shouldReceive('tags')->with('users')->andReturn($cache);
$cache->shouldReceive('remember')->andReturn(null);
// Instantiate the repository
$repository = new CachingUserRepository($repo, $cache);
// Get all
$item = $repository->findOrFail(1);
$this->assertNull($item);
}
/**
* Test creating a single item
*
* @return void
*/
public function testCreate()
{
// Create mock of decorated repository
$repo = m::mock('App\Repositories\Interfaces\UserRepositoryInterface');
$repo->shouldReceive('create')->with(['email' => 'bob@example.com'])->andReturn(true);
// Create mock of cache
$cache = m::mock('Illuminate\Contracts\Cache\Repository');
$cache->shouldReceive('tags')->with('usersUser')->andReturn($cache);
$cache->shouldReceive('flush')->andReturn(true);
// Instantiate the repository
$repository = new CachingUserRepository($repo, $cache);
// Get all
$item = $repository->create(['email' => 'bob@example.com']);
$this->assertTrue($item);
}
public function tearDown()
{
m::close();
parent::tearDown();
}
}

As you can see, all we care about is that the underlying repository interface receives the correct method calls and arguments, nothing more. That way our test is fast and repository-agnositc.

Other applications

Here I’ve used this technique to cache the queries, but that’s not the only use case for decorating a repository. For instance, you could decorate a repository to fire events when certain methods are called, and write different decorators when reusing these repositories for different applications. You could create one to log interactions with the repository, or you could use an external library to cache your queries, all without touching your existing repository. Should we need to switch back to our base repository, it’s just a matter of amending the service provider accordingly as both the decorator and the repository implement the same interface.

Creating decorators does mean you have to implement all of the interface’s methods again, but if you have a base repository that your other ones inherit from, you can easily create a base decorator in a similar fashion that wraps methods common to all the repositories, and then just implement the additional methods for each decorator as required. Also, each method is likely to be fairly limited in scope so it’s not generally too onerous.

Recent Posts

Decorating Service Classes

Simplify Your Tests With Anonymous Classes

Adding React to a Legacy Project

Do You Still Need Jquery?

An Approach to Writing Golden Master Tests for PHP Web Applications

About me

I'm a web and mobile app developer based in Norfolk. My skillset includes Python, PHP and Javascript, and I have extensive experience working with CodeIgniter, Laravel, Django, Phonegap and Angular.js.