ホームページ  >  記事  >  バックエンド開発  >  Laravel フレームワーク - EloquentORM の高度な部分の詳細な紹介

Laravel フレームワーク - EloquentORM の高度な部分の詳細な紹介

黄舟
黄舟オリジナル
2017-03-21 09:18:191577ブラウズ

関連付け関係

One To One
UsermodelがPhoneモデルに関連付けられていると仮定します。このような関連付けを定義するには、hasOneメソッドで定義された関連付けを返すPhoneメソッドをUserモデルに定義する必要があります。

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class User extends Model{
    /**
     * Get the phone record associated with the user.
     */
    public function phone()
    {
        return $this->hasOne(&#39;App\Phone&#39;);
    }
}

hasOne メソッド 最初のパラメータは関連付けられるモデルです。定義後、次の構文を使用して関連付けられた属性をクエリできます。

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

Eloquent は関連付けられた外部キーがモデル名に基づいていると想定します。 Phone モデルは自動的に user_id フィールドを外部キーとして使用します。2 番目のパラメーターと 3 番目のパラメーターを使用して

return $this->hasOne(&#39;App\Phone&#39;, &#39;foreign_key&#39;);return $this->hasOne(&#39;App\Phone&#39;, &#39;foreign_key&#39;, &#39;local_key&#39;);

を上書きし、逆の関係を定義できます

上記のモデルを定義した後、User モデルを使用して取得することができますもちろん、Phone モデルを通じて対応するユーザーを取得することもできます。これは、belongsTo メソッドを使用します

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Phone extends Model{
    /**
     * Get the user that owns the phone.
     */
    public function user()
    {
        return $this->belongsTo(&#39;App\User&#39;);        // return $this->belongsTo(&#39;App\User&#39;, &#39;foreign_key&#39;);
        // return $this->belongsTo(&#39;App\User&#39;, &#39;foreign_key&#39;, &#39;other_key&#39;);

    }
}

この場合、関連するコメント情報がたくさんある投稿があるとします。 、hasMany メソッドを使用して 1 対多の関連付けを使用する必要があります

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Post extends Model{
    /**
     * Get the comments for the blog post.
     */
    public function comments()
    {
        return $this->hasMany(&#39;App\Comment&#39;);
    }
}

クエリ操作

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

$comments = App\Post::find(1)->comments()->where(&#39;title&#39;, &#39;foo&#39;)->first();

逆関連付けを定義する

逆関連付けでは、belongsTo メソッドも使用します。「1 対 1」セクションを参照してください。

$comment = App\Comment::find(1);
echo $comment->post->title;

多対多

多対多の関連付けは、追加の中間テーブルがあるため、hasOne や hasMany よりも実装が複雑です。

1 人のユーザーが複数のロールに属することができ、1 つのロールが複数のユーザーに属することもできるシナリオを考えてみましょう。これにより、users、roles、role_user という 3 つのテーブルが導入されます。 role_user テーブルは関連テーブルであり、user_id と role_id の 2 つのフィールドが含まれています。

多対多の関連付けには、belongsToMany メソッドが必要です

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class User extends Model{
    /**
     * The roles that belong to the user.
     */
    public function roles()
    {
        // 指定关联表
        // return $this->belongsToMany(&#39;App\Role&#39;, &#39;role_user&#39;);
        // 指定关联表,关联字段
        // return $this->belongsToMany(&#39;App\Role&#39;, &#39;role_user&#39;, &#39;user_id&#39;, &#39;role_id&#39;);

        return $this->belongsToMany(&#39;App\Role&#39;);
    }
}

上記では、関係が確立されると、ユーザーが複数のロールに属することを定義しています

user = App\User::find(1);
foreach ($user->roles as $role) {    //}$roles = App\User::find(1)->roles()->orderBy(&#39;name&#39;)->get();

逆関連付け関係

逆の関係が実装されています。前方リレーションシップと同じ

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Role extends Model{
    /**
     * The users that belong to the role.
     */
    public function users()
    {
        return $this->belongsToMany(&#39;App\User&#39;);
    }
}

中間テーブルのカラム値を取得します

多対多リレーションシップの場合、中間テーブルが導入されるため、カラム値をクエリする方法が必要ですリレーションシップが確立された時刻など、中間テーブルのクエリを実行します。ピボット属性を使用して中間テーブル テーブルをクエリします

$user = App\User::find(1);

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

上記のコードは、中間テーブルの created_at フィールドにアクセスします。

デフォルトでは、モデルのキーはピボット

オブジェクトを介してアクセスできることに注意してください。中間テーブルに追加の属性が含まれている場合は、関連付けを指定するときに withPivot メソッドを使用して列名を明示的に指定する必要があります

return $this->belongsToMany(&#39;App\Role&#39;)->withPivot(&#39;column1&#39;, &#39;column2&#39;);

Has Many Through

この関係は比較的強いシナリオを想定します: Country モデルには複数の User モデルが含まれ、各 User モデルには複数の Post モデルが含まれます。つまり、1 つの国には多数のユーザーが存在し、これらのユーザーは特定の国のすべての投稿をクエリしたいのですが、これを実現するには、Has Many Through 関係を使用します

countries    id - integer
    name - stringusers    id - integer
    country_id - integer
    name - stringposts    id - integer
    user_id - integer
    title - string

ご覧のとおり、posts テーブルには、country_id が直接含まれていません。国テーブルには、Has Many Through 関係

namespace App;

use Illuminate\Database\Eloquent\Model;class Country extends Model{    /**
     * Get all of the posts for the country.
     */
    public function posts()
    {        // return $this->hasManyThrough(&#39;App\Post&#39;, &#39;App\User&#39;, &#39;country_id&#39;, &#39;user_id&#39;);

        return $this->hasManyThrough(&#39;App\Post&#39;, &#39;App\User&#39;);
    }
}
を使用した関係

があります。メソッド hasManyThrough の最初のパラメータは、アクセスしたいモデルの名前であり、2 番目のパラメータは中間モデル名です。 。

HasManyThrough hasManyThrough( 
    string $related, 
    string $through, 
    string|null $firstKey = null, 
    string|null $secondKey = null, 
    string|null $localKey = null)

ポリモーフィック リレーション (ポリモーフィック リレーション)

ポリモーフィック リレーションでは、1 つのリレーションを使用して、同じモデルが複数の異なるモデルに属することができます。投稿テーブルとコメント テーブルの両方を使用できます。またはこの状況にどう対処すればよいでしょうか?

テーブル構造は次のとおりです

posts    id - integer
    title - string
    body - textcomments    id - integer
    post_id - integer
    body - textlikes    id - integer
    likeable_id - integer
    likeable_type - string

ご覧のとおり、likes テーブルの likeable_type フィールドを使用して、レコードが投稿またはコメントを気に入っているかどうかを判断します。テーブル構造が整ったら、次は、 model

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;class Like extends Model{    /**
     * Get all of the owning likeable models.
     */
    public function likeable()
    {        return $this->morphTo();
    }
}class Post extends Model{    /**
     * Get all of the product&#39;s likes.
     */
    public function likes()
    {        return $this->morphMany(&#39;App\Like&#39;, &#39;likeable&#39;);
    }
}class Comment extends Model{    /**
     * Get all of the comment&#39;s likes.
     */
    public function likes()
    {        return $this->morphMany(&#39;App\Like&#39;, &#39;likeable&#39;);
    }
}

デフォルトでは、likeable_type タイプは、ここでは AppPost や AppComment など、関連付けられたモデルの完全な名前です。

通常、関連付けられたテーブル名を識別するためにカスタム値を使用することがあります。そのため、プロジェクトのサービスプロバイダーオブジェクトのブートメソッドで関連付けをカスタマイズする必要があります。

use Illuminate\Database\Eloquent\Relations\Relation;Relation::morphMap([    &#39;posts&#39; => App\Post::class,
    &#39;likes&#39; => App\Like::class,]);

はポリモーフィックな関係を取得します

AppServiceProvider 投稿のすべての「いいね」にアクセスします

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

いいねされた投稿またはコメントにアクセスします

$like = App\Like::find(1);   
$likeable = $like->likeable;

上記の例では、返された likeable はレコードのタイプに応じて投稿またはコメントを返します。

多対多のポリモーフィックな関連付け

多対多の関連付けは、morphToMany と morphedByMany メソッドを使用します。ここではこれ以上ナンセンスではありません

関係クエリ

Eloquent では、すべての関係は関数を使用して

定義されており

、関連するインスタンスを取得できます関連するクエリを実行せずに。ブログ システムがあり、User モデルが多くの Post モデルに関連付けられているとします。

/**
 * Get all of the posts for the user.
 */public function posts()
{   return $this->hasMany(&#39;App\Post&#39;);
}

次のように関連付けをクエリし、追加の 制約を追加できます

$user = App\User::find(1);$user->posts()->where(&#39;active&#39;, 1)->get();

関連する属性に制約を追加する必要がない場合は、上記の例のように、モデル属性アクセスとして直接使用できます。次のメソッドを使用してユーザーの投稿にアクセスできます

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

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

查询关系存在性

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

// 检索至少有一个评论的所有帖子...$posts = App\Post::has(&#39;comments&#39;)->get();
// Retrieve all posts that have three or more comments...$posts = Post::has(&#39;comments&#39;, &#39;>=&#39;, 3)->get();
// Retrieve all posts that have at least one comment with votes...$posts = Post::has(&#39;comments.votes&#39;)->get();

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

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

预加载

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

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Book extends Model{
    /**
     * Get the author that wrote the book.
     */
    public function author()
    {
        return $this->belongsTo(&#39;App\Author&#39;);
    }
}

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

$books = App\Book::all();
foreach ($books as $book) {    
echo $book->author->name;
}

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

$books = App\Book::with(&#39;author&#39;)->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 = App\Book::with(&#39;author&#39;, &#39;publisher&#39;)->get();

嵌套的预加载

$books = App\Book::with(&#39;author.contacts&#39;)->get();

带约束的预加载

$users = App\User::with([&#39;posts&#39; => function ($query) {
    $query->where(&#39;title&#39;, &#39;like&#39;, &#39;%first%&#39;);
}])->get();$users = App\User::with([&#39;posts&#39; => function ($query) {
    $query->orderBy(&#39;created_at&#39;, &#39;desc&#39;);
}])->get();

延迟预加载#

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

$books = App\Book::all();if ($someCondition) {    $books->load(&#39;author&#39;, &#39;publisher&#39;);
}$books->load([&#39;author&#39; => function ($query) {
    $query->orderBy(&#39;published_date&#39;, &#39;asc&#39;);
}]);

关联模型插入

save方法

保存单个关联模型

$comment = new App\Comment([&#39;message&#39; => &#39;A new comment.&#39;]);
$post = App\Post::find(1);$post->comments()->save($comment);

保存多个关联模型

$post = App\Post::find(1); 
$post->comments()->saveMany([    
new App\Comment([&#39;message&#39; => &#39;A new comment.&#39;]),    
new App\Comment([&#39;message&#39; => &#39;Another comment.&#39;]),
]);

save方法和多对多关联

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

App\User::find(1)->roles()->save($role, [&#39;expires&#39; => $expires]);

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

create方法

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

$post = App\Post::find(1);$comment = $post->comments()->create([
    &#39;message&#39; => &#39;A new comment.&#39;,]);

更新 “Belongs To” 关系

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

$account = App\Account::find(10);
$user->account()->associate($account);
$user->save();

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

$user->account()->dissociate();$user->save();

Many to Many 关系

中间表查询条件#

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

$enterprise->with([&#39;favorites&#39; => function($query) {    
$query->wherePivot(&#39;enterprise_id&#39;, &#39;=&#39;, 12)->select(&#39;id&#39;);
}]);

Attaching / Detaching#

$user = App\User::find(1);
// 为用户添加角色
$user->roles()->attach($roleId);
// 为用户添加角色,更新中间表的expires字段
$user->roles()->attach($roleId, [&#39;expires&#39; => $expires]);
// 移除用户的单个角色
$user->roles()->detach($roleId);
// 移除用户的所有角色
$user->roles()->detach();

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

$user = App\User::find(1);
$user->roles()->detach([1, 2, 3]);
$user->roles()->attach([1 => [&#39;expires&#39; => $expires], 2, 3]);

更新中间表(关联表)字段

使用updateExistingPivot方法更新中间表

$user = App\User::find(1);$user->roles()->updateExistingPivot($roleId, $attributes);

同步中间表(同步关联关系)#

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

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

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

更新父模型的时间戳#

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

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

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;class Comment extends Model{    /**
     * All of the relationships to be touched.
     *
     * @var array
     */
    protected $touches = [&#39;post&#39;];    /**
     * Get the post that the comment belongs to.
     */
    public function post()
    {        return $this->belongsTo(&#39;App\Post&#39;);
    }
}

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

$comment = App\Comment::find(1);$comment->text = &#39;Edit to this comment!&#39;;$comment->save();

以上がLaravel フレームワーク - EloquentORM の高度な部分の詳細な紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。