Rewriting a legacy PHP application to Laravel doesn't have to be an all or nothing process, in this article, I go through how you can rewrite your app into feature classes in Laravel step by step while still using code from your old app.
How to rewrite a legacy PHP application to Laravel.
I’ve done a couple of rewrites of existing systems in my career. The largest one so far was a rewrite of a big blackbox e-commerce system, where I was the lead engineer of a team of 3-4 engineers which took 1 year and 3 months in total to complete.
My experience is that if you own the code that you’ll be rewriting, it will make it easier to start small and iterate quick. Getting results quickly and constantly is going to keep the motivation level up and increase the chance of a successfully completed rewrite. A far worse scenario would be if you had to do a full replacement and rewrite the whole old application before switching over to the new one. These kind of rewrites demand a lot more planning and resource allocation and are harder to complete.
One approach that I think works very well with this type of iterative, step by step rewrites is Feature Driven Development (FDD). If you haven’t heard about it, take a quick look at an article I wrote a couple of months ago here.
When starting off a rewrite, instead of spending time defining domain models and it’s boundaries in your business logic, it’s better to think simple and start rewriting the actual code as early as possible and in as small chunks as possible to get results quick.
I think this is where FDD really shines. FDD is based on features and therefore makes it easy to isolate tasks without them having dependencies on each other. Since tasks are isolated and not blocking each other it also makes it easier to split up the work between different engineers to proceed with the rewrite in parallel.
The legacy app
So you have your old application that hasn’t got the attention it deserves and it has been neglected for too long when you or your company finally decides that it’s time to do something about it. When I’m talking about a legacy application in this article, I’m referring to how we used to write PHP applications back in the day. That is, with inline PHP inside HTML markup, without any separation of logic whatsoever. What you see in your single PHP file is what you’ll get when you access it in a web browser.
Rewriting a legacy application into more structured code is a huge gain itself. But probably the bigger merit is that you can leverage the power of modern frameworks like Laravel, where advanced features such as queues, caches, websockets, object-relational mapping, templates and more, are just one line of code away. Thanks to open-source code projects, we don’t need to write loads of boilerplate code anymore, hurray! 🎉
Checking for dependency conflicts
I’m using Laravel in this rewrite so the first step is to make sure that we can use it in our application. Start with checking the system requirements for Laravel here. If you’re using composer in your application already we need to check that your dependencies aren’t too old and satisfy the requirements that Laravel has. Run the following command to check if you can install Laravel in your application with your current dependencies:
If this command finished without errors, you’re all set and good to go.
Install Laravel in a subfolder
Now, we’re going to work with Laravel installed in a subfolder in your current application folder root. Run this command to install Laravel in a subfolder called, laravel.
Sample legacy app
For the purpose of this demo, I’ve created a simple product catalog application.
The code is all contained in one PHP file and looks like this.
Identifying features in your code
Now, to start the rewriting of your code you need to identify features in your code. In the example application above we only have two, one is listing of products and one is updating the “display in shop” attribute.
To keep things simple, let’s create a folder called “Features” inside our ./laravel/app folder, you could put these feature classes anywhere you want inside the laravel folder, I’m putting them here because this folder is already set to be auto-loaded in composer by Laravel.
Here are the features encapsulated into feature classes.
Testing your feature classes
The good thing about small feature classes is that they’re really easy to test. Before rewriting them to use Laravel code, write a quick test to make sure that the code works as expected. While this is an optional step, I strongly encourage writing some test cases, it will save you many headaches further down the road and also give you confidence that the code works as excepted.
Bootstrapping Laravel inside your legacy app
Now that we’ve moved our features into feature classes inside the laravel subfolder we need to bootstrap the Laravel framework to start using them. However, we can’t just bootstrap the framework like we normally would through the router since we’re not going to be using the Laravel routing system just yet.
Let’s create a file called bootstrap_laravel.php in the root of our legacy application with the following contents.
Now, let’s tweak the original PHP file to bootstrap Laravel and use these feature classes.
Rewriting your features to Laravel code
After moving your code into feature classes in the laravel subfolder and bootstrapping Laravel the application will work just like before. But we don’t want to stop there, the whole point of this rewrite is to enable you to use all the powerful features in the Laravel framework. Let’s remove the MySQLi logic and create an Eloquent model in a new folder called Models in the ./laravel/app folder.
The GetAllProducts feature class can now be slimmed down to just a one-liner replacing all that raw database fetching logic we had before.
Finishing off one page
Now that we’ve rewritten all our features, let’s take the next and final step and move the last piece into Laravel, the template. We’ll need to create a route, controller and a blade template file for this.
First, start by defining new routes in your ./laravel/routes/web.php file. Since we now have a powerful router at our hands, we do not want to handle update requests and products list requests in the same file anymore, so let’s define two routes, one GET and one PUT for updating.
Next, create your controller, add a ProductsListController.php file into your ./laravel/app/Http/Controllers folder with the following content.
As you see, we bind all the products to the products_list view so that we can use them in our Laravel Blade template. Let’s add a products_list.blade.php file in ./laravel/resources/views with the following content.
Now that we do not have anything left of our old application logic in our old PHP file, it is now possible to use Laravel’s own routing and we don’t need the custom ./boostrap_laravel.php file anymore. We can now bootstrap Laravel by simply doing a require on the ./laravel/public/index.php file, like so.
Now that we’ve completely rewritten one file in our legacy application and that we’re using the Laravel router, one further step that you could take is to setup a rewrite from the old file products_list.php to just /product_list, that way we can be completely independent on old logic, making the transition to Laravel smooth.
I hope that this was an interesting read and that it gave you some new ideas on how to proceed with a rewrite of your legacy app. I would love your input and thoughts, please let me know in the comment box down below.
Until next time, have a good one!
For a complete diff of the files that were tweaked, checkout the link below: