资讯专栏INFORMATION COLUMN

Laravel & Lumen之Eloquent ORM使用速查-进阶部分

Chaz / 2415人阅读

摘要:关联关系查询在中,所有的关系都是使用函数定义的,可以在不执行关联查询的情况下获取关联的实例。

关联关系 One To One

假设User模型关联了Phone模型,要定义这样一个关联,需要在User模型中定义一个phone方法,该方法返回一个hasOne方法定义的关联

hasOne("AppPhone");
    }
}

hasOne方法的第一个参数为要关联的模型,定义好之后,可以使用下列语法查询到关联属性了

$phone = User::find(1)->phone;

Eloquent会假定关联的外键是基于模型名称的,因此Phone模型会自动使用user_id字段作为外键,可以使用第二个参数和第三个参数覆盖

return $this->hasOne("AppPhone", "foreign_key");
return $this->hasOne("AppPhone", "foreign_key", "local_key");
定义反向关系

定义上述的模型之后,就可以使用User模型获取Phone模型了,当然也可以通过Phone模型获取所属的User了,这就用到了belongsTo方法了

belongsTo("AppUser");
        // return $this->belongsTo("AppUser", "foreign_key");
        // return $this->belongsTo("AppUser", "foreign_key", "other_key");

    }
}
One To Many

假设有一个帖子,它有很多关联的评论信息,这种情况下应该使用一对多的关联,使用hasMany方法

hasMany("AppComment");
    }
}

查询操作

$comments = AppPost::find(1)->comments;
foreach ($comments as $comment) {
    //
}

$comments = AppPost::find(1)->comments()->where("title", "foo")->first();
定义反向关联

反向关联也是使用belongsTo方法,参考One To One部分。

$comment = AppComment::find(1);
echo $comment->post->title;
Many To Many

多对多关联因为多了一个中间表,实现起来比hasOnehasMany复杂一些。

考虑这样一个场景,用户可以属于多个角色,一个角色也可以属于多个用户。这就引入了三个表: users, roles, role_user。其中role_user表为关联表,包含两个字段user_idrole_id

多对多关联需要使用belongsToMany方法

belongsToMany("AppRole", "role_user");
        // 指定关联表,关联字段
        // return $this->belongsToMany("AppRole", "role_user", "user_id", "role_id");

        return $this->belongsToMany("AppRole");
    }
}

上述定义了一个用户属于多个角色,一旦该关系确立,就可以查询了

$user = AppUser::find(1);
foreach ($user->roles as $role) {
    //
}

$roles = AppUser::find(1)->roles()->orderBy("name")->get();
反向关联关系

反向关系与正向关系实现一样

belongsToMany("AppUser");
    }
}
检索中间表的列值

对多对多关系来说,引入了一个中间表,因此需要有方法能够查询到中间表的列值,比如关系确立的时间等,使用pivot属性查询中间表

$user = AppUser::find(1);

foreach ($user->roles as $role) {
    echo $role->pivot->created_at;
}

上述代码访问了中间表的created_at字段。

注意的是,默认情况下之后模型的键可以通过pivot对象进行访问,如果中间表包含了额外的属性,在指定关联关系的时候,需要使用withPivot方法明确的指定列名

return $this->belongsToMany("AppRole")->withPivot("column1", "column2");

如果希望中间表自动维护created_atupdated_at字段的话,需要使用withTimestamps()

return $this->belongsToMany("AppRole")->withTimestamps();
Has Many Through

这种关系比较强大,假设这样一个场景:Country模型下包含了多个User模型,而每个User模型又包含了多个Post模型,也就是说一个国家有很多用户,而这些用户都有很多帖子,我们希望查询某个国家的所有帖子,怎么实现呢,这就用到了Has Many Through关系

countries
    id - integer
    name - string

users
    id - integer
    country_id - integer
    name - string

posts
    id - integer
    user_id - integer
    title - string

可以看到,posts表中并不直接包含country_id,但是它通过users表与countries表建立了关系

使用Has Many Through关系

namespace App;

use IlluminateDatabaseEloquentModel;

class Country extends Model
{

    public function posts()
    {
        // return $this->hasManyThrough("AppPost", "AppUser", "country_id", "user_id");

        return $this->hasManyThrough("AppPost", "AppUser");
    }
}

方法hasManyThrough的第一个参数是我们希望访问的模型名称,第二个参数是中间模型名称。

HasManyThrough hasManyThrough( 
    string $related, 
    string $through, 
    string|null $firstKey = null, 
    string|null $secondKey = null, 
    string|null $localKey = null
)
Polymorphic Relations (多态关联)

多态关联使得同一个模型使用一个关联就可以属于多个不同的模型,假设这样一个场景,我们有一个帖子表和一个评论表,用户既可以对帖子执行喜欢操作,也可以对评论执行喜欢操作,这样的情况下该怎么处理呢?

表结构如下

posts
    id - integer
    title - string
    body - text

comments
    id - integer
    post_id - integer
    body - text

likes
    id - integer
    likeable_id - integer
    likeable_type - string

