LordF
LordF

Reputation: 439

Laravel Eloquent - Model extends other model

I have a question about extending my own Models eloquent.

In the project I am currently working on is table called modules and it contains list of project modules, number of elements of that module, add date etc.

For example: id = 1; name = 'users'; count = 120; date_add = '2007-05-05';

and this entity called users corresponds to model User (Table - users) so that "count" it's number of Users

and to update count we use script running every day (I know that it's not good way but... u know).

In that script is loop and inside that loop a lot of if statement (1 per module) and inside the if a single query with count. According to example it's similar to:

foreach($modules as $module) {
    if($module['name'] == 'users') {
        $count = old_and_bad_method_to_count('users', "state = 'on'");
    }
}

function old_and_bad_method_to_count($table, $sql_cond) {}

So its look terrible.

I need to refactor that code a little bit, because it's use a dangerous function instead of Query/Builder or Eloquent/Model and looks bad.

I came up with an idea that I will use a Models and create Interface ElementsCountable and all models that do not have an interface will use the Model::all()->count(), and those with an interface will use the interface method:

foreach ($modules as $module) {
    $className = $module->getModelName();

    if($className) {
        $modelInterfaces = class_implements($className);
        if(isset($modelInterfaces[ElementsCountable::class])) {
            /** @var ElementsCountable $className */
            $count = $className::countModuleElements();
        } else {
            /** @var Model $className */
            $count = $className::all()->count();
        }
    }
}

in method getModelName() i use a const map array (table -> model) which I created, because a lot of models have custom table name.

But then I realize that will be a good way, but there is a few records in Modules that use the same table, for example users_off which use the same table as users, but use other condition - state = 'off'

So it complicated things a little bit, and there is a right question: There is a good way to extends User and add scope with condition on boot?

class UserOff extends User
{
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope(function (Builder $builder) {
            $builder->where('state', '=', 'off');
        });
    }
}

Because I have some concerns if this is a good solution. Because all method of that class NEED always that scope and how to prevent from method withoutGlobalScope() and what about other complications?

Upvotes: 1

Views: 294

Answers (1)

mikenewbuild
mikenewbuild

Reputation: 148

I think it's a good solution to create the UserOff model with the additional global scope for this purpose.

I also think the solution I would want to implement would allow me to do something like


$count = $modules->sum(function ($module) {
    $className = $module->getModelName();

    return $className::modulesCount();
}

I would create an interface ModulesCountable that mandates a modulesCount() method on each of the models. The modulesCount() method would return either the default count or whatever current implementation you have in countModuleElements().

If there are a lot of models I would probably use a trait DefaultModulesCount for the default count, and maybe the custom version too eg. ElementsModuleCount if that is consistent.

Upvotes: 1

Related Questions