Category

Uncategorized

migrating php Application

Migrating legacy PHP application to modern web technologies using Angular and Laravel

By | Uncategorized | No Comments

Background : Recently one of our clients approached us for the revamp of their existing web application. This was a SaaS solution and was highly customizable. It mainly consisted of 2 parts. 

Admin Site: This part of the application was used for customizing customer facing web application. It had the capability to add and change contents that the customer facing website was showing. Also has capability to run different campaigns and programs during specific time. All these contents and programs might vary for different companies. The Site Admin had capability to turn on and off some of these features.    

End user site: This part of the application was visible to end users of the application. Its contents varied for different companies. 

All this admin section and user facing web site were part of one single application and also were using one common database. The application was written in php 5.6 and didn’t had any separation of API and UI. It had some Ajax calls but they returned the complete HTML page instead of json. And all this was a single application with no clear logical separation. Initially the client just wanted to change the end user facing part of the application and provide users with some time to migrate to a new UI by giving them an option to choose between the old and new UI.

Development approach 

As we had to rebuild the end user facing part, we decided to use modern UI frameworks such as Angular. We also needed to rest APIs for Angular. Another advantage of having REST APIs was that in the future we could build Mobile Apps easily. One option of building REST APIs was to write wrapper around existing classes. Instead we decided to build an API layer using the latest PHP framework like Laravel. The Laravel framework has a lot of advantages like High Security MVC architecture, DB migrations etc,  but for our specific case where we were migrating part of the existing application, Unit testing was one of the important aspects. Laravel has built in support for unit testing of REST APIs. 

Steps to setup Dev environment.

At the time of developing this application, the latest version for Laravel was 7.x. Our development team members were using different Operating Systems. Some of them were using Windows, Some Linux and some Mac. So, we decided to use Vagrant and Virtual machine to setup the development environment. Laravel Homestead is an official, pre-packaged Vagrant box that provides you a wonderful development environment without requiring you to install PHP, a web server, and any other server software on your local machine. Vagrant boxes are completely disposable. If something goes wrong, you can destroy and re-create the box in minutes!   

Setup new Laravel Environment.

Support multiple sites.

Once we had the Homestead environment running, we also wanted to setup existing php site on same vagrant box. Homestead uses Nginx server.  We can run multiple sites (Laravel as well as non laravel sites) on a single Homestead environment. To add an additional site, add the site to your Homestead.yaml file:

sites:
- map: newsite.api
to: /home/vagrant/project1/public
- map: oldsite.web
to: /home/vagrant/project2/public

If Vagrant is not automatically managing your “hosts” file, you may need to add the new site to that file as well:

192.168.10.10 newsite.api
192.168.10.10 oldsite.web

Once the site has been added, run the vagrant reload provision command from your Homestead directory.

Supporting multiple versions of PHP

Existing website was written in php 5.6 where as we are developing APIs using latest version of PHP 7.4 We wanted to have existing site running on dev env so that we can integrate new Angular app with it.

Homestead 6 introduced support for multiple versions of PHP on the same virtual machine. You may specify which version of PHP to use for a given site within your Homestead.yaml file. The available PHP versions are: “5.6”, “7.0”, “7.1”, “7.2”, “7.3”, and “7.4” (the default):

sites:
- map: newsite.api
to: /home/vagrant/project1/public
- map: oldsite.web
to: /home/vagrant/project2/public
php: "5.6"

for API site we didn’t provide any specific php version. So by default it will use 7.4. But if you want you can specify the version.

Setup Unit testing Laravel API

In Laravel we can create a new project using laravel new command

laravel new api-project

Once project is created, on your root directory, update open phpunit.xml file. 

We need to add following two keys

<server name="DB_CONNECTION" value="sqlite"/>
<server name="DB_DATABASE" value=":memory:"/>

Updated phpunit.xml file will be something like this

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true">
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./app</directory>
</whitelist>
</filter>
<php>
<server name="APP_ENV" value="testing"/>
<server name="BCRYPT_ROUNDS" value="4"/>
<server name="CACHE_DRIVER" value="array"/>
<server name="DB_CONNECTION" value="sqlite"/>
<server name="DB_DATABASE" value=":memory:"/>
<server name="MAIL_MAILER" value="array"/>
<server name="QUEUE_CONNECTION" value="sync"/>
<server name="SESSION_DRIVER" value="array"/>
</php>
</phpunit>