可以看到,我们使用likes表中的likeable_type字段判断该记录喜欢的是帖子还是评论,表结构有了,接下来就该定义模型了

morphTo();
    }
}

class Post extends Model
{

    public function likes()
    {
        return $this->morphMany("AppLike", "likeable");
    }
}

class Comment extends Model
{

    public function likes()
    {
        return $this->morphMany("AppLike", "likeable");
    }
}

默认情况下,likeable_type的类型是关联的模型的完整名称,比如这里就是AppPostAppComment

通常情况下我们可能会使用自定义的值标识关联的表名,因此,这就需要自定义这个值了,我们需要在项目的服务提供者对象的boot方法中注册关联关系,比如AppServiceProviderboot方法中

use IlluminateDatabaseEloquentRelationsRelation;

Relation::morphMap([
    "posts" => AppPost::class,
    "likes" => AppLike::class,
]);
检索多态关系

访问一个帖子所有的喜欢

$post = AppPost::find(1);  
foreach ($post->likes as $like) {
    //
}

访问一个喜欢的帖子或者评论

$like = AppLike::find(1);   
$likeable = $like->likeable;

上面的例子中,返回的likeable会根据该记录的类型返回帖子或者评论。

多对多的多态关联

多对多的关联使用方法morphToManymorphedByMany,这里就不多废话了。

关联关系查询

在Eloquent中,所有的关系都是使用函数定义的,可以在不执行关联查询的情况下获取关联的实例。假设我们有一个博客系统,User模型关联了很多Post模型:

public function posts()
{
   return $this->hasMany("AppPost");
}

你可以像下面这样查询关联并且添加额外的约束

$user = AppUser::find(1);
$user->posts()->where("active", 1)->get();

如果不需要对关联的属性添加约束,可以直接作为模型的属性访问,例如上面的例子,我们可以使用下面的方式访问User的Post

$user = AppUser::find(1);
foreach ($user->posts as $post) {
    //
}

动态的属性都是延迟加载的,它们只有在被访问的时候才会去查询数据库,与之对应的是预加载,预加载可以使用关联查询出所有数据,减少执行sql的数量。

查询关系存在性

使用has方法可以基于关系的存在性返回结果

// 检索至少有一个评论的所有帖子...
$posts = AppPost::has("comments")->get();

// Retrieve all posts that have three or more comments...
$posts = Post::has("comments", ">=", 3)->get();
// Retrieve all posts that have at least one comment with votes...
$posts = Post::has("comments.votes")->get();

如果需要更加强大的功能,可以使用whereHasorWhereHas方法,把where条件放到has语句中。

// 检索所有至少存在一个匹配foo%的评论的帖子
$posts = Post::whereHas("comments", function ($query) {
    $query->where("content", "like", "foo%");
})->get();
预加载

在访问Eloquent模型的时候,默认情况下所有的关联关系都是延迟加载的,在使用的时候才会开始加载,这就造成了需要执行大量的sql的问题,使用预加载功能可以使用关联查询出所有结果

belongsTo("AppAuthor");
    }
}

接下来我们检索所有的书和他们的作者

$books = AppBook::all();

foreach ($books as $book) {
    echo $book->author->name;
}

上面的查询将会执行一个查询查询出所有的书,然后在遍历的时候再执行N个查询查询出作者信息,显然这样做是非常低效的,幸好我们还有预加载功能,可以将这N+1个查询减少到2个查询,在查询的时候,可以使用with方法指定哪个关系需要预加载。

$books = AppBook::with("author")->get();
foreach ($books as $book) {
    echo $book->author->name;
}

对于该操作,会执行下列两个sql

select * from books
select * from authors where id in (1, 2, 3, 4, 5, ...)

预加载多个关系

$books = AppBook::with("author", "publisher")->get();

嵌套的预加载

$books = AppBook::with("author.contacts")->get();
带约束的预加载
$users = AppUser::with(["posts" => function ($query) {
    $query->where("title", "like", "%first%");
}])->get();

$users = AppUser::with(["posts" => function ($query) {
    $query->orderBy("created_at", "desc");
}])->get();
延迟预加载

有时候,在上级模型已经检索出来之后,可能会需要预加载关联数据,可以使用load方法

$books = AppBook::all();
if ($someCondition) {
    $books->load("author", "publisher");
}

$books->load(["author" => function ($query) {
    $query->orderBy("published_date", "asc");
}]);
关联模型插入 save方法

保存单个关联模型

$comment = new AppComment(["message" => "A new comment."]);
$post = AppPost::find(1);
$post->comments()->save($comment);

保存多个关联模型

$post = AppPost::find(1); 
$post->comments()->saveMany([
    new AppComment(["message" => "A new comment."]),
    new AppComment(["message" => "Another comment."]),
]);
save方法和多对多关联

多对多关联可以为save的第二个参数指定关联表中的属性

AppUser::find(1)->roles()->save($role, ["expires" => $expires]);

上述代码会更新中间表的expires字段。

create方法

使用create方法与save方法的不同在于它是使用数组的形式创建关联模型的

$post = AppPost::find(1);
$comment = $post->comments()->create([
    "message" => "A new comment.",
]);
更新 "Belongs To" 关系

