From CodeIgniter to Laravel Part 2 - Where to start?

Sign up for the „From CodeIgniter To Laravel“ Newsletter Learn more

From CodeIgniter to Laravel Part 2 - Where to start?

Where to start?

Where does one start migrating a humongous legacy application from one framework to another? And is it legacy if it gets updated regularly?

On Twitter, Jonty Behr recommended doing this. And it seemed like a feasible way to proceed, so I gave it a spin. Getting CodeIgniter to run inside a Laravel application only takes a few steps:

  1. Install Laravel
  2. Move the entire CodeIgniter codebase to a folder inside the Laravel application
  3. Create a catch-all Controller that forwards requests to that folder
  4. Work from here

Well, sounds easy enough, but doing so opened another can of worms:

  • Accessing assets (CSS, JS) did not work out of the box; migrating to Laravel Mix would solve that
  • Since my CodeIgniter application doesn't use CSRF token, I needed to disable the VerifyCsrfToken middleware
  • The application has its own session management, not making things easier

I gave it a shot but concluded that it would be less work starting from a kind of a green field with some constraints (e.g. database tables) would be less painful.

Starting with an empty Git repository, my fellow developer - we'll call her Alice - and I started fleshing out issues to get a bit of a structure that we could build on.

Create a fresh Laravel app

Starting from scratch, let's whip up a brandnew laravel application and add the missing TALL stack components TailwindCSS, Alpine.js and Laravel Livewire:

$ laravel new trainers-app
$ cd trainers-app
$ composer require livewire/livewire laravel-frontend-presets/tall
$ php artisan ui tall --auth
$ npm install
$ npm run dev

These steps will install Laravel and the TALL stack preset with authentication enabled.

As I've mentioned before, we've fleshed out many issues, and here is the first.

Issue #1 - Create Eloquent models

When I started creating the database back in the day, I made some mistakes, first and foremost not naming the tables plural, so the table for the trainers is called trainer and not trainers, and the foreign keys look like id_trainer and not trainer_id. Also, pivot tables are sometimes not named alphabetically, e.g. trainer_articletype and not articletype_trainer. However, Laravel is savvy about its models and corresponding table names and keys. Luckily, one can customize the eloquent models and relationships to use different columns and table names.


class Trainer extends Authenticatable
{
	...

    protected $table = 'trainer';

    ...


    public function articleTypes(): BelongsToMany
    {
        return $this->belongsToMany(
        	ArticleType::class,
        	'trainer_articletype',
        	'id_trainer',
        	'id_articletype'ha
        )->withPivot([
        	'is_main',
        	'is_minor',
        ]);
    }

}

How many models did we migrate, you ask? Twenty-five, to be exact. Of course, that's not all models in the existing codebase, but those we need to make the trainer application work. Those models come with migrations and factories, too, so we can whip up a working development database in no time.

Let's have a look at the trainer factory:

class TrainerFactory extends Factory
{
    public function definition(): array
    {
        return [
            'username' => $this->faker->userName(),
            'lastname' => $this->faker->lastName(),
            'firstname' => $this->faker->firstName(),
            ...
        ];
    }

    ...
}

And here is the migration for the trainer table (still using the 'wrong' table name):

class CreateTrainersTable extends Migration
{
    public function up()
    {
        Schema::create('trainer', function (Blueprint $table) {
            $table->id();
            $table->string('email')->unique();
            $table->string('username')->unique();
            $table->string('lastname')->nullable();
            $table->string('firstname')->nullable();
            ...
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('trainer');
    }
}

Issue #2 - Making Laravel authentication work with our custom user model

We are not using the Laravel provided user model and table; we have our own trainer model. We're using password resets and the eloquent authentication provider, though. The CodeIgniter application has three different user types:

  • customers,
  • trainers and
  • administrators

in three different tables. And I would like to keep the separation this way. The main reason for doing so is that those user models have nothing in common except username and password. Administrators have just those two properties; customers have a delivery address, birth date, and various schools attached to them. On the other hand, Trainers have a social security number, article types, and a bunch of other data linked to them.