Laravel Tip: Refactoring to lookup tables

Laravel Tip: Refactoring to lookup tables

Sometimes you find yourself in a situation where you have too many if statements in a controller. On a recent project that we were working on, we had a case where we had to show three different views based on the model type.

Our initial code looked something like this:

<?php

public function create(Report $report, $type)
{
    if ($type == 1)
        return view('items.inventory.create', ['report' => $report]);
    elseif ($type == 2)
        return view('items.stock.create', ['report' => $report]);
    elseif ($type == 3)
  return view('items.special.create', ['report' => $report]);
}

This will work but if you have to copy this to other methods it will become a burden to refactor this in the future or if the complexity of the project grows.

The first issue that we are going to tackle is to introduce constants in our Item model. This makes it easier and more readable the three different types.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Item extends Model
{
    use HasFactory;

    const INVENTORY = 1;
    const STOCK = 2;
    const SPECIAL = 3;
    
}

Now that we have the constants in our model file we can go back to the controller and introduce a new method which handles the correct routing based on the item type.

Inside our controller we need a new protected function that will return the route based on the type, this function will only accept a $type. Then, inside the route of our create function in the controller we can replace all the if statements with a single line of code.

<?php

namespace App\Http\Controllers;

use App\Models\Report;
use App\Models\Item;
use Illuminate\Http\Request;

class ItemController extends Controller
{
  
  public function create(Report $report, $type)
  {
    return view($this->filterItems($type), ['report' => $report]);
  }
  
  
  protected function filterItems($type)
  {
    $items = [
      Item::INVENTORY => 'items.inventory.create',
      Item::STOCK => 'items.stock.create',
      Item::SPECIAL => 'items.special.create',
    ];
  
    return $items[$type];
  }
  
}

That's it! So, when we hit the create route with the current type, it will go through the function filterItems and based on the type (1, 2 or 3), it will redirect to that view.

You can use this technique whenever you are dealing with complex if/else statements and want to cleanup the code to be more readable.