Three Good Things

Three Good Things: Building and deploying a project with Laravel

David Nash laravel, linux, mysql, php 2 Comments

What do developers do in their spare time? Code! Three Good Things is a little app that I started over the holidays, and have been working on in my spare time.

During the Christmas/New Year holiday period I read Flourish: A Visionary New Understanding of Happiness and Well-being, by Dr Martin Seligman. In it he describes an exercise called “What-Went-Well” (or “Three Blessings”). The idea is, every night you write three good things that happened in the last 24 hours, and why they happened.

I thought it would be a good idea for an app, so I set to work. It was a great learning experience, and I covered a lot of things I otherwise might not have.


Here’s the set of features that I wanted

  • Easy to use
  • Looks great on mobile, tablet and desktop
  • Simple – avoid feature bloat
  • No registration needed
  • Optionally remind people to enter their Three Good Things every day
  • Free (so I have to keep costs to a minimum)
  • Quickly show potential users that it works, via a chart
  • Quickly show how fast it is to do the exercise each day

I had a big list of other features, but I needed to cut them down to what was essential.


My first notes for Three Good Things

My first notes for Three Good Things

I found I had to do some UX research. So I made notes, and where I wasn’t sure how something should work, I simply listed all the possibilities and chose the best one.


Ideally this would be a mobile app. But that would mean creating an iPhone app and a separate Android app. Which is something I’ve never done before, and I didn’t want to have to spent 3 months learning each before I could even start building it.

The main reason was the reminders – I wanted to give users the option to get a notification at a time they specify. So I decided that in order to get this up and running, I could create a web app. I thought about a SMS notification system (which could be expensive) and I thought about web push notifications.

I’m also not a designer, and I wanted it to look great.


I’d used the Laravel PHP framework previously for a client on R8Check. I found Laravel easy to learn, and it was a joy to build with, so that was an easy decision.

It’s very easy to set up a user registration/login system, which I needed.

On the front-end Laravel comes with Bootstrap and Vue by default. I know Bootstrap well. Vue not so much, but it was easy to remove (I’ve since started another project using Vue, and so far it’s been great!).

For the design I found a free Bootstrap theme on Bootswatch, which provides Sass files. Perfect.


I built two functional prototypes before starting work on the version that I was happy with. I was still learning a lot about Laravel, and I was still nailing down the UI. Prototyping was an invaluable experience, and Laravel makes it very easy to experiment.

Unexpected complexity

I’d decided on a minimum viable product. For reminders, it would simply send emails, at a time the user chose. The app could be used by anyone, anywhere, so I needed to know the user’s time-zone. Time-zones are tricky if you want to on-board users as easily as possible. It’s a huge list.

I found the jsTimezoneDetect library could greatly simplify this, by automatically setting a select field to the user’s current timezone.

More complexity arose when I realised that the app was going to have to do time-zone conversions. The user enters three good things per day, but it’s the day where they are, not where the server is. And not just 24 hours, it needs to be a calendar day. The server runs on UTC time and then I wrote some functions using the Carbon API extension (which comes with Laravel).

I also use Carbon for sending out reminder emails. I set up Laravel Task Scheduling that runs every five minutes. It looks for all users who have not received a reminder email in the last 24 hours. The user’s reminder time is converted and then stored in UTC time, and the server is set to UTC time, so that wasn’t too difficult.

To use the site without creating an account, I had to come up with a system of “guest” users. When you visit the site, a user is created and stored in the database. If you login or register, any data you entered are re-assigned to your account. A simple Laravel task cleans up the abandoned guest user accounts.


Before I could start sending notification emails to users, I wanted to verify the email address. I set up a basic verification system that automatically emails the user a link to click when they register.

I also wanted the emails to look great. Again, Laravel has this covered, supporting markdown generated HTML email out of the box. I had an issue with inserting my logo into the header, but hopefully that bug will be fixed in a future version.

Avoiding spam filters

I spent some time configuring SPF and DKIM, and making sure the reminder emails wouldn’t be marked as spam. I created a web route so that it was easy for users to unsubscribe.

Setting up the Three Good Things web server

I had the domain name, I was happy with what I built, I just needed to get it online.

I spent a couple of days playing with Amazon’s AWS. As a cloud service it means it would be scalable and I’d only pay for the resources I used. The hurdle came with the domain name. Using Amazon’s Route 53 meant that I couldn’t have the app running at, it had to be on a subdomain like I had the perfect domain name but couldn’t use it.

That’s when I looked into DigitalOcean. For just $5 USD a month I could have my own server, and install whatever I wanted on it. I even had a voucher so the first two months were free.

Their website is easy to use, looks great, and the whole service is great.

Use this referral to get $10USD credit on DigitalOcean!

I installed Ubuntu, SSHed in, and felt totally at home. Their tutorials are great, and it didn’t take too long to get up and running: Apache, MySQL, and the IMAP/SMTP servers. Plus all the security stuff like a firewall, SSH and IP rate-limiter.

I generated a free SSL certificate using Let’s Encrypt and was shocked at how easy it was (install a package, run it… and you’re done).

