goredwards
goredwards

Reputation: 2494

Laravel - where to store common model functions & how to access them from controllers?

I have a number of functions that are used by most of my models
so it makes sense to store them centrally rather than repeating code.

For example, I have a function that finds a random row from whichever table I need:

function getRandomRow() {
    $rowCount = DB::connection('mydatabasename')
                ->table('my_table_name')
                ->selectRaw("FLOOR(RAND() * COUNT(*)) AS offset")
                ->first();

    $offset = $rowCount->offset;

    $randomRow = DB::connection('mydatabasename')
                    ->table('my_table_name')
                    ->select()
                    ->offset($offset)
                    ->limit(1)
                    ->first();

    return $randomRow;
}

I've looked at inheritance (extends) and traits - as well as the docs on polymorphic relations - but it's not clear which is the most efficient and how to access the table name (associated with the model being used) within the common function.

My questions:
- where do I put this function so that it can be used by all models ?
- how do I write the code so that the 'my_table_name' uses the correct model table ?

Upvotes: 1

Views: 5939

Answers (2)

goredwards
goredwards

Reputation: 2494

The simplest solution I found is to create a parent model / class - as noted in this answer to a different question - for the current example let's call it BaseModel.

For Laravel this is stored along with the other models, directly in the App directory

...in my case the parent model / class can be abstract as it doesn't need to have it's own table (also meaning the BaseModel functions can only be accessed via a child model):

namespace App;

use Illuminate\Database\Eloquent\Model;

abstract class BaseModel extends Model
{
    // ...put common model functions here
}

The trick to get this working (where a BaseModel function will need to access a child model's table) is to:
- have each child model / class extend the BaseModel class
- ensure that the $table is defined in all the child models - as noted here:

class MyFunkyModel extends BaseModel
{
    protected $table = 'funktown';

    // ...put child specific functions here

}

which means $this->table can be used in the BaseModel abstract parent class to reference the designated $table of the child model / class:

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;

abstract class BaseModel extends Model
{
    function getRandomRow() {
        $rowCount = DB::connection('mydatabasename')
                        ->table($this->table)
                        ->selectRaw("FLOOR(RAND() * COUNT(*)) AS offset")
                        ->first();

        $offset = $rowCount->offset;

        $profileRow = DB::connection('mydatabasename')
                        ->table($this->table)
                        ->select()
                        ->offset($offset)
                        ->limit(1)
                        ->first();

        return $profileRow;
    }
}

Now from a controller you can access any functions stored in the BaseModel simply via the child model(s):

namespace App\Http\Controllers;

use App\MyFunkyModel;
use App\MySeriousModel;
use App\MyHappyModel;

class MyController extends Controller
{
    public function getRandomName($type){
        switch ($type) {
            case 'funky':
                $modelFunky = new MyFunkyModel;
                $funkyRow = $modelFunky->getRandomRow();
                $randomName = $funkyRow->funky_name_column;
                break;
            case 'serious':
                $modelSerious = new MySeriousModel;
                $seriousRow = $modelSerious->getRandomRow();
                $randomName = $seriousRow->serious_name_column;
                break;
            case 'happy':
                $modelHappy = new MyHappyModel;
                $happyRow = $modelHappy->getRandomRow();
                $randomName = $happyRow->happy_name_column;
                break;
        return $randomName;
    }
}

Upvotes: 4

Gayan
Gayan

Reputation: 3704

You can use both inheritance and trait for this. But highly recommend to use Eloquent models rather than DB::table(). The reason is if somehow the table name get changed you have to change it everywhere. With Eloquent you only need to mention table name once in you model.

protected $table = 'new_table_name'

using inheritance

structure you BaseModel and class as follows.

class BaseModel extends Model {
    public static function getRandomRow()
    {
        $rowCount = self::selectRaw("FLOOR(RAND() * COUNT(*)) AS offset")
            ->first();

        $offset = $rowCount->offset;

        $randomRow = self::select()
            ->offset($offset)
            ->limit(1)
            ->first();

        return $randomRow;
    }
}

class YourModel extends BaseModel {
    // getRandomRow() available here
}

using trait

structure your trait and class as follows.

trait BaseTrait {
    public static function getRandomRow()
    {
        $rowCount = self::selectRaw("FLOOR(RAND() * COUNT(*)) AS offset")
            ->first();

        $offset = $rowCount->offset;

        $randomRow = self::select()
            ->offset($offset)
            ->limit(1)
            ->first();

        return $randomRow;
    }
}

class YourClass {
    use BaseTrait;
    // getRandomRow() available here
}

Either way you can access your method like

class yourController {
    public function youMethod()
    {
        $rows = YourModel::getRandomRow();
    }
}

Upvotes: 4

Related Questions