我们在实际开发中 数据表间存在着多种关系:一对一,一对多等。下面举例说明:
一对一
一对一的关系很简单,好比方用户和社交账号,我们来生成社交账号表:
php artisan make:migration create_accounts_table --create=accounts
编辑迁移文件:
public function up() { Schema::create('accounts', function (Blueprint $table) { $table->increments('id'); $table->string('qq',20)->nullable(); $table->string('weibo',100)->nullable(); $table->string('wechat',100)->nullable(); $table->integer('user_id')->unsigned(); $table->timestamps(); }); }
执行migrate:
php artisan migrate
创建完表之后创建模型:
php artisan make:model Account
tinker中创建一条数据:
>>> $account->qq = '123456'; => "123456" >>> $account->wechat = 'some wechat account'; => "some wechat account" >>> $account->weibo = 'some weibo account'; => "some weibo account" >>> $account->user_id = 2; => 2 >>> $account->save();
User模型中新建方法:
public function account() { // hasOne 这个方法名很直接了 就是有一个一对一的关联 return $this->hasOne(Account::class); }
Account模型中新建方法:
public function user() { return $this->belongsTo(User::class); }
新建路由 测试在User模型中能不能取到account:
Auth::loginUsingID(2); Route::get('/test', function(){ return AppUser::findOrFail(2)->account; });
测试在Account模型中能不能取到User:
Route::get('/test', function(){ return AppAccount::findOrFail(1)->user; });
注意:我们并没有在调用belongsTo
的时候指定相应的外键信息,那么Eloquent模型底层是怎么判断User
与Account
的对应关系的呢?
默认情况下,Eloquent将调用belongsTo
的关联方法名user
作为关联关系$relation
的值,并将$relation.'_id'
作为默认外键名对应users
表的id
,如果表中没有相应列,又没有在定义关联关系的时候指定具体的外键,就会报错。
那么又该如何在定义关联关系的时候指定外键呢?
实际上在底层无论是hasOne
方法还是belongsTo
方法都可以接收额外参数,比如accounts
中关联users
的外键是user_id
,该外键对应users
表中的列是id
,那么我们可以这样调用belongsTo方法:
public function user() { return $this->belongsTo(User::class,'user_id','id'); }
此外,belongsTo
还接收一个额外参数$relation
,用于指定关联关系名称,其默认值为调用belongsTo
的方法名,我们写的方法名是user
。
一对多
一对多的情况就很常见了,比如一个用户可以有多篇文章,但一篇文章只可以属于一个用户,创建迁移文件:
make:migration create_posts_table --create=posts
编辑迁移文件:
public function up() { Schema::create('posts', function (Blueprint $table) { $table->increments('id'); $table->string('title'); $table->text('body'); $table->string('views'); $table->integer('user_id')->unsigned(); $table->timestamps(); }); }
php artisan migrate
创建模型:
php artisan make:model Post
自行生成数据吧 ^ ^,我就不粘代码了,直接进入主题。
在Post模型中创建方法:
class Post extends Model { public function user() { return $this->belongsTo(User::class); } }
测试代码:
Route::get('/', function () { return AppPost::findOrFail(2)->user; });
上面的测试代码是可以取出第二条文章的所属作者的。但是user这个方法显得不是很好,我们来改一下方法名:
class Post extends Model { public function author() { return $this->belongsTo(User::class,'user_id','id'); } }
如果修改了方法名,我们就得传入额外的两个参数了,不懂就看看一对一时的红底儿白字儿。
对应的测试代码:
Route::get('/', function () { return AppPost::findOrFail(2)->author; });
在User中添加方法:
public function posts() { return $this->hasMany(Post::class); }
对应的测试代码:
Route::get('/', function () { return AppUser::findOrFail(1)->posts; });
这样我们会取出全部文章,如果要取出部分文章需要这样:
Route::get('/', function () { return AppUser::findOrFail(1)->posts()->where('id','<',3)->get(); });
多对多
另一种常见的关联呢 就是多对多,比如说我之前做的ACL备忘,里面有一个角色表 它和用户表就是多对多的关系,一个用户可以有多个角色,一个角色也可以被多个用户引用,我们只需要添加一张中间表来把稍微复杂的多对多关系转化成两个一对多关系。
我们创建一个迁移文件:
php artisan make:migration create_role_user_table --create=roles
在这个迁移文件中创建两张表:
public function up() { Schema::create('roles', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); // 创建关联表 Schema::create('role_user', function (Blueprint $table) { $table->increments('id'); $table->integer('role_id')->unsigned(); $table->integer('user_id')->unsigned(); $table->timestamps(); }); }
然后自行生成数据吧 哈哈。
在User模型中添加方法:
public function roles() { return $this->belongsToMany(Role::class); }
如果中间表不是role_user
,那么需要将中间表作为第二个参数传入belongsToMany
方法,如果中间表中的字段不是user_id
和role_id
,这里我们姑且将其命名为$user_id
和$role_id
,那么需要将$user_id
作为第三个参数传入该方法,$role_id
作为第四个参数传入该方法,如果关联方法名不是roles
还可以将对应的关联方法名作为第五个参数传入该方法。
在关联表中添加数据我们可以手动添加,也可以通过方法添加:
public function giveRole(Role $role) { return $this->roles()->save($role); }
对应的测试代码:
$user = AppUser::find(1); $role = AppRole::find(1); dd($user->giveRole($role));
执行完上面的代码我们就可以看到关联表中的数据了。
下面来测试关联是否成功:
Route::get('/', function () { $user = AppUser::find(1); echo $user->name . '所拥有的角色如下:' . '<br />'; foreach ($user->roles as $role){ echo $role->name; echo '<br />'; } });
在Role模型中添加方法:
public function users() { return $this->belongsToMany(User::class); }
测试代码:
Route::get('/', function () { $role = AppRole::find(1); echo $role->name . '角色所被这些用户拥有:' . '<br />'; foreach ($role->users as $user){ echo $user->name; echo '<br />'; } });