duncanmcclean.com Duncan McClean's personal website. en-GB Copyright 2023, Duncan McClean Fri, 18 Nov 2022 00:00:00 UTC Fri, 18 Nov 2022 00:00:00 UTC 60 <![CDATA[Statamic developers to follow on Mastodon]]> Over the next few days, it's looking like more and more folks will start moving away from Twitter after all that's happening right now.

It looks like Mastodon seems to be the Twitter replacement for lots of Laravel/PHP folks. After seeing Freek's post about Laravel & PHP developers to follow on Mastodon, I thought I'd make an additional list for Statamic developers to follow on Mastodon.

If you want to be featured on this list, reply to my Tweet or post on Mastodon and I'll get you added. I know I've probably missed some people.

  • Joshua Blum: joshuablum@mastodon.social

  • Rob de Kort: robdekort@mastodon.social

  • Erin Dalzell: emd@mastodon.social

  • Dave Smyth: davesmyth@mastodon.social

  • Me: duncanmcclean@mastodon.social

  • Jack Sleight: jacksleight@mastodon.social

  • Ryan Mitchell: ryanmitchell@mastodon.social

  • David Lindahl: Austriker@mstdn.io

  • Marty Friedel: martyfriedel@mastodon.au

  • Rias Van der Veken: rias@phpc.social

  • ncla: ncla@mastodon.social

  • Johannes Schuba: joschuba@mastodon.social

  • Simon Schweissinger: simonolog@mstdn.social

  • Konafets: Konafets@norden.social

  • klickreflex: klickreflex@freiburg.social

There's a bunch of different Mastodon "servers" - you can signup for any of them really & you can follow people from different servers. I'm on mastodon.social (a popular one) but there's also other options like mastodon.cloud, mstdn.social and phpc.social.

Justin Jackson has written a post if you're interested in getting started with Mastodon.