Charting well-being

At the start of every day, Three Good Things asks you to enter your overall well-being. This is a 1-10 scale. I used Sass to come up with a nice way of gradating the button colours. Because I’m so pleased with the result, here’s the code I used to do it:

.buttons {
    //change the colour from green to blue
    @for $i from 0 to 10 {
        .form-group:nth-child(#{$i+1}) button {
            $color: mix($brand-success, $brand-primary, 100-$i*10%);
            background-color: $color;
            border-color: $color;

            &:hover {
                background-color: lighten($color, 5%);
                border-color: lighten($color, 5%);

The result is this:

Three Good Things well-being buttons

Three Good Things well-being buttons, with colours generated by a Sass for loop

If a user clicks “Worse than ever”, they’re offered a link to free online counselling.

I wanted to use that data and generate a graph. I did this using Chart.js, mainly because I love the little animation you get when it first displays the data.

Three Good Things user well-being chart

Three Good Things user well-being chart

It was tricky getting it set up so that it looked good on all devices. I originally had the days of the week (Monday – Sunday) listed on the labels, but they were very long and got cut off, so I had to compromise on that.

I also wanted to show a graph of all users’ average mood, to see if the site was working. This is displayed on the Three Good Things About page.

This took some pretty funky SQL beyond my expertise. Luckily a kind soul helped me out on StackOverflow. Here’s the SQL I use to generate the graph:

select w1.row day, avg(w1.rating) avg 
from (select w.created_at, w.rating, 
if(@lastUser=user_id,@row:= @row+1, @row:=1) as row,
@lastUser:=w.user_id as user_id 
from wellbeing_entries w, 
(select @row:=0, @lastUser:=0) vars order by w.user_id asc, w.created_at asc) w1
where w1.row <= 7 group by w1.row

Content filtering

After the second prototype, I realised that users would feel “alone”. They didn’t get to see the good things happening to others. So I have an All Good Things page and an option in user settings to anonymously show people’s three good things.

But that lead to another potential problem: people casually swearing, and worse, trolls. I’m not particularly into censorship but this needs to be a site for users of all ages and moral standards. I don’t want anyone to stop using the site because they’re offended.

The solution to this was the Laravel 5 & PHP Profanity Filter. Again I found it easy to install and configure. It will match against special characters so it’s difficult to cheat it, and I kept the number of filtered words to a minimum.


I wanted the experience to feel smooth and fast. Laravel 5.4 uses webpack to minify the CSS and JS, which was a good start. I started cutting out all the unused Bootstrap CSS, which was fairly easy using Sass. I did the same for the JS that Bootstrap uses. I got it down to a manageable size, about 109kB for the JS and about 90kB for the CSS. Not great for the initial site load, but fine for subsequent loads.

I then used Google PageSpeed Insights for more suggestions. A big one was server compression and caching. I installed the PageSpeed Apache module and mod_expires, and then used Laravel’s mix to generate files that would use cache busting so I could still update the site.

In the end I managed a PageSpeed score of 99 on mobile and 99 on desktop! Google deducts 1 point because the site uses Google Analytics. Thanks Google.

Three Good Things PageSpeed Insights

Three Good Things on Google PageSpeed Insights. I’m 1 point off the perfect 100 because I use Google Analytics. Thanks, Google.


Web push notifications

Everything was running well: Reasonably fast, easy to use, and the reminder emails were being sent on time. There was just one more thing that was bugging me: lack of mobile notifications.

I tried a few push notification tutorials. To use push notifications, you need web service workers. I found it very confusing. Then I found a service that was free and easy to use: OneSignal.

I got the basics of OneSignal working very quickly, but I wanted to customise it so that it was an option on the user’s settings page. I’d look at their extensive documentation but just couldn’t work out where to start. They were extremely helpful in pointing me in the right direction, and suggesting the best way to do what I wanted.

With all the customisation it was still a bit of work, and I think if I’d just spent the time working on my own implementation of Web Service Workers and Web Push Notifications I could have avoided a third-party solution. But this was a great learning experience, testing all the combinations of subscribed/unsuscribed, allowed/blocked by browser.

The biggest drawback is that while it works on Chrome desktop and mobile, it doesn’t work on iPhone or iPad. Apple haven’t implemented Web Service Workers for iOS Safari.

To send the notifications I used Guzzle to talk to the OneSignal API.


The site is running well. I’m pretty proud of it. Ideally it would still be a native Android and iPhone app. I’m keeping my eye on Jasonette, which seems to be a pretty good way to avoid writing two additional apps. And I know JSON.

People who use it have told me doing the exercise really does work. I know personally it helps my resilience towards the little adversities we all come across.

I know this has been a massive post, but this really only scratches the surface of what I’ve learnt. At this point I won’t even mind too much if there were no users, the experience has been worth it!

I’ve always thought of myself as a full-stack developer, and now I feel confident saying it out loud.

So… what are your Three Good Things for today?

Comments 2

  1. Pingback: Laravel and Vue.js: creating the Mark My Words web app - Part 1 - David Nash

Leave a Reply