On migrating my blog from WordPress to a Laravel application
Regular visitors will have noticed that last week this blog got a new coat of paint. This new layout isn't just a new WordPress theme. Things have changed on the backend as well. Previously my blog was powered by WordPress. I've migrated it to a custom built Laravel app. That app is open sourced. You can find the actual code that's being deployed on the server in this repo on GitHub.
In this blogpost I'd like to explain why and how I did this migration.
Loving and ditching WordPress
When I started blogging, nearly three years ago, I just wanted to have an easy way to share interesting links to other people's blogs. WordPress was perfect for that. It's easily installed. It comes with great features out of the box, such as an excellent admin interface, RSS feeds, a good search, ... There are also great plugins to add things like Analytics tracking, page caching, seo optimization, ... Because at my company I didn't do any styling I never bothered to pick up learning css, so it was also quite handy that WordPress has thousands of themes available.
For the first year in WordPress land, life was good. Here's how the blog looked back then:
Meanwhile I started to work in the open source space and together with my team created some packages. Instead of only sharing links on my blog to other people's content I started to write introductory posts to those packages. Because the blog started to attract more visitors I decided to put a little effort to make the blog look better. I did this by just installing an new theme (which was tweaked a little by my colleague Willem).
I wanted to customize the placing of all the content a bit more, but as a non-WordPress developer messing with the WordPress codebase can be quite daunting. I'm used to work with modern PHP code. I did some dirty hacks in some of the php files. That didn't feel quite good, but it worked. As a user I love WordPress, but as a developer I kinda hate it.
For a time I was happy again with the looks of the blog. Here's a screenshot how it looked like from around October 2016 up until to a few days ago.
In the summer of this year ago Adam Wathan announced that, together with some friends, he was working on a new open source css framework called Tailwind. Like mentioned above, I never took the time to learn css and I considered it my Achilles heel. Tailwind could be an interesting entry point in the world of css for me. I'm always more motivated to learn something if I can use new knowledge on a concrete project. That's why I decided to rebuild my blog with Tailwind, Laravel and a couple of our own packages.
Without any prior css knowledge I worked through the excellent Tailwind docs. I read this blogpost by Adam and Tailwind co-creator Steve Schoger. This video on rebuilding Laravel.io was very helpful too. I experimenting with Tailwind a bit. I didn't have a specific design in mind when starting with the project. The final design that you see now is an accidental result of my learning.
Behind the scenes
Like mentioned above my blog is now a Laravel application. You can view the entire source code in this repo on GitHub. Let's highlight a few interesting bits.
Searching blogposts
Searching blogposts is powered by Algolia, a blazing fast search service. All blogposts are transfered from the database to Algolia using Laravel Scout. With Scout installed you get an Artisan command to perform the initial transfer of a database table to Algolia. Scout also provides a Searchable
trait that, applied to model, will ensure that new records and updates to existing ones will be sent to the Algolia index as well.
Now that you know how posts are transfered to index, let's take a look at how those posts are retrieved. If you try searching blogposts you'll hopefully notice that the results appear nearly instantly. The search requests do not hit my own server. This whole search operation happens client side. This Vue component is responsible for rendering the search field and results. It uses the Algolia's JavaScript API to perform search requests.
I should mention that Algolia is not a free service but, unless you really expect a lot of search requests, their free tier is sufficient to use on a personal blog.
Importing WordPress posts
Of course I didn't want to lose all old content, so had to come up of a way to transfer WordPress posts to my the new Laravel app. Fortunately this is not so difficult. Here is the artisan command that imports posts and tags from a WordPress database. To handle tags I used our home grown laravel-tags package. That package makes it easy to associate tags with an Eloquent model.
Handling changed urls
When using WordPress the publication year and month of a post is used in the url eg: https://freek.dev/2017/06/building-realtime-dashboard-powered-laravel-vue-2017-edition/
. For my new blog I wanted to lose that time component and use shorter links as https://freek.dev/building-realtime-dashboard-powered-laravel-vue-2017-edition
. The old WP urls are in the Google index. If left unhandled all visitors coming to my blog via a Google search would see a 404 page.
Our laravel-missing-page-redirector package was built to solve this problem. The import command also populates a redirect
table. The redirector package has support for creating your own redirector to specifiy a source for the redirects (such as a database table). Here is the redirector that reads the redirects
table.
With all this in place all the old urls of my blog are redirected to the new pages. Try it out by visiting https://freek.dev/2017/06/building-realtime-dashboard-powered-laravel-vue-2017-edition
Providing an RSS feed
I try to keep up to date with what's happening in my sector by reading a lot of blogs. Visiting each blog separately would take a lot of time, that's why I prefer subscribing to RSS feeds and read all content in my prefered RSS reader.
Of course the redesigned murze.be should have an RSS feed. WordPress comes with support for RSS feeds out of the box. In Laravel land you can use another homegrown package: laravel-feed. That package makes it very easy add a feed or multiple feeds to your blog. Here is the function that transforms a post to a feed item.
The feeds themselves are found on https://freek.dev/feed (contains all posts) and https://freek.dev/feed/php (contains only posts that are tagged php
).
Crossposting content
Whenever I published a new post on my WordPress blog a tweet was sent and the content was cross posted to Medium. This was all handled by two plugins. Of course I wanted to do keep that behaviour. Fortunately both the Twitter and Medium API's are pretty easy to use, so I coded up a solution myself without relying on any packages.
Here is the code in the Post
model that dispatches the SendTweet
and the PostOnMedium
jobs.
On my old WP blog publishing a post always took a couple of seconds because communicating with Twitter and Medium takes a while. On my new Laravel blog publishing something is very fast because those SendTweet
and PostOnMedium
posts are queued. In fact all aforementioned syncing with Algolia is queued as well. Horizon is used to handle and monitor all queued jobs.
Miscellaneous
Writing Markdown
To write posts I've build a basic admin interface. I like to write Markdown instead of plain html. I've tried a couple of JS powered markdown editors and landed on using SimpleMDE.
On the front site that Markdown content must be rendered as html. I've added this method to the Post
model.
public function getTextAttribute($original)
{
return (new Parsedown())->text($original);
}
This will make sure that whenever $post->text
is used in a view the content of a post is converted from Markdown to html.
Improving performance
The Laravel app is already pretty fast out of the box, but we can do better. In the past there were a few of my posts that ended up fairly high on Hacker News. When that happens there are a lot of concurrent readers and the blog should be able to handle it. In WordPress I've used the WP Super Cache plugin. This will save a rendered page as a file on disk. The next time the same page is requested again it will just serve the saved content from that file instead of building the page up again from scratch.
In Laravel the exact same thing can be achieved by using yet another one of our packages: laravel-responsecache. If you want to know more about that package read the introductory blogpost about it.
You can see the package working if you inspect the response headers of this blog. Notice the laravel-responsecache
header.
Sending newsletters
Every two weeks I send out a digest of all new content on my blog to subscribers of my newsletter. You can subscribe here. A few months ago, to decrease expenses, I switched ditched MailChimp in favour of Sendy. You'll find more info on swichting to Sendy in this blogpost by Mattias Geniar.
Previously I had to manually collect all the titles, links and excerpts to include in the newsletter. For every edition of my newsletter that took about 30 to 45 minutes. On my new blog I've added some code to automatically fetch all the content and build up the html of the newsletter.
Deploying a new version
I'm a big fan of Envoy, a tool to easily run commands on remote servers. My blog repo contains an envoy script that can perform a Capistrano like deployment with near zero downtime.
In closing
I hope that you've enjoyed this little tour of the codebase of my blog. Feel free to fork the code and use it for your own blog. Keep in mind that the code in the repo isn't a full fledged CMS but just a simple app that's tailored to my needs.
I had a lot of fun learning Tailwind. Without any prior knowledge of css I managed to quickly build a design where I'm happy with. There was also some frustration involved when things didn't work as expected. But I blame my own lack of css skills for that. Luckily my team and some awesome people in the community helped me out a bit, when I was stuck.
If you're into Laravel be sure to check out the repo containing the source code: https://github.com/spatie/murze.be
thanks so much. this is a very helpful blog site I have ever visited
hello, I'm from rtppragmaticplay.top, nice to meet you in advance. The content you write is really useful and informative. The division of paragraphs and the neat wording make it easy to read and understand by everyone. I am a game content writer. Visit me by sending me an email at Email = rtppragmatic.top@gmail.com