<![CDATA[Using Torchlight with Statamic's Bard field]]> Torchlight is a really awesome API for syntax highlighting. In the past, folks might have used libraries like Prism or highlight.js. These libraries work but they're not completely accurate.

Whereas, Torchlight uses the same syntax highlighting system under the hood as VS Code which means it can support any language that VS Code can (including Antlers!)

I've been using Torchlight on this site for a while, along with on my documentation sites. And as I'm seeing more and more folks using it, I thought I'd share how to setup Torchlight to work with Statamic's Bard field.

Install Torchlight

Before you can do anything else, you'll need to install Torchlight's Laravel package and copy over the default config file. You can do these two steps by running these two commands:

In your config folder, you should now see a `torchlight.php` file which contains some settings, like the VS Code theme you wish your code blocks to be rendered in.

If you haven't already, create an API token which you can do on this page (provided you're logged in). Then, take the token and add it to your `.env` file.

Next, add Torchlight's middleware to the Http Kernel (`app/Http/Kernel.php`).

Add a set your Bard field

Next, go in and edit your blueprint, click into your Bard field (create one if you don't already have one), then create a new set which will be used for your code blocks.

I have two fields in my 'Code block' sets. One for the code itself (using the Code fieldtype) and one for the language which we later pass to Torchlight to highlight the code properly (can use a normal Text fieldtype).

Then, you'll want to save your blueprint.

Torchlight ūüĒ¶

Add this code to wherever you're looping through your Bard field and have a code block set (replace the handles if you need to)

Next, we need to create the Torchlight tag you see in the code example above. I've already written this for you, so all you need to do is paste it into `app/Tags` as `Torchlight.php`.

Finally, you'll want to add in Torchlight's CSS to your site so the code blocks will be rendered properly - as documented on the Torchlight site.

Now, when you load up your site, your code blocks should be rendered with Torchlight. ūüöÄ

<![CDATA[Under the hood of the Duplicator addon]]> Duplicator is by far my most popular Statamic addon, by downloads at least. And to be honest, it's probably one of my simplest.

Duplicator registers a few actions: one for each of the 'things' you can delete - an entry, an asset, a term and soon (hopefully) a form.

Actions are what you see when you click the 'three dots' on the listing tables:

Today, I'm going to walk you through the action for duplicating entries (which is probably the one used most often). So let's dig in:

I thought I'd start off easy. That just returns the title of the action which is the text that's displayed on the 'actions dropdown in the CP.

We're making use of Laravel's __ method here which lets us 'localise' the text so it can be 'Duplicate' but in whatever language the current Control Panel user is using.

The community has contributed translations for a few languages - you can review the docs here on what languages are supported and how to add your own.

Actions can optionally use the fieldItems method to return any fields which should be shown to the user before the action is run.

If Duplicator is running on a multi-site, it'll show a 'Site' dropdown field which gives the user the option of duplicating the selected entry to a specific site or even all sites.

We'll get onto how the multisite duplicating works in a little bit.

These two methods (visibleTo and visibleToBulk) are where you decide whether an action is shown/visible for a certain item(s). An item could be an Entry, a Term, an Asset, etc.

The visibleTo method expects a single item to be passed in. We then check whether the $item is an instance of the Entry object. If it is, we'll return true to tell Statamic our 'Duplicate' action should show in the Actions List.

The second method, visibleToBulk does pretty much the same thing but multiple items are passed into it.

Because I was lazy when I wrote this, we're just calling the other method for the first item in the $items collection. In some of my other addons, I map through the $items, pass it through the visibleTo, then check the count of them being true is the same as the original count of the $items collection.

The visibleToBulk method is called when you select multiple entries in the Listing Table - which then displays the available Actions at the top of the table.

And here we are... the meat of the article. How Duplicator does the duplicating!

Because an action can be run on multiple items at once, the first thing we're doing is looping through each of the $items with a Laravel Collection.

Next, we check if the current $item is actually an Entry instance (in case, somehow the visibleTo stuff didn't work).

After that, we then call two custom methods: getEntryParentFromStructure which figures out the entry's parent from the collection's tree, then the second, generateTitleAndSlug generates a title and slug for the duplicated entry. We'll discuss how both of these methods work individually later on.

Next, we start building an Entry object. We set the collection, the blueprint, the locale/site for it to be duplicated into, the publish status, the slug we just made, along with 'entry data'.

  • The collection will be the same as the original entry, so we can use $item->collection() to grab it.

  • The blueprint will also be the same. We can get the handle of the blueprint with $item->blueprint()->handle()

  • Depending on what the user selected when choosing the site(s) to duplicate on, we'll either duplicate to the selected site or to the same site as the original entry.

  • By default, for the publish status, we'll use the same published state as the original entry. However, you can override this in the addon's config file (maybe you want any duplicates to be unpublished by default)

  • The slug will literally just be the slug we generated a minute ago.

  • In terms of the entry data, we'll mostly just copy over the entry data from the original entry but we'll remove any 'ignored fields' (a config option in Duplicator) and we'll set a different title.

After that, we check if the entry collection has dates enabled. If it does, we set the date of the duplicated entry to the date of the original entry.

Duplicator has a concept of 'fingerprints' which essentially means it leaves a is_duplicate key on the entry so you can tell if an entry was created by Duplicator.

We then save the entry! ūüéČ

After it's saved, we do a couple more things:

  • Add the duplicated entry into the collection's tree (if it has one) - it'll be added just underneath the original entry.

  • If the user wants an entry to be duplicated to all of their sites, we'll do that here by creating a 'localisation' of the entry for each of the other sites.

Here's the first of our two custom methods. As described above, this method figures out the parent of the original entry in the collection's tree.

If the collection doesn't have a tree, we'll just return early.

Otherwise, we'll get the instance of the original entry in the tree, then we'll call the ->parent() method on it.

If the original entry has no parent in the collection tree, we also then just return early.

We then do another check to see if the original entry was the 'root page' in the collection tree (eg. a homepage). If it is, we also just return early as we can't have two root pages ūüėÖ.

If we make it this far, we simply return the parent entry.

And here's the second custom method...

This one generates the title & slug for duplicate entries. You might notice that if you duplicate the same entry multiple times, you get titles/slugs like Entry #1, Entry #2, Entry #3. This is the method that figures how many duplicates you've previously made and adjusts the title.

I copied this code from Statamic v2 (it had Duplicator's functionality built into Core).

The first thing this method does is assigns the title & slug of the original entry to variables.

Then, it'll generate a title based on the $attempt variable. So, $attempt being 5 would generate Entry #5.

It then checks if there's any existing entries in the collection with that title. If there is, it calls itself again, and ups the $attempt by 1. It does this until it finds a unique title/slug it can use, which it then returns.

There you go! I've literally given you all the code to build your own Duplicator addon. Hopefully that's been a helpful learning experience for you - diving into how Actions work & how to interact with some of Statamic's internal APIs.

<![CDATA[Tailwind CSS 3 is awesome! ūüöÄ]]> I've been living under a rock for the last few months. I've had a chance to check out Tailwind CSS v3 on some fresh projects and I love it!

I've made a note of some of my favourite features:

Play CDN

Whenever I go to whip up a prototype for something, I find using the Play CDN a really easy way to bring in Tailwind and add a few custom config options.

My only wish is if I could pull in 1st party plugins, like Typography and Forms.

Updates to the Typography plugin

I've been using the Typography plugin since it came out. It's simple, just add the prose class and your content will be all nicely formatted!

But, I discovered a new feature the team have released: applying classes to elements inside the Prose element.

Scroll snapping ūü§Į

I watched Simon's "What's new in Tailwind CSS v3.0" video and was amazed at how much there was to share!

One bit stuck out to me though, the 'scroll snap' feature. I didn't even know that was possible in CSS.

Docs: https://tailwindcss.com/docs/scroll-snap-type

Thanks to the @tailwindcss team (& all the collaborators) for all their hard work! ‚ô•ÔłŹ CSS is fun again!

<![CDATA[My Documentation Sites: How they work]]> FYI: I originally posted this on Twitter. If you want to see more like this, follow me over there, I'm @_duncanmcclean.


Last year, I unified the way I handled the docs sites for my addons.

They're all handled by a single Laravel app which pulls in the content from each addon's GitHub repo.

I thought it could be interesting to share how it works...

1. Get the content from GitHub: If we don't already have the docs content, it'll go grab it using GitHub's zip download feature.

Then, it'll extract the zip and move the docs into their own folder. The images are also moved around, into the 'public' directory.

2. Next up, we go and get the file from the directory we just copied them to. If the requested file doesn't exist, we throw a 404.

3. Now we have the file contents: it's passed into Commonmark's Markdown converter and piped through some extensions, like Torchlight, a YAML front matter parser and one that handles Markdown tables.

4. Now we have the HTML, it's passed into a Document class, along with some bits of metadata, like the page title & path.

At this point, whatever we returned is sent to the Cache so it's super duper quick for the next user to visit this docs page.

5. Last but not least..

Whenever I push changes to my addon's on GitHub, a webhook is triggered which clears the cloned files & the Laravel cache for the addon that's been updated.

And that's it! Honestly a pretty simple system and I'm pretty happy with it.

You can see it in action over on the Runway docs: https://runway.duncanmcclean.com/

<![CDATA[Testing with Stripe Elements in Laravel Dusk]]> Today I've been playing around with using Laravel Dusk to test a payment form on a small website.

The payment form is a key part of the owner's business so it's crucial it works all the time, whenever the customer makes a purchase.

The site uses Stripe Elements to let the customer enter their payment information securely.

Elements creates its own iframe element which makes it a little tricky to target and test. However, there's a handy method in Dusk which allows you to type inside of an iframe.

Anything you need to type inside of Stripe's iframe you can do inside the withinFrame method and target the relevant inputs via their placeholders.

Hopefully someone finds that helpful! ūüôā

<![CDATA[Get rid of red bars in Sketch]]> If you're slicing a design from a designer in Sketch, you may find some horizontal/vertical red bars. Here's a quick little how-to on how to get rid of them.

  1. Go to the Sketch artboard with the red bars

  2. Go to View in the top Sketch menu, in the dropdown, hover over Canvas and then Layout Settings

  3. You'll see something like this:

  1. Uncheck Columns and Rows, then press Confirm and hey presto, bars are gone!

<![CDATA[Install Imagick with Laravel Valet]]> I'm going to presume you already have Valet and Homebrew setup. To install Imagick, there's a couple of commands you'll need to run:

  • brew install imagemagick

  • brew install pkg-config(also if it's already done)

  • pecl install imagick

  • valet restart

In case you get a Connection Timeout when visiting one of your Valet sites, you may need to reinstall Valet with valet uninstall && valet install && valet park

<![CDATA[When you accidentally block Forge from your server.... ūü§¶‚Äć‚ôāÔłŹ]]> I did something a bit stupid yesterday... I was playing around with Firewall rules on my Forge server. I setup a rule so that the only place I can SSH into my server was from my home IP Address.

However, as soon as I did that, I realised that I could not longer deploy code or do anything via Forge on that server. Then I realised my mistake.... I blocked Forge's IP Address from SSH'ing to my server.

In case someone else finds them in my position, here are the two commands I ran to fix the issue. Of course, you need to SSH to run them.

Forge uses ufw under the hood to manage firewall rules so that's what I used to manually give Forge back control. The two IP addresses I listed in those commands link up to the IP addresses from their documentation.

<![CDATA[Simple Commerce has launched!]]> A few weeks ago today I finally launched the first version of Simple Commerce.

If you've not been following me for very long, Simple Commerce is the e-commerce addon I've been building for Statamic.

If I remember correctly, I started prototyping what Simple Commerce could look like in late November/early December last year (when it was in private alpha) and started working on it properly in January/February time. I even rebuilt the addon about a gazillion times but more about that in a minute.

The story...

I've been in and around the Statamic community for the past few years. In that time, there's been plenty of people who have asked for ways to handle e-commerce in Statamic.

Previously in v1, Jason (who's now one of the Statamic gents) built Bison and in v2, someone built Statamify which has now been archived. If you really wanted to use Statamic for your e-commerce, you could also use Snipcart but it's a separate service.

In November/December of last year, when I was thinking about what I could build to test out addon development in Statamic 3, I tried to think about what I could build that's been widely requested, the first thing that came to mind was e-commerce.

I talked to a few people about it, mainly Erin, who tried to persuade me against it but I went ahead and did it anyway ūüėā

And so I began.....

Initially I tried building my own version of Statamic's collections system to handle products, orders etc. I inherited some traits and did some other stuff that I didn't really understand. I can't remember how well I got that version working (if I ever did). If you're really interested, feel free to dig through the commit history.

After a couple weeks doing that I think I moved onto using Eloquent instead. It wasn't completely convinced it was a great option but I went with it anyway. My main reasoning behind the switch was "its easier to do relationship stuff and query things. it makes it faster for development". Technically, all of that can be done with Statamic's facades but that's not something I understood how it worked 6 months ago.

The eloquent driven version actually worked quite well. Once it was fairly stable I have access to a few people in the Statamic community so they could give it a try and see the progress I was making. I also refactored & rebuilt the eloquent version many, many times. That's the part that probably took the longest in the whole project.

At the start of June, I decided to set a launch date for getting the Eloquent version out of the door after being in development for the longest time. I set the date for the end of the month. I had everything planned perfectly but then I thought about something....

If someone new is looking at Statamic and thinks "Oh nice, a content management system with no database. That's pretty rad. I wonder if I can build an e-commerce site with it. Looks at Simple Commerce, goes to the install guide. what the chickens?? why do I need a database for a flat-file CMS".

I kept thinking about that for a few days, in the end, I came to a conclusion: I need to rebuilt this with flat files!!

So there I went.... I spent the best part of a month, spending pretty much all of my free time rebuilding this e-commerce addon back into flat-files. But this time instead of rebuilding the collections system, I actually just used the Collections system.

I wanted to make Simple Commerce feel as native to Statamic as possible. Eloquent wasn't native, but collections/entries are. Using pre-built views and controllers wasn't native but just letting users use Statamic Antlers the way they're used to is.

It feels so much better and lightweight now that everything's done 'the Statamic way'.

A few week back I officially launched it but I didn't really make a big deal out of it. I only really announced it on Discord so hopefully this blog post is a better announcement!

There's already a few people trying it out and building sites with it, I really can't wait to see how it grows. If you're interested - feel free to give it a whirl! I've got some plans for it over the next few months but I'm keeping them secret for now ūüĎÄ

<![CDATA[Fix HTTPS issue in the Statamic 3 Control Panel]]> In the past, I've had issues when trying to use the Statamic Control Panel alongside using HTTPS. Usually, you'll get issues like the JS & CSS not loading or requests to the CP's API not working properly.

The good thing is.... it's an easy fix.

2021 Update: This issue commonly occurs when using Cloudflare with Statamic. The recommended way to resolve this issue is to use the Cloudflare Trusted Proxies package.

In the site's AppServiceProvider class (app/Providers/AppServiceProvider.php), add this into the register method:

If you only want it to apply while in production you could add a little environment check like so:

Hopefully that should sort the issues you're having!

<![CDATA[How to Bypass Composer's Memory Issue]]> Since I reset my laptop a few months back I've been getting memory errors when I try and run Composer commands, like composer update.

But today, I've finally found a proper solution to bypass them whenever you run into them.

Instead of running just composer update, you can run this instead: php -d memory_limit=-1 /usr/local/bin/composer.

That command will temporally set your local PHP's memory limit to -1 (so basically unlimited) and will use the normal composer binary to run the rest of the command.

Hopefully someone finds this helpful.

<![CDATA[Building a Likes addon in Statamic 3]]> Recently, I had to build out a help site for a client. One of the features in site was for the user to be able to like help article or forum posts. I managed to build it out reasonably quickly, I think it took me a day to get everything working.

I was thinking that this sort of addon would be a good candidate for building in a blog post. So that's exactly what I've done. It goes through each step of process, from bootstrapping the addon in Statamic's command-line to creating a small little front-end component for likes.

Bootstrapping our addon

The first thing we need to do is bootstrap all the things that we need for our addon. We'll need to create our service provider, setup all the Composer stuff, etc. I'm going to walk you through all of that.

You could always bootstrap your addon by using a boilerplate but we're going to do it from scratch in this tutorial.

To get started, Statamic actually provides a nice please command to get most of the stuff we need up and running. Just do php please make:addon damcclean/likes in your Terminal. Remember to replace damcclean/likes with the Composer package name for your addon.

Once that command has done it's stuff, you should see a new directory popup in your site's root directory. An addons directory. It'll contain two other folders, damcclean and then likes because that's my Composer package name.

In the likes folder, you'll see two things, a composer.json file for your addon and a src/ServiceProvider.php file which is our addon's service provider.

The composer.json file tells Statamic the name of your addon, it's description, the namespace for the Service Provider etc.

And the ServiceProvider.php is the class that tells Statamic what things should be booted up and registered. Whether that be routes or commands or middlewares, that's where they all get registered. We'll come back to this file later on in the tutorial.

We now have a Statamic addon running from addons/damcclean/likes. ūüéČ Well Done us!!

Setting up our tests

I don't know about you but when I'm writing addons or any sort of backend code, I like to write tests to check that my code works and it returns what I want it to return.

You could always do your testing manually but I like to automate it. Laravel, the framework that sits behind Statamic, uses a testing framework called PHPUnit.

PHPUnit is the testing framework we're going to pull in, in a minute. We're also gonna pull in a thing called Orchestra Testbench. Testbench sits on top of PHPUnit and provides helper functionality for building Laravel packages. (if you've not guessed it already, a Statamic addon is a Laravel package)

Anyway, to install PHPUnit and Testbench, run this command inside your addon's directory. likes in my case.

After that command has done it's thing, you'll see some more files popup. You'll see a composer.lock file which you can ignore, and you'll see a vendor directory. In case your new to Composer, the vendor directory is where all of the Composer dependencies are installed. In our case, those dependencies are Testbench, PHPUnit and all of the other packages they require to run.

If you're planning on setting up a Git repository for this project, you're gonna want to ignore that vendor directory. It's never a good idea to keep those sort of things in Git (except in Statamic 2, but that's away from the point).

To do that, just create a .gitignore file and add the folder name.

Also, you're going to want to install Statamic inside your addon's composer file too, so your test suite can take advantage of Statamic things.

Before you can just install it, you'll need to lower your minimum stability level for dependencies, which is easy enough to do in your composer.json file.

And then you can require statamic into your addon with composer require statamic/cms.

Now, we need to setup our PHPUnit configuration file. You should create a file called phpunit.xml in your addon's root directory. I've also included the contents of the config file.

Next, we need to create the directory where all of our tests are going to live. Just call it tests and put it alongside the src folder in your addon's root directory.

Now that we have tests folder, we'll need to create two files.

The first of which is our Test Case. The TestCase is a class that will be extended by each of your tests to tell it how to run Statamic, how to run your addon, etc. Just create a file called TestCase.php in your tests folder with the following contents.

Most of that should just be a simple copy paste job, but there's one thing you'll need to adapt to make it work for your addon. In the getEnvironmentSetup function, you'll need to change Damcclean\Likes to your own package name.

Also, we'll need to let Composer know about our testing namespace, to do that, just add this to your addon's composer.json file.

Just to be sure, you might also want to run composer dump-autoload to make sure it picks the new namespace up.

Later on in this tutorial, we'll be running tests with real collections and entries, so there's some stuff we'll want to setup to fake that. First, create some folders:

  • tests/__fixtures__/config/statamic

  • tests/__fixtures__/content

  • tests/__fixtures__/users

You'll probably also want to add some more things to your addon's Gitignore file.

As well, you'll want to create a stache.php file inside your fixtures/config/statamic folder. This file will be used in testing to override the default Stache config, basically just telling Statamic to use the directories we just created to store our content and users.

There we go! We've setup our test suite so it's all ready to go!

Writing some real code...

In order for users to like things on the site, we'll need to create a few things. We'll need to create a field where user IDs will be stored, we'll need to create a controller where the like/dislike submissions will go and we'll need to create some sort of front-end component that can send data to that controller.

OK, so let's take one thing at a time, starting with the field I was talking about. The easiest way to do any sort of content work is through the Control Panel. So just login to the CP, go to the blueprint where you want to use likes, and add a field.

You'll want to add a Users field because it allows us to store a list of Statamic user IDs, which we'll need to tell who has liked the entry.

Make sure to change the Max Items setting on the field so that it can store more than 1 user's ID.

Now that we've got the field in our blueprint, we need to create some controllers to do the legwork of actually liking and unliking entries.

In your addon's folder, addon/yourname/packagename, create a folder structure like this, src/Http/Controllers. In the Controllers folder you'll want to create a controller, you can call it whatever you want but I'll call mine LikeController.

My LikeController is going to have two methods, a store method which will be hit whenever a user likes an entry and a destroy method which will be hit whenever a user unlikes an entry.

Now we've got the methods, let's make them do something! I'm going to start with the store method.

So when someone makes a request to the controller, we want to know two things: the currently logged in user and the ID of the entry the user liked.

To do this, we're going to add the Request $request and the $id as method parameters.

Oh and by the way, Request is Illuminate\Http\Request

Next we're going to want to get an instance of the entry from the ID because the ID is just a randomly generated string. We can do that by calling the Entry facade, provided by Statamic, and it's find function.

Next, we'll want to grab all of the likes the entry currently has and we'll want to add the logged in user's ID to that list.

In the code you can see below, we create the $likes variable, we check if there are any likes on entry at the moment, if yes, we use them, if not we just set an empty array.

We also go ahead and merge in the ID of the currently logged in user to that array.

Now all that's left for us to do is save the updated likes array back to entry file, which is as easy as pie.

I've returned went ahead and returned the user back to the page they came from, but that's up to you.

So we're done with the store method, now let's jump onto the destroy method which removes the users' like from the entry.

Again, we're going to use the Request $request and the $id as method parameters. We're also going to need to find the entry as well.

Next we just want to remove the currently logged user's like from the array of likes and then save the entry. The way I'm going to handle it is by using Laravel's collect method, going through each of likes, removing the one with the user's ID and then outputting a fresh array.

That's really all we need for the controllers. Next we need to create routes so the controller method can actually be hit from the web.

We're going to create Action routes, which means the URLs will look like this /!/likes/like and /!/likes/dislike.

We're going to need a routes file, create a routes folder in your addon's root and then create an actions.php file. This file will hold our addon's routes.

Your entire routes file only needs to be three lines long.

Sadly, Statamic doesn't register these routes automatically, so we'll need to go and register them in our addon's ServiceProvider.

Registering things in your service provider is actually incredibly easy. I'll just explain how you do it for routes, but it's pretty much the same thing for registering fieldtypes, tags, widgets, modifiers, etc, you get the gist.

As long as you've setup everything the same way I have, that should just work.

And the front-end

The backend code is all done, but we still need to actually build out the front-end so users can actually like/dislike entries.

In this example I'm going to build out a simple like/dislike thing in Antlers but there's no reason you couldn't use a Vue component or something along those lines to do it. There's no wrong answers.

I made my likes 'component' into a nice Antlers partial that displays a count of how many people have liked the entry and buttons to like/dislike depending on if the user has already liked the entry or not.

(yes I know the above code snippet is broken, here's a Github Gist of what it's meant to look like)

If you look at the code for the two forms. They are the endpoints that point to our controller actions that we made earlier.

Finally, let's write some tests

We setup PHPUnit and Orchestra Testbench earlier in this tutorial but we still need to write some actual tests so that we can know for sure that our controller code does exactly what we want it to.

We're going to write a few tests. One test to make sure that we can like an entry and one to make sure we can dislike one. We're going to need a file for our tests, I'm just going to call mine LikeControllerTest.php and place it in my addon's tests directory.

To get us started, I've made a quick scaffolding of the class.

I usually use /** @test */ above my test methods but if that feels weird to you just add test_ in front of the method names, like test_can_store_like.

Let's start writing our first test. Before writing the test, we need to think about what we need to setup, what we need to do to run the code and what we need to do to make sure the code did its job.

What we need to setup: a user, a collection and an entry

What we need to do to run the code: hit the action route

What we need to do to make sure the code did its job: make sure the user's ID is inside of the entry's file

Let's start with the setup! We're going to setup our user, our collection and our entry with some helper methods I built into the TestCase you copied in earlier, just to make the process nice and easy.

Next, we'll want to hit the route that goes to the controller. Laravel provides quite a lot of HTTP testing stuff out of the box, so there's nothing custom needing done here.

Basically what we're doing there is logging in as the user we just created, making a POST request to our Like route with the ID of the entry and we're asserting that we get a redirect status code back.

Heads up: Be sure to change the assertRedirect assertion to something else if you didn't return a redirect in your controller.

The last thing we want to check is that the user's ID has been added to the likes array of the entry. To do this we're going to get an updated instance of the Entry and we're going to do a quick check against the likes array.

You should end up with this in the end:

Now onto our test to make sure a user can dislike an entry. Before writing the test, I'm going to think about what I need to setup, what code I need to run and how I can check the code has worked correctly.

What we need to setup: a user, a collection and an entry where the user's ID is in the likes array

What we need to do to run the code: hit the action route

What we need to do to make sure the code did its job: make sure the user's ID is not inside of the entry's file

This test is pretty much identical to the last one, except from the fact we need to include the users ID in the likes array during setup and we need to check the user's ID is not in the likes array at the end.

So after we've created the entry, we just need to do the same sort of thing we did in the controller to add the user's ID to likes array.

And then at the end when we asserted that the likes array in the entry contains the User ID, we just want to do the opposite so we use assertStringNotContainsString.

So in full, the delete test should look a little like this:

And then if you go into your addon's root directory in your Terminal and if you run ./vendor/bin/phpunit, you should get some green text, like this:

That's it! You've built a likes addon, with a fully working test suite. You should be proud of yourself!

Hopefully this tutorial was clear enough for you to understand, if you have any questions, ping me on the Statamic discord and I'll try to help


Big thanks to Jason Varga for proof reading this for me!

<![CDATA[Move S3 objects from one AWS account to another]]> I'm in the process of moving all of my AWS stuff into it's own account, separated from my online shopping account. It's a process that has to be done manually as AWS don't have an easy way of moving resources between accounts.

Anyway, to stop you trolling through Amazon's horrible documentation to get this ugly job done. Here's some simple instructions on moving files between buckets and AWS accounts.

  1. In the AWS account you want to move stuff to, create a new bucket, sadly it'll need a unique name that's not used by your current bucket.

  2. Get the AWS account ID of your new AWS account (the one you wish to move stuff to). You can find it in your 'My Account' page.

  3. Update the bucket policy in the old bucket (old AWS account) to this. Replace ACCOUNT_ID with your new AWS account's ID and replace awsexamplesourcebucket with the name of the old bucket.

  1. Then you'll want to install the AWS CLI, if you haven't already and configure it for your new AWS account.

  2. Then you should be able to run the following command, replacing the obvious. If you did everything right, that command should copy everything over to your new bucket.

    aws s3 sync s3://old-bucket-name s3://new-bucket-name
<![CDATA[Use Laravel's Maintenance Mode in Statamic]]> Now that Statamic is just a package inside a Laravel application, it means you can take advantage of Laravel features.

There's a Laravel feature called Maintenance Mode which means visitors will see a 'site down' message. In this article, I'm going to show you how to use it and how to setup your own custom site down screen.

First, let's understand how it works. Laravel's command line, artisan, comes with two commands. php artisan down and php artisan up. The down command takes the site down and the up command puts the site back up. Simple.

When you take a Laravel app down, it stops users from accessing any pages on the site. However, you may want to override this for a few routes, for example, like the Control Panel.

It's actually really easy to do this. Just open up App\Http\Middleware\CheckForMaintenanceMode.php. It should look like this:

To add the Control Panel to our exceptions list, just do something like this:

Now you should be able to access the Control Panel while your Laravel app is in maintenance mode.

By default, your visitors will see a screen like this while you maintenance mode turned on.

However, it's likely that you'll want to customise this page to fit in with the site. It's actually really easy to do. Just create a Blade or Antlers view with this filename. resources/views/errors/503.blade.php

Whatever you put in that file will get picked up and used instead of the one Laravel provides.

And that's it - it's really that simple!

<![CDATA[Setting up for Statamic addon development]]> The Statamic 3 beta has been out since the middle of November if I can remember correctly. V3 basically turns it into a package for Laravel rather than a pre-built Laravel app. This is the change which allows for Statamic add ons to finally become Laravel packages.

I’m gonna quickly give you a walk through of how you get started building an addon, including setting up the environment.

Just figured out that you can skip this whole manual process, all you need to do is run php please make:addon damcclean/github-gists. Obviously you can replace the package name with your own.

I’m going on the assumption that you already have v3 installed on your site. If not its only a composer command away.

Anyways, firstly you want to create the folder structure of where you want your addon to live.

So for example - if you wanted to create an addon called GithubGists and your Github username was damcclean, then your folder structure could look like this:

Obviously you can create the folder structure in whatever way you want but this is the recommended way to do it, from what I can gather.

In your terminal, navigate to inside of your GithubGists folder (or whatever you called it) and run composer init. It’ll lead you through a wizard. When it asks you for a package type, type in statamic-addon. This will let Statamic know that your package will be a Statamic addon.

Then you can open your addon's composer.json file to make a few changes.

You’ll want to add in this snippet. It’ll let composer know where to load in your namespace from. In my code, I’m loading in the Damcclean\GithubGists namespace from my src folder.

This next bit of code does two things. It lets Statamic know the name and description of the addon, so it can display it inside the Control Panel. And then the second bit talks to Laravel to tell where the Service Provider is, we’ll get round to actually creating this in a bit.

I think that’s pretty much it for composer.json. Next we can create our service provider.

I’d recommend just calling it ServiceProvider and putting it in the src directory. That’s the way most people do it.

For those of you who are new to the concept of service providers, it’s basically a class that Laravel goes to where you call everything you need to make your addon work.

So your routes would be registered in your service provider, so would your widgets, your tags etc.

In your service provider, you need to extend the AddonServiceProvider class and have two methods, boot and register.

Now we’re almost done!! The next thing is to actually install our addon in our Statamic site.

So for that, in your main composer.json, add in the name of your package to the require section. Also, just add dev-master as the version.

Just so it knows where your project lives, as its not live on Composer, you’ll need to add it as a repository which is pretty easy, just add this code to the bottom of the same file. Obviously changing out the folder names etc for the ones you have setup.

After that, just run composer install in your project’s root directory and you’ll be as good to go.

<![CDATA[2019 Year in Review]]> I've never written one of these before but I thought it would be a good time to start, especially as I've had a year where quite a few things changed in my life.


In March of this year, I started building ReadCast, an app which converts articles from the web into your own personal podcast. I started it after J Cobb sparked a reply to a tweet from my friend Miguel about his startup, BlogCast which does the same thing but for publishers.

I remember that I spent a few hours on a Saturday and I built out the key parts of the app - saving articles, crawling those articles and turning them into audio.

Quickly after that, I decided to put ReadCast on the web for others to use and I launched it on ProductHunt where we managed to get to 4th product of the day (even if I got the product featured by speaking with one of the PH team).

I remember the first paycheck coming into my bank account, it was so cool being able to get money from something I made and put on the internet. It felt like a real acomplishment.


This year was my last year at secondary school (high school). Although it didn't come without its challenges.

I was never good at learning, especially at learning things that I didn't enjoy. I find it very hard to read a page (whether that be in a book or on the internet) and get anything from it. I could read a whole chapter of a book and not know a single thing about what I read. This started to really upset me and stress me out as we would do RUAE's in English (a closed reading passage) and we would have to answer questions. No matter how many times I read over the passage, it didn't make any difference to what I knew about it so I ended up just writing down random answers.

I also had exams to do which made me very stressed and worried about it. Even if at the end of the day it's just a few words on a bit of paper that means you've spent 2 years learning something.

I actually failed all of my prelim exams (except for the Computing one). Because of that my school made me feel bad by sitting me down in a room, along with my peers who failed any of the prelims and they forced us to partner with a teacher in each of the failing subjects so we pass (although, I reckon they only cared about the school's repuation and not how I did in my results).

Throughout all of this, I started to pray to God for the strength to get through my exams and not to be stressed as I know he holds it in his hands. And if I do fail, it ultimatly doesn't really mean anything. I belive that God was the one who managed to get me through that time in my life.

Leaving school

As I said this year I left school after not really knowing what to do for the remaining two years I had left. Straight after I chose my subjects, I went to visit the school's careers advisor with my mum and he urged me to start applying for college courses because it might be a less stressful option than staying in school and it might be something that I might enjoy doing at the end of the day.

So that night I went and applied for two colleges, I got an interview for both of them. I also got accepted to both of them without any conditions. I feel like God was with me and trying to point me to the way he wants for my life.

I left school at the start of May and had until the end of August until my college course started but I'll get into that later.

Moving house

This year, my family and I put our house up for sale. My parents had lived in that house of 17 or 18 years, they moved in on the 23rd of December 2001 I think.

We all fancied a bit of a change and a bit of a bigger house, especially for my sister who had a really, I mean really small room.

We put offers in on two houses, one a few miles away and one in the same housing estate. Our offers didn't get accepted on either of them.

After seeing the second house, before we made our offer, we decided to start selling the house. On the first night of viewings, 4th viewer in, there was a couple who were really enthusiastic about the house and the surrounding area. They wanted to buy it from us there and then, but legally we had to go through the lawyers... haha

But we were left for a month or two where we had sold our house and signed it away but we still hadn't found ourselves a new house.

We did however go for a viewing to a house which is round the corner from where we are now. We went to the house which was totally empty, as in no furniature but there was stuff in the fridge, a CCTV system setup and there were clothes in the bedroom wardrobes. But no one was living there. We thought something dodgy was happening there so we didn't put an offer in.

However, a week or two after that we decided to go and see the show homes in a new housing development. We asked the ladies in the show home about if they have any homes left, they said yes, one. We asked about bedrooms, they said three, the amount we were looking for. We asked about price, it was in the high end of our price end. We also asked when would it be ready, they said late July early August and we said brillant. It ticked all of our boxes. The bedrooms were all big enough and the back garden was much bigger than we could have ever dreamed of.

My parents actually went and signed on the dotted line the day after we went into the show home which meant it would eventually become our new home.

Moving day didn't exactly go to plan because of some legal issues but it all worked out eventually.

But again, God comes into play. He's the one who directed us down to this new housing estate. He directed us down here for a reason because he had a house sitting ready for us at the perfect time. Without us putting our trust in the Lord, I honestly don't know where we would have ended up.


Eventually, at the end of August I started college. It feels like it was a long time ago.

Even though I'm learning things like how Google Calendar is important and how to use a master slide in a PowerPoint, I still feel like it's being something that's matured me.

Friends & Church

A few years back my family and I moved church. When we came to the church we're at now, Harvest Glasgow, there was only 5 other people in the youth group. But now there's 20 or so. It's such a great thing to be friends with other people my age who want to grow in their relationship with Jesus Christ. It's so encouraging living life with each other and talking to one another when we're going through hard times.

I think that's a good review of how my 2019 went. I feel like this year I've matured as a person and I've come to trust Jesus more than I have before.

<![CDATA[Story of my accidental £1.7k bill from Google Cloud Platform]]>

As I'm writing this I've just got off a live chat with a Google Cloud Platform support agent who helped me escape this accidental bill. I can't say thank you enough for the agent that helped me out during this very stressful situation.

So a week or so ago I was using ReadCast and I ran a command to manually run all of ReadCast's subscriptions, there is only 15 or so of them. While I ran the command, I was watching Laravel Horizon, a tool I use to monitor my backend jobs.

I saw that the number of jobs that my server was processing was getting higher and higher and then I started getting Slack notifications telling me MySQL had 'gone away' and that my server had high memory usage.

Right enough, when I checked my server in Digital Ocean's dashboard, I saw that my server was using 100% of it's memory. I restarted the server a few times but it kept trying to reprocess all my jobs, not a good thing.

I restarted the server one more time and managed to get into the Redis command line and flush everything. Suddenly, everything was back to normal.

A few days later, I actually experienced exactly the same issue, where high numbers of jobs were being processed by my server so I knew how to kill it.

Fast forward to today. I was looking through my phone, in the Google Pay app, as you do and I looked at my 'Subscriptions' screen. However I looked at my Google Cloud Platform subscription and it came up with me having a £1,700 balance. I thought that any balance here would be particularly unexpected as a few months back, before I started using GCP I got some startup credit so I could use GCP for free for a few months and its been lasting me since then.

For context I use Google Cloud Platform for ReadCast to do the audio generation.

Anyway, I contacted an agent through Google Cloud Platform's support centre and they confirmed that I was due to be billed as I had run out of credit.

The first question that came into my head was - how on earth had I run out of credit and how on earth had I racked up that much money? But after talking to Peter from Branch, who's actually a customer of mine, he pointed out that it could have been caused from the high loads of jobs that were being processed a week or so ago.

I begged to the agent at Google and they said that they would file for an adjustment to my bill as long as I deleted my project and disabled my billing account and that I would not open an account with them in the future. So here's hoping I don't get charged when my bill for the month's due.

<![CDATA[How to Force Remove Service Worker from Chrome]]> So I recently created a service worker for one of my apps so I could turn it into a Progressive Web App.

Although, for some reason I was under the impression that I should cache the main page of my app. I later realised that it was a very bad idea.

I think I'm the only user of the site that came across this issue before I patched it but I found it quite a pain to unregister the service worker and bust it's cache so I could get the latest version of the page from the server.

I had to do this process on both my MacBook and on my Android phone, so here's both of those processes:

Chrome on the MacBook

The first thing to do is to open up Developer Tools and head to the 'Application' tab. In there it has its own little sidebar. Go to the 'Service worker' part of that. There it should give you the option of 'Unregistering' your service worker. Click on that link and also go and clear the whole site's cache because why not.

Chrome on the Android phone

The first thing to do is to go to the site, tap on the site's security padlock (or warning sign¬†‚ö†ÔłŹ) and tap on Site settings. Then tap on Clear & reset. Then just refresh the page and you should be good to go!

<![CDATA[Setup Statamic v2 on Laravel Forge]]> If you're looking for a version of this article for Statamic v3, you should check out Steven's article instead.

A big thanks to Ben Furfie who helped by looking over this article and suggested loads of improvements.

Every site needs to be hosted, and your Statamic site is no different. The recommended solution is a combination of Digital Ocean and Laravel Forge to handle your hosting and provisioning respectively.

This is the setup I use personally, but being around in the Statamic discord for long enough has prompted me that it's something not everyone knows how to do. So I thought I would write up something to help lighten the learning curve.

First things first, it's worth making sure that you know what each part of the puzzle does.

Laravel Forge

Forge is the tool that is designed to make the provisioning, deploying and managing your website easier.

Digital Ocean

Digital Ocean is the company that you rent your server from. You pay them for your own virtual private server which is often abbreviated as a VPS.

Now that we know what each thing is for, let's actually get started with the setup process.

Setting up Laravel Forge for Statamic

Next, you'll need to setup your own Laravel Forge account. You can do this by clicking here and creating one, that is if you don't already have one. After you've entered all your details, you will be prompted to connect with Github or one of the other version control providers. You'll also need to connect with a service provider, which in our case is Digital Ocean.

Forge will ask you for a Digital Ocean token, you can find this by:

  • logging into your Digital Ocean account

  • going into the API section

  • creating a token with¬†Read & Write¬†access.

Then you just need to copy the token it gives you and add it to Forge where it asks for it.

Configuring your first server

You will now be on the server screen. This is where you'll be able to create your first server. It'll ask you to choose some options.

Word of advice: unless you're going to be serving loads of users and you're going to be using loads of storage, then you don't need to go any higher than the minimum $5 Digital Ocean droplet. If you do need to go higher in the future, it's easy to change - all you need to do is change a small setting in the Digital Ocean control panel.

Once you're done with all of the above, Forge will create a server for you in your Digital Ocean account. It usually takes anywhere from five to ten minutes for Forge to provision a new server; so you might want to find a good YouTube video to watch in that time.

Your first Laravel Forge Statamic site

Once the server is ready, you'll be able to click on the name of the server and you'll be given the option to create your first site.

With this first site, it might be clever to start with a dummy domain or a low traffic site; something that you're not going to be panicking about if all goes weird.

Fill in the domain of the site you want to host. You can also add aliases here, like a www version.

The last version is a select. For Statamic sites, leave this as General PHP / Laravel.

You'll also want to change the web directory from /public to /. Of course, if you've setup Statamic to work above webroot or your planning on using a service like Envoyer, this doesn't apply, but as we're planning on keeping everything simple, just set it as /.

Selecting your Statamic Github repo

Next, you'll be asked if you want to set the site up from a repo, or as a fresh WordPress install. As this is a Statamic site, you're going to want to select the first option.

As with most git related services, you need to include both the repo author, and the repo name. So my github name is duncanmcclean, and my example repo is awesome-statamic.com. So the repo name will be duncanmcclean/awesome-statamic.com. That's what you put in that field.

Next, you'll be asked if you want to install any composer dependencies. Untick that box or you're going to have a bad time.

Once you've done that, click the button and Forge will do it's thing. These include things like configuring nginx and other server things you don't need to worry about right now. After Forge finishes loading, your site should be ready.

The only thing I'd recommend doing now is tweaking the default deploy script. In the settings screen, you'll see a box with some options starting with something like:

cd /home/forge/awesome-statamic.com git pull origin master echo "" | sudo -S service php7.3-fpm reload

I often add a couple of extra please commands to make it easier when I push a new version of my site live. My config often looks like this:

cd /home/forge/awesome-statamic.com git pull origin master echo "" | sudo -S service php7.3-fpm reload

php please clear:cache php please clear:stache php please clear:static

One thing you might want to look into is using Spock. Spock commits & pushes updates to entries, assets and settings when you make them in the Control Panel. This makes sure your Git repo always has the latest content. Ben Furfie has recorded a little video clip on how to get this setup.

Pointing your domain to Forge

Getting your domain to point to Forge is easily one of the hardest and most fiddly parts of dealing with hosting - largely because how you do this depends on who and where you bought your domain.

The key thing is you'll need to get the server's IP address from Forge and point an A record at it. Beyond the basics, how to point a domain to a server is a whole other topic and is outside the scope of this article (sorry...).

Getting into your server

There may be times when you need to access your server to look at log files or access some sort of system configuration. To do this, you'll need to be able to use SSH.

Unlike other virtual private servers (the droplet you pay Digital Ocean for), you can't just put in a username and password to SSH into a server. Instead you need a username and an SSH key.

If you haven't already created an SSH key for yourself - and you're on a Mac - you can do it by running these commands.

ssh-keychain -t rsa pbcopy < ~/.ssh/id_rsa.pub

I'm not sure on how you do this on Windows as I don't use Windows. But if I find out then I'll add instructions on it to this article.

The first command will create your key, while the second will copy the contents of the public version of that key to your clipboard. Once you've done that, you'll be able to go to Forge account, go to User Settings and then click the SSH Keys tab.

You'll see a box where you can paste in your key. Once you've done that, click save and Forge will now have the part of your SSH key it needs to authenticate your computer.

The last thing you need to do is click on the 'Add to Servers' button, next to the key. This will add that key to each individual server.

Now when you want to SSH into your server, you can type this (replacing with the IP of the server in your Forge account):

ssh forge@

A few quick things

  • If you want your site to be auto-deployed when you push to your Git repo, you need to enable¬†Quick Deploy¬†on the Site page in Forge.

  • I would recommend that you should install SSL on your site. Thankfully, Forge makes this super easy. Within your site options, click on the SSL link on the left hand menu in Forge. Then click on¬†Let's Encrypt¬†and then click on¬†Obtain Certificate. A fresh certificate should be created and installed for you within a minute.

<![CDATA[Getting Started with testing Laravel Apps]]> Recently I've started to write tests in my Laravel applications. Tests are useful because they allow you to make sure important parts of your application are working, things like registration or subscriptions. Things that could be easily missed and could cause uproar if they broke.

When I started building ReadCast, about a month ago I created it as a prototype and missed out on writing tests. Now that the application is stable and has a few users, I think it's about time to write some tests.

In a standard Laravel install, PHPUnit is already installed for you and so are a few example tests.

In this tutorial I'm going to go through:

  • Setting up your tests

  • Creating database seeds

  • Writing tests

Setting up your tests

In Laravel, your tests belong inside the tests directory. In there you have two directories. One for your feature tests and one for your unit tests. I should probably know the difference between this but I don't, so go elsewhere if you want to know the meaning.

Most applications will talk to a database when doing anything. When testing, there are two types of databases that people will likely use. They will either use a SQLite database or a MySQL database.

If you don't know, a SQLite database is pretty much a lightweight SQL database that's stored in a single file.

I tend to create a separate MySQL database for running my tests. Why? Because last time I tried to use SQLite I managed to break some dependency that every programming language needed which meant I had to rebuild my Mac, not a fun time. However, feel free to use whatever you like best.

If you like using MySQL like me, this is how I tend to configure it.

First, I create my testing database, usually called something like app_testing.

The next thing I do is go to my database.php configuration file and setup a new testing database connection. It looks like this:

The values rely on some environment variables so you'll need to add these env variables to your .env and .env.example files.

Now there's one more thing you need to do before being fully setup. You'll need to specific the type of database you wish to use when testing. We can do this in PHPUnit's configuration file, phpunit.xml.

In the bottom <php> part, make it look like below, where we add the DB_CONNECTION rule and specify the testing database connection we just created.

That should be us done with the setup.

Setting up factories

Factories allow you to you to generate database records. Laravel comes with a package called Faker which makes it easy to create factories with fake information. Things like names, emails, passwords, that sort of thing.

You can find your factories in the database/factories directory. If you need to create your own factory, you can do so with a simple artisan command: php artisan make:factory ArticleFactory

Laravel comes with a User Factory, so we'll just use that one. The user factory looks like this:

The information in the sample user factory is fine for me but you might want to change it, depending on what fields you have in models.

Writing tests

Laravel gives you a pretty basic test which visits a page and the test passes if the status of the page request is 200 (that means its OK).

Now that we know what tests look like, let's delete the example test and create our own. To create our example test, run this command:

In the test I'm going to write, I'm going to create a user, login to my application, submit my email and password then the test will pass if we get status code 200.

Just a heads up, when writing test, make sure to start the method with test so PHPUnit knows which methods are actually tests.

In our test, we need to specify that we want to use the RefreshDatabase trait. This will allow us to have a clean database for every test meaning results won't be messed up with those from other tests, You can add trait like this:

Now let's write the test. The first thing I said we would do in the test would be to create the user we want to login. We can use Factories to do this. In the code sample below, we're creating a user and storing it's information in the user variable.

Now we have created the user, we need to send a POST request to my login endpoint with my email and password.

The below code will find the input with the name of email and will fill it with the email of the user we created and will find the input with the name passwordand will fill it with the password of the user (by default, Laravel's user factory password is 'secret'). It will then find the button with the text that says 'Login' which submits the form. Then it checks to make sure we're redirected successfully to the /articles url.

You should now have something that looks a little like this:

If you've done everything correctly, you should be able to run your tests and see them all pass.

And that's it, we've got Laravel setup and we've written tests to make sure logins to our application are working.

<![CDATA[Update Customer in Stripe using Laravel Cashier]]> I've just had the situation where I needed to setup my Laravel app to update my customer's information in Stripe when they change it in my app.

The method I used to do this is actually undocumented on the official Laravel Cashier documentation, I found it while I was looking through the Cashier code.

In my Settings controller I used the following code to update the customer's name and email when they resubmit the settings form.

That code should work as long as the User model has the Billable trait.

<![CDATA[How I built a Web Scraper to create a Bin Collection API]]> A few months back I wrote some code that would go to my local council's website, search for a street name, open the link, get the bins for that week. When I got it working I was amazed at how easy it was. I was able to just take out content from another site and present it however I wanted.

I wrote this project with the end goal of turning it into Voice skills for Alexa and Google Assistant, which I will do, just in a few weeks when I have some spare time.

Above is the PHP function I wrote to serve the API request. I'm just going to walk you through the code.

So the first thing it does is sets up three arrays, the binArray, the timeArray and the locationArray. These will be used later to store data we capture during our scraping.

The next two variables hold the two URL parameters required for the request. So the API request URL would look like this. https://bin-collections.herokuapp.com/api/bin?street=Calderwood%20Road&area=camglen

We then get have four if statements. Each of the if statements check if the $area parameter equals to one of the areas. It then sets the base URL for the search results on the local council's website.

The next thing we do is to setup Goutte, which is a PHP web scraper library. I used a Laravel wrapper for Goutte in this project. We send Goutte to the base URL, the one we set in the last bit.

On the page we then go ahead and find the result links and we click on the first one we find and we grab the URL from that and store that in the $linkvariable.

Now we go to the street page we found in the search results.

The next part of the code, we find parts in the web page and take out the text of it and we store it in the arrays we created a minute ago.

So for example, we store the location in the locationArray and so on.

Then we go ahead and send a JSON response with the content of each of the arrays.

On the frontend of my bin collection site, I'm using a Vue component that makes HTTP requests to the API I built.

I'm pretty proud of the whole thing. It could probably be simplified quite a bit but at least it works.

<![CDATA[Using Vuex to store authentication in Local Storage]]> I'm currently Rebuilding BiblioSpot as a standalone Vue app which fetches data from our private API. At the start of the project, I was wondering, how I should go about handling Authentication? I tried out a few different solutions but this one seems to work, so I'm going to go along with it.

The first thing I did was obviously create a new Vue app so I did vue create app-name. I chose Vue router, Vuex and some other stuff that was there that looked like a good option. I also installed Axios for the HTTP request stuff. Yes I know Vue has their own package for this and there is a similar function built into Javascript, but who cares.

Now, the first thing that we should do is to write code to allow the user to actually login. I'm using Laravel Passport for API auth and it gives us the /oauth/token route which we can send a payload to and it will give us our access token. A simple bit of code like this should do the jiffy.

Once you've got your HTTP requests to work, you can create your Vuex store. This is a simplified version of my store.js file.

Basically what I've done in my Vuex store is set it up so that you can change and view the bearer/access token.

Now that we've got this in place, when you login your token should be set in your Vuex store and you should be directed to /. (you can change this route in the success part of the Axios request in the Login.vue component)

However, if you refresh the page, your bearer token is no longer in the Vuex store. This is where local storage comes in. It can store data that can be used even if you close the browser entirely.

The way I stored my state in local storage was by using the vuex-persistedstate npm package. You just need to install it and import it into your store, like so.

Now you should be able to login and refresh your page and your bearer token will still be in your store.

Now you've pretty much got everything working. However, if you need to check within views/components of your application if the user is logged in or out for that matter I've devised a little component that you can use that will detect such things and will redirect the user to the login page. (If you need to check that the user is logged out you can place the redirect at the other side of the if statement)

That's us done!

<![CDATA[CoderDojo Bridgeton Website]]>

For the last year and a half I have been building the CoderDojo Bridgeton website. A large portion of that time was spent creating a static prototype with HTML and CSS.

What is CoderDojo?

"The CoderDojo movement believes that an understanding of programming languages is increasingly important in the modern world, that it's both better and easier to learn these skills early, and that nobody should be denied the opportunity to do so. To that end, we've built a global network of free, volunteer-led, community-based programming clubs for young people. Anyone aged seven to seventeen can visit a Dojo where they can learn to code, build a website, create an app or a game, and explore technology in an informal, creative, and social environment." - About CoderDojo

Wireframes and Planning

Before we could get started with anything, We needed to plan out the website and create wireframes of the design. The below images are wireframes of the Homepage and the Blog Archive.


As I said at the start of this post, Prototyping took up most of the time in this project (over a year). The prototype was created using plain HTML and CSS. Later on I converted all of the CSS to SASS so it's easier to maintain.

I learnt a lot whilst I was creating this project thanks to my mentors.

The End Product

After the prototype, I built the site using Jekyll and I am hosting it with https://pages.github.com/. I also made use of Cloudflare because it gives us a free SSL certificate and a bunch of other benefits.

The website is live! Please check it out.