When we execute a test case, Laravel provides an option by which it executes database migration each time using the RefreshDatabase option. If your tests use database and you want test run independently without affecting each other. You can use the RefreshDatabase option to run test with a clean database. By clean I mean a known state, fresh migrated or seeded, not necessarily an empty one.

If you are creating an application from scratch, we recommend you use sqlite database and :memory: for the database, this will execute test cases in memory and is fast way to run test cases. This setting is good if you are creating a completely fresh application in which you will be creating migrations and models from scratch. But in our case, we wanted to use existing database. So, we didn’t want to create database from scratch and won’t use this option. 

Laravel Model using Eloquent

Eloquent is ORM included with Laravel provides simple yet powerful implementation for working with the database. Each database table has a corresponding “Model” which is used to interact with that table. Models allow you to query for data in your tables, as well as insert new records into the table.

In normal flow we can create a Model first. When we create a model, we have the option to create migrations and controller along with the model. But in our case, we already had a database. So, we decided to create Models from a database.

Create Models from database: 

If you are using MySQL and Laravel 5.1 or above you can use Eloquent Model Generator  package.

Here are steps to create models from existing database.

  • Import package through composer with

     composer require krlove/eloquent-model-generator --dev

We want to use this package only on dev env where we want to generate model from db, so we should use –dev  option  

  • Add the service provider to your config/app.php file as follows

'providers' => [
// ...
Reliese\Coders\CodersServiceProvider::class,
];

  • Publish the config file with php artisan vendor:publish –tag=reliese-models
  • Make sure your database is correctly configured in config/database.php and .env files.
  • And finally issue the command: php artisan code:models

This will create all models for your database. If your database has relationships, this also creates those relationships using belongsTo and hasMany kind of relations. 

Querying records using Eloquent

Once these models are ready you can query and update these models with very simple Eloquent commands

For e.g. We had a model Members.                       $user = User::all();

This command will return all records from Users table                      $user = User::find(10);

Above command will find a User with specific Id                       $rahul = User::where('name', '=', 'Rahul')->first();

Above command will return a user matching a name

We can also user relationships to get information from other related tables. For e.g User belongs to a Department. We can get related Department information using With Keyword like$user = User::with(‘department’)->find();

We can also provide specific columns which we want to fetch in with function. Also we can multiple relationships in with function like. 

return User::with([
'department' => function($query) {
$query->select(['department_id','name']);
},
'business_unit' => function($query) {
$query->select([' business_unit_id','name']);
}
])->find($user_id);

Updating records

Updating records using Eloquent is as easy. To update a record in Eloquent, we need to first find the record you would like to update, change the attributes, and save. For example, to change the designation of Rahul to developer, first find the user and then execute the save method. $user = User::where('name', '=', 'Rahul')->first();
$user->designation = ‘developer’;
$user ->save();

The save method may also be used to update models that already exist in the database.

Similarly, to delete a record we can do find and delete 

$user = User::find(10);
$user->delete();

Creating Migration Files from Existing Database in Laravel

When you want to unit test some part of a code, having clean database allows us to create end to end test cases for a feature. You can use migrations to create in memory database, have seeders and execute end to end cases. For such cases you need migrations. But in case when you are migrating legacy php application you won’t have migration scripts available for the database. You can use Laravel Migrations Generator package to generate migration scripts from the existing database. 

 Install

The recommended way to install this is through composer:

composer require --dev "kitloong/laravel-migrations-generator"

Laravel will automatically register the service provider for you.

We want to use this package only on dev env where we want to generate model from db, so we should use –dev  option  

  • Add the service provider to your config/app.php file as follows

'providers' => [
// ...
Krlove\EloquentModelGenerator\Provider\GeneratorServiceProvider::class,
];

 To generate migration files from existing database, you need to have your database setup in Laravel’s Config.

Run php artisan migrate:generate to create migrations for all the tables.  

You can also specify the tables you wish to generate using php artisan migrate:generate table1,table2 

You can also ignore tables with –ignore=”table3,table4″

Conclusion

PHP is a widely used language for web applications. The world’s most popular CMS WordPress is written in PHP. There are many other open source applications like Magento. There are a lot of complex legacy applications which are also written in PHP. With the help of modern Frameworks like Laravel we can easily migrate them to new platforms where we can create REST APIs for the backend and use Angular applications and mobile Apps for frontend.