Laravel 转换现有用户系统多类型
Posted
技术标签:
【中文标题】Laravel 转换现有用户系统多类型【英文标题】:Laravel Converting Existing User System Multi-Type 【发布时间】:2019-12-20 05:43:54 【问题描述】:我最近接手了一个拥有现有用户系统的项目(标准 Laravel 5.7 make:auth 设置)。我已经确定的是,由于系统中用户类型的不同,用户表中有大量的 mysql 数据库列是空白或 NULL。出于本示例的目的,假设他们是管理员、父母、儿童。但在当前的应用程序中,除了一个名为“type”的列之外,没有明确的区别用户是什么,对于孩子来说是 NULL,对于 ADMINISTRATOR 是 'admin',对于 PARENTS 是 'parent'。
我希望能够将所有这些列分开以与适当的用户类型相关联,同时保持相同的身份验证方法(这是我目前的思考过程,不是一成不变的)。
根据我收集到的信息,我应该研究一种多态关系,其中用户模型与每个用户类型都具有“可类型化”的变形关系。我还读到这需要在用户模型上有一个“typeable_type”和“typeable_id”列,其中“type”指的是模型——在这种情况下是“App\Models\Admin”、“App\Models\Parent”, “应用\模型\儿童”。
对于其中的每一个,我都会做这样的事情:
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');
我的问题是 - 我如何在“类型”类中创建可以使用父用户 ID 作为任何关系的外键/基础的关系?
例如,Parent 可能在数据库中有订阅,而 Admin 可能没有。订阅表通过“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);
如何在“父”类拥有该关系的架构中检索它。请记住,我正在使用我继承的旧版应用程序,因此目前所有内容都围绕用户和 user_id。
长话短说,我希望能够根据用户类型提取不同类型的信息,但仍将每种用户类型视为传统意义上的“用户”。一个理想的系统是每个用户类型拥有的信息被“扁平化”到标准用户中以供使用。
【问题讨论】:
【参考方案1】:我认为使用关系并不是你真正想要的。
我设置它的方式是创建一个User
类(扩展Model
),然后让三个模型扩展User
类(Parent
、Child
和Admin
)。
在User
基类中,您将添加适用于所有用户的所有代码。
然后,我将在 Parent
、Child
和 Admin
类中添加一个 global scope(例如,我使用了匿名全局范围,但如果您可以使用普通范围希望):
class Admin extends Users
protected static function boot()
parent::boot();
static::addGlobalScope('user_type', function (Builder $builder)
$builder->where('type', '=', 'admin');
);
然后您可以将任何相关方法添加到三个类中的每一个中,例如,您的订阅关系如下所示:
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'
在三个类中的每一个中设置表 - 但我不确定这一点。
如果当时只为您提供了一个 user
类,并希望将该用户转换为特定的基类之一,您可以向该类添加这样的函数:
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;
【讨论】:
感谢您在回复中花费的时间,这听起来是我应该考虑的合法途径,老实说,可以相对快速且零碎地迁移。形象地说,我只是想将用户扩展到不同的类型,这是一个切实的实现。理想情况下,我想听听是否有围绕这个概念的任何其他范例——特定于 Laravel——但总的来说,这是一个可靠的解决方案。有了这个,您是否只需拥有特定于用户类型的表,这些表只是通过 user_id 作为标准用户引用它们?一个admin_meta表,parent_meta表…… 如果您想了解每种数据存储方式的优缺点,请阅读本文***.com/questions/3579079/… @radiantstatic 我不相信有任何 Laravel 特定的范例,唯一特别是 Laravel 的是Gates and Policies。我个人会避开元表,因为它们可能会导致数据重复。如果你想要 Laravel 特定的东西,我会研究政策。通常(如果不是 Laravel)我建议实现一个role
表(id
,name
),一个 permissions
表(id
,permission
)和一个 role_permissions
表(@ 987654346@, permission_id
) 并使用角色检查权限是否存在。
@NickClark - 所以(暂时)我真的不太关心它的权限方面 - 尽管我理解它的重要性。举个例子,假设大多数人都可以做任何事情,但不改变不适用于他们的数据。在 Wouter Van Damme 的回应中——我认为“类表继承”是最适用的场景。显然它的语义称为元 - 但你仍然不同意这种模式吗?
@radiantstatic 这个问题并没有真正的错误解决方案,它只取决于您将来(或当前)是否要拥有可自定义的权限以及您希望权限存在的位置(在数据库或应用程序代码中)。正如我之前提到的,我看到使用元表的唯一真正问题是数据库中数据重复的可能性——但这将是一件非常小的事情,所以如果你觉得元表最舒服,那就使用它们以上是关于Laravel 转换现有用户系统多类型的主要内容,如果未能解决你的问题,请参考以下文章