更新belongsTo关系的时候,可以使用associate方法,该方法会设置子模型的外键

$account = AppAccount::find(10);
$user->account()->associate($account);
$user->save();

要移除belongsTo关系的话,使用dissociate方法

$user->account()->dissociate();
$user->save();
Many to Many 关系 中间表查询条件

当查询时需要对使用中间表作为查询条件时,可以使用wherePivotwherePivotInorWherePivotorWherePivotIn添加查询条件。

$enterprise->with(["favorites" => function($query) {
    $query->wherePivot("enterprise_id", "=", 12)->select("id");
}]);
Attaching / Detaching
$user = AppUser::find(1);
// 为用户添加角色
$user->roles()->attach($roleId);
// 为用户添加角色,更新中间表的expires字段
$user->roles()->attach($roleId, ["expires" => $expires]);

// 移除用户的单个角色
$user->roles()->detach($roleId);
// 移除用户的所有角色
$user->roles()->detach();

attachdetach方法支持数组参数,同时添加和移除多个

$user = AppUser::find(1);
$user->roles()->detach([1, 2, 3]);
$user->roles()->attach([1 => ["expires" => $expires], 2, 3]);
更新中间表(关联表)字段

使用updateExistingPivot方法更新中间表

$user = AppUser::find(1);
$user->roles()->updateExistingPivot($roleId, $attributes);
同步中间表(同步关联关系)

使用sync方法,可以指定两个模型之间只存在指定的关联关系

$user->roles()->sync([1, 2, 3]);
$user->roles()->sync([1 => ["expires" => true], 2, 3]);

上述两个方法都会让用户只存在1,2,3三个角色,如果用户之前存在其他角色,则会被删除。

更新父模型的时间戳

假设场景如下,我们为一个帖子增加了一个新的评论,我们希望这个时候帖子的更新时间会相应的改变,这种行为在Eloquent中是非常容易实现的。

在子模型中使用$touches属性实现该功能

belongsTo("AppPost");
    }
}

现在,更新评论的时候,帖子的updated_at字段也会被更新

$comment = AppComment::find(1);
$comment->text = "Edit to this comment!";
$comment->save();

参考: Eloquent: Relationships

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/21723.html

相关文章

  • Laravel & LumenEloquent ORM使用速查-基础部分

    摘要:使用时,数据库查询构造器的方法对模型类也是也用的,使用上只是省略了表名部分。在模型中使用成员变量指定绑定的表名。 使用Eloquent [eləkwənt] 时,数据库查询构造器的方法对模型类也是也用的,使用上只是省略了DB::table(表名)部分。 在模型中使用protected成员变量$table指定绑定的表名。

    NervosNetwork 评论0 收藏0
  • Laravel & LumenEloquent ORM使用速查-高级部分

    摘要:使用全局作用域功能可以为模型的所有操作增加约束。提供了一些方法可以方便的来实现数据类型之间的转换。要定义一个,需要在模型中创建一个名称为的方法,其中的是驼峰命名法的字段名。 查询作用域 全局作用域 全局作用域允许你对给定模型的所有查询添加约束。使用全局作用域功能可以为模型的所有操作增加约束。 软删除功能实际上就是利用了全局作用域功能 实现一个全局作用域功能只需要定义一个实现Illumi...

    BigNerdCoding 评论0 收藏0
  • Laravel & Lumen 数据库操作速查

    摘要:在中执行数据库操作有两种方式,一种是使用外观对象的静态方法直接执行查询,另外一种是使用类的静态方法实际上也是的实现,使用静态访问方式访问的方法,内部采用了魔术方法代理了对成员方法的访问。在闭包函数中,如果返回,则会停止后续的处理。 在Laravel中执行数据库操作有两种方式,一种是使用DB外观对象的静态方法直接执行sql查询,另外一种是使用Model类的静态方法(实际上也是Facade...

    用户83 评论0 收藏0
  • Lumen 初体验(二)

    摘要:的现状目前是版本,是基于开发。入口文件启动文件和配置文件框架的入口文件是。在路由中指定控制器类必须写全命名空间,不然会提示找不到类。目前支持四种数据库系统以及。使用时发生错误,因为在文件中,的默认驱动是。 最近使用 Lumen 做了 2 个业余项目,特此记录和分享一下。 Lumen 的介绍 在使用一项新的技术时,了解其应用场景是首要的事情。 Lumen 的口号:为速度而生的 La...

    Cheriselalala 评论0 收藏0
  • Lumen---为速度而生的 Laravel 框架

    摘要:什么是官网是一个由组件搭建而成的微框架是当前最快的框架之一在什么时候使用专为微服务或者设计举个例子如果你的应用里面有部分业务逻辑的请求频率比较高就可以单独把这部分业务逻辑拿出来使用来构建一个小因为是对优化了框架的加载机制所以对资源的要求少很 什么是 Lumen?官网 lumen 是一个由 Laravel 组件搭建而成的微框架,是当前最快的 PHP 框架之一! 在什么时候使用 Lume...

    104828720 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<