Laravel 将现有用户系统转换为多类型

Laravel Converting Existing User System Multi-Type

我最近接手了一个现有用户系统(标准 Laravel 5.7 make:auth 设置)的项目。我已经确定的是,由于系统中的用户类型不同,用户 table 中有大量 MySQL 数据库列为空白或 NULL。出于本示例的目的,假设他们是 ADMINISTRATOR、PARENTS 和 CHILDREN。目前,在该应用程序中,除了名为 "type" 的列之外,没有明确区分用户是什么,该列对于儿童为 NULL,对于 ADMINISTRATOR 为 'admin',对于 PARENTS 为 'parent' .

我希望能够将所有这些列分开,以便与适当的用户类型相关联,同时保持相同的身份验证方法(这是我当前的思考过程,并非一成不变)。

根据我收集到的信息,我应该查看多态关系,其中用户模型与每个用户类型具有 "typeable" 变形关系。我还读到这需要在用户模型上有一个 "typeable_type" 和 "typeable_id" 列,其中 "type" 指的是模型 - 在这种情况下 "App\Models\Admin", "App\Models\Parent", "App\Model\Child".

对于其中的每一个,我都会做这样的事情:

class User extends Model
{

    public function typeable()
    {
        return $this->morphTo();
    }
}

class Admin extends Model
{
    public function user()
    {
        return $this->morphMany('App\User', 'typeable');
    }
}

class Parent extends Model
{
    public function user()
    {
        return $this->morphMany('App\User', 'typeable');
    }
}

class Child extends Model
{
    public function user()
    {
        return $this->morphMany('App\User', 'typeable');
    }
}

我的问题是 - 如何在 "type" classes 中创建可以使用父用户 ID 作为任何关系的外键/基础的关系?

例如,家长可能在数据库中有订阅,而管理员可能没有。订阅 table 通过 "user_id" 列引用当前用户。

传统上,用户只会通过正常关系引用订阅。

class User extends Model
{

    public function subscription()
    {
        return $this->hasOne(App\Models\Subscription::class);
    }
}

class User extends Model
{

    public function subscription()
    {
        return $this->hasMany(App\Models\User::class);
    }
}

我如何在该关系由 "Parent" class 拥有的体系结构中检索它。请记住,我在我继承的遗留应用程序中工作,所以目前一切都围绕着用户 & user_id.

长话短说,我希望能够根据用户类型提取不同类型的信息,但仍将每种用户类型视为传统意义上的 "User"。一个理想的系统应该是每个用户类型拥有的信息"flattened"进入标准用户使用。

我不认为使用关系是你真正想要的。

我的设置方式是创建一个 User class(扩展 Model),然后让三个模型扩展 User class(ParentChildAdmin)。

User 基础 class 中,您将添加适用于所有用户的所有代码。

然后我会在每个 ParentChildAdmin class 中添加一个 global scope(例如,我已经使用匿名全局作用域,但如果您愿意,也可以使用普通作用域):

class Admin extends Users
{
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope('user_type', function (Builder $builder) {
            $builder->where('type', '=', 'admin');
        });
    }
}

然后您可以将任何相关方法添加到三个 class 中的每一个中,例如,您的订阅关系如下所示:

class Admin extends User
{
    public function subscription()
    {
        return $this->hasOne(Subscription::class);
    }
}

class Subscription extends Model
{
    public function admins()
    {
        return $this->hasMany(Admin::class);
    }
}

然后您可能还需要使用 protected $table = 'users' 在三个 class 中的每一个中设置 table - 但我不确定这一点。

如果您当时只获得了 user class 并且想要将该用户转换为特定基础 class 之一,您可以将这样的功能添加到class:

class User extends Model
{
    public function getUserType() // Or whatever you would like the name to be
    {
        // NOTE: I've made this function load without having to do any database calls
        // but if you don't mind an additional call, it might be easier to just change
        // the switch to return Admin::find($this->id);

        $class = null;
        switch($this->type) {
            case "admin":
                $class = new Admin($this->attributesToArray());
                break;
            case "parent":
                $class = new Parent($this->attributesToArray());
                break;
            case "child":
                $class = new Child($this->attributesToArray());
                break;
        }

        if(is_null($class)) {
            // Throw error, return null, whatever you like
            return $class;
        }

        // Perform any additional initialisation here, like loading relationships
        // if required or adding anything that isn't an attribute

        return $class;
    }
}