Laravel Eloquent ORM 仕上げ

WBOY
リリース: 2016-06-23 13:33:34
オリジナル
1047 人が閲覧しました

はじめに

Laravel の Eloquent ORM は、データベースと対話するための美しく簡潔な ActiveRecord 実装を提供します。 各データベース テーブルは、対応する「モデル」と対話します。

開始する前に、config/database.php でデータベース接続を構成することを忘れないでください。

基本的な使い方

まずは Eloquent モデルを構築しましょう。モデルは通常、アプリ ディレクトリに配置されますが、composer.json 経由で自動的にロードできる限り、どこにでも配置できます。すべての Eloquent モデルは IlluminateDatabaseEloquentModel を継承します

  • たとえば、app/Models の下に配置した場合、composer.json の構成は次のようになります:
  • "autoload-dev": {    "classmap": ["tests/TestCase.php", "app/Models"]},
    ログイン後にコピー

    手動で作成します

    app/Models の下新しいファイルの作成 Logo.php

    <?php namespace App\Model; use Illuminate\Database\Eloquent\Model; class Logo extends Model {}
    ログイン後にコピー

    コマンド作成

    php artisan make:model User
    ログイン後にコピー

    コマンド作成 利点は、対応するデータベース移行ファイルがデータベース/移行の下に同時に生成されることです。
  • Eloquent User モデルにどのデータベース テーブルを使用するかを指示していないことに注意してください。特別な指定がない場合、デフォルトではシステムが自動的に「クラス名の小文字複数形」という名前でデータベーステーブルに対応します。したがって、上記の例では、Eloquent は User モデルがユーザー データベース テーブルにデータを保存すると想定します。クラスで table 属性を定義して、対応するデータベース テーブルをカスタマイズすることもできます。
    class Logo extends Model {    protected $table = 'my_logo';}
    ログイン後にコピー
  • 注: Eloquent は、各データベース テーブルにフィールド名が id の主キーがあることも前提としています。これは、クラスでprimaryKeyプロパティを定義することでオーバーライドできます。同様に、接続属性を定義して、指定したデータベース接続に接続するモデルを指定することもできます。
  • モデルを定義した後、データベーステーブルにデータを追加したり、データベーステーブルからデータを取得したりできます。デフォルトでは、データベース テーブルに 2 つのフィールド、updated_at と created_at が必要であることに注意してください。これら 2 つのフィールドを設定したり、自動的に更新したくない場合は、クラスの $timestamps プロパティを false に設定するだけです。
    public $timestamps = false;
    ログイン後にコピー
  • 2. [Model] を使用します

    モデルの使用方法

    すべてのレコードを取得します: $logos = Logo::all();

    主キーに基づいてデータを取得します: $logos = Logo:: find(1);

    Split クエリ: メソッドに渡される最初のパラメーターは、「分割」ごとに取得されるデータの量を示します。 2 番目のパラメーターの終了関数は、データが取得されるたびに呼び出されます。

    他のメソッドは 1 つずつリストされていません: [eloquent ORM](http://laravel-china.org/docs/5.0/eloquent)

    Fluent インターフェイスを使用してクエリ ステートメントを生成できない場合は、 whereRaw メソッドも使用できます:
  • User::chunk(200, function($users) {   // 每次查询200条    foreach ($users as $user)    {        //    }});
    ログイン後にコピー
  • ソフト削除

    ソフト削除によってモデルを削除した後、モデル内のデータは実際にはデータベースから削除されません。代わりに、deleted_at タイムスタンプが設定されます。モデルにソフト削除関数を使用させるには、SoftDeletingTrait をモデル クラスに追加するだけです:

    $users = User::whereRaw('age > ? and votes = 100', [25])->get();
    ログイン後にコピー

    データベース テーブルに delete_at フィールドを追加するには、移行ファイルで SoftDeletes メソッドを使用できます:

    use Illuminate\Database\Eloquent\SoftDeletes;class User extends Model {    use SoftDeletes;    protected $dates = ['deleted_at'];}
    ログイン後にコピー

    モデルを使用するときはdelete メソッドを呼び出すと、deleted_at フィールドが現在のタイムスタンプに更新されます。論理的な削除機能を使用してモデルをクエリすると、「削除された」モデル データはクエリ結果に表示されません。

    通常のクエリ メソッドは、deleted_at が NULL であるデータ、つまり削除/有効ではないデータのみをクエリできます。論理的に削除されたモデル データをクエリ結果に強制的に表示するには、クエリ時に withTrashed メソッドを使用します:
  • $table->softDeletes();
    ログイン後にコピー
  • 論理的に削除されたモデル データのみをクエリしたい場合は、onlyTrashed メソッドを使用できます:
  • $users = User::withTrashed()->where('account_id', 1)->get();
    ログイン後にコピー
  • To query論理的に削除されたモデル データ 削除されたモデル データを復元するには、restore メソッドを使用します:
  • $users = User::onlyTrashed()->where('account_id', 1)->get();
    ログイン後にコピー
  • クエリ ステートメントと組み合わせて復元を使用することもできます:
  • $user->restore();
    ログイン後にコピー
  • モデル データベースから実際に削除したい場合は、forceDelete メソッドを使用します:
  • User::withTrashed()->where('account_id', 1)->restore();
    ログイン後にコピー
  • モデルが論理的に削除されたかどうかを確認するには、Trashed メソッドを使用できます:
  • $user->forceDelete();
    ログイン後にコピー
  • 範囲クエリ

    範囲クエリを定義する

    範囲クエリを使用すると、モデルのクエリ ロジックを簡単に再利用できます。スコープ クエリを設定するには、スコープ プレフィックスを付けてモデル メソッドを定義するだけです。

    if ($user->trashed()){    //}
    ログイン後にコピー
    スコープ クエリの使用
    class User extends Model {    public function scopePopular($query) {        return $query->where('votes', '>', 100);    }    public function scopeWomen($query) {        return $query->whereGender('W');    }}
    ログイン後にコピー
    動的スコープ クエリ

    パラメーターを受け入れるスコープ クエリ メソッドを定義したい場合があります。メソッドにパラメーターを追加するだけです:

    $users = User::popular()->women()->orderBy('created_at')->get();
    ログイン後にコピー

    次に、パラメーター値を範囲クエリ メソッド呼び出しに渡します:

    class User extends Model {    public function scopeOfType($query, $type) {        return $query->whereType($type);    }}
    ログイン後にコピー

    3. 関連

    One-to-one

    1 対 1 の関連付けを定義します

    One-to-oneアソシエーションは非常に基本的なアソシエーションです。たとえば、 User モデルは Phone に対応します。 Eloquent では、次のように関連付けを定義できます:

    $users = User::ofType('member')->get();
    ログイン後にコピー

    hasOne メソッドに渡される最初のパラメータは、関連付けられたモデルのクラス名です。関連付けを定義した後、Eloquent の動的プロパティを使用して、関連付けられたオブジェクトを取得できます:

    class User extends Model {    public function phone() {        return $this->hasOne('App\Phone');    }}
    ログイン後にコピー

    SQL は次のステートメントを実行します:

    select * from users where id = 1select * from phones where user_id = 1
    ログイン後にコピー
  • 注意, Eloquent 假设对应的关联模型数据库表里,外键名称是基于模型名称。在这个例子里,默认 Phone 模型数据库表会以 user_id 作为外键。如果想要更改这个默认,可以传入第二个参数到 hasOne 方法里。更进一步,您可以传入第三个参数,指定关联的外键要对应到本身的哪个字段:
  • return $this->hasOne('App\Phone', 'foreign_key');return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
    ログイン後にコピー
    定义相对的关联

    要在 Phone 模型里定义相对的关联,可以使用 belongsTo 方法:

    class Phone extends Model {    public function user() {        return $this->belongsTo('App\User');    }}
    ログイン後にコピー

    在上面的例子里, Eloquent 默认会使用 phones 数据库表的 user_id 字段查询关联。如果想要自己指定外键字段,可以在 belongsTo 方法里传入第二个参数:

    class Phone extends Model {    public function user() {        return $this->belongsTo('App\User', 'local_key');    }}
    ログイン後にコピー

    除此之外,也可以传入第三个参数指定要参照上层数据库表的哪个字段:

    class Phone extends Model {    public function user() {        return $this->belongsTo('App\User', 'local_key', 'parent_key');    }}
    ログイン後にコピー

    一对多

    定义一对多关联

    一对多关联的例子如,一篇 Blog 文章可能「有很多」评论。可以像这样定义关联:

    class Post extends Model {    public function comments() {        return $this->hasMany('App\Comment');    }}
    ログイン後にコピー

    现在可以经由动态属性取得文章的评论:

    $comments = Post::find(1)->comments;
    ログイン後にコピー

    如果需要增加更多条件限制,可以在调用 comments 方法后面通过链式查询条件方法:

    $comments = Post::find(1)->comments()->where('title', '=', 'foo')->first();
    ログイン後にコピー

    同样的,您可以传入第二个参数到 hasMany 方法更改默认的外键名称。以及,如同 hasOne 关联,可以指定本身的对应字段:

    return $this->hasMany('App\Comment', 'foreign_key');return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
    ログイン後にコピー
    定义相对的关联

    要在 Comment 模型定义相对应的关联,可使用 belongsTo 方法:

    class Comment extends Model {    public function post() {        return $this->belongsTo('App\Post');    }}
    ログイン後にコピー

    多对多

    定义多对多关联

    多对多关联更为复杂。这种关联的例子如,一个用户( user )可能用有很多身份( role ),而一种身份可能很多用户都有。例如很多用户都是「管理者」。多对多关联需要用到三个数据库表: users , roles ,和 role_user 。 role_user 枢纽表命名是以相关联的两个模型数据库表,依照字母顺序命名,枢纽表里面应该要有 user_id 和 role_id 字段。

    可以使用 belongsToMany 方法定义多对多关系:

    class User extends Model {    public function roles() {        return $this->belongsToMany('App\Role');    }}class Role extends Model {    public function users() {        return $this->belongsToMany('App\User');    }}
    ログイン後にコピー

    现在我们可以从 User 模型取得 roles:

    $roles = User::find(1)->roles;
    ログイン後にコピー

    如果不想使用默认的枢纽数据库表命名方式,可以传递数据库表名称作为 belongsToMany 方法的第二个参数:

    return $this->belongsToMany('App\Role', 'user_roles');
    ログイン後にコピー

    也可以更改默认的关联字段名称:

    return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'foo_id');
    ログイン後にコピー

    Has Many Through 远层一对多关联

    「远层一对多关联」提供了方便简短的方法,可以经由多层间的关联取得远层的关联。例如,一个 Country 模型可能通过 Users 关联到很多 Posts 模型。 数据库表间的关系可能看起来如下:

    countries    id - integer    name - stringusers    id - integer    country_id - integer    name - stringposts    id - integer    user_id - integer    title - string
    ログイン後にコピー

    虽然 posts 数据库表本身没有 country_id 字段,但 hasManyThrough 方法让我们可以使用 $country->posts 取得 country 的 posts。我们可以定义以下关联:

    class Country extends Model {    public function posts() {        return $this->hasManyThrough('App\Post', 'App\User');    }}
    ログイン後にコピー

    如果想要手动指定关联的字段名称,可以传入第三和第四个参数到方法里:

    class Country extends Model {    public function posts() {        return $this->hasManyThrough('App\Post', 'App\User', 'country_id', 'user_id');    }}
    ログイン後にコピー

    关联查询

    根据关联条件查询

    在取得模型数据时,您可能想要以关联模型作为查询限制。例如,您可能想要取得所有「至少有一篇评论」的Blog 文章。可以使用 has 方法达成目的:

    $posts = Post::has('comments')->get();
    ログイン後にコピー

    也可以指定运算符和数量:

    $posts = Post::has('comments', '>=', 3)->get();
    ログイン後にコピー

    也可以使用”点号”的形式来获取嵌套的 has 声明:

    $posts = Post::has('comments.votes')->get();
    ログイン後にコピー

    如果想要更进阶的用法,可以使用 whereHas 和 orWhereHas 方法,在 has 查询里设置 “where” 条件 :

    $posts = Post::whereHas('comments', function($q){    $q->where('content', 'like', 'foo%');})->get();
    ログイン後にコピー

    动态属性

    Eloquent 可以经由动态属性取得关联对象。 Eloquent 会自动进行关联查询,而且会很聪明的知道应该要使用 get(用在一对多关联)或是 first (用在一对一关联)方法。可以经由和「关联方法名称相同」的动态属性取得对象。例如,如下面的模型对象 $phone:

    class Phone extends Model {    public function user() {        return $this->belongsTo('App\User');    }}$phone = Phone::find(1);
    ログイン後にコピー

    您可以不用像下面这样打印用户的 email :echo $phone->user()->first()->email;
    而可以简写如下:echo $phone->user->email;

  • 若取得的是许多关联对象,会返回 Illuminate\Database\Eloquent\Collection 对象。
  • 预载入

    预载入是用来减少 N + 1 查询问题。例如,一个 Book 模型数据会关联到一个 Author 。关联会像下面这样定义:

    class Book extends Model {    public function author() {        return $this->belongsTo('App\Author');    }}
    ログイン後にコピー

    现在考虑下面的代码:

    foreach (Book::all() as $book){    echo $book->author->name;}
    ログイン後にコピー

    上面的循环会执行一次查询取回所有数据库表上的书籍,然而每本书籍都会执行一次查询取得作者。所以若我们有 25 本书,就会进行 26次查询。

    很幸运地,我们可以使用预载入大量减少查询次数。使用 with 方法指定想要预载入的关联对象:

    foreach (Book::with('author')->get() as $book) { echo $book->author->name;}
    ログイン後にコピー

    现在,上面的循环总共只会执行两次查询:

    select * from booksselect * from authors where id in (1, 2, 3, 4, 5, ...)
    ログイン後にコピー

    使用预载入可以大大提高程序的性能。

    当然,也可以同时载入多种关联:

    $books = Book::with('author', 'publisher')->get();
    ログイン後にコピー

    预载入条件限制

    有时您可能想要预载入关联,同时也想要指定载入时的查询限制。下面有一个例子:

    $users = User::with(['posts' => function($query) {    $query->where('title', 'like', '%first%');}])->get();
    ログイン後にコピー

    上面的例子里,我们预载入了 user 的 posts 关联,并限制条件为 post 的 title 字段需包含 “first” 。

    当然,预载入的闭合函数里不一定只能加上条件限制,也可以加上排序:

    $users = User::with(['posts' => function($query) {    $query->orderBy('created_at', 'desc');}])->get();
    ログイン後にコピー

    新增关联模型

    附加一个关联模型

    您常常会需要加入新的关联模型。例如新增一个 comment 到 post 。除了手动设定模型的 post_id 外键,
    也可以从上层的 Post 模型新增关联的 comment :

    $comment = new Comment(['message' => 'A new comment.']);$post = Post::find(1);$comment = $post->comments()->save($comment);
    ログイン後にコピー

    上面的例子里,新增的 comment 模型中 post_id 字段会被自动设定。

    如果想要同时新增很多关联模型:

    $comments = [    new Comment(['message' => 'A new comment.']),    new Comment(['message' => 'Another comment.']),    new Comment(['message' => 'The latest comment.'])];$post = Post::find(1);$post->comments()->saveMany($comments);
    ログイン後にコピー

    从属关联模型 ( Belongs To )

    要更新 belongsTo 关联时,可以使用 associate 方法。这个方法会设定子模型的外键:

    $account = Account::find(10);$user->account()->associate($account);$user->save();
    ログイン後にコピー

    新增多对多关联模型 ( Many To Many )

    您也可以新增多对多的关联模型。让我们继续使用 User 和 Role 模型作为例子。我们可以使用 attach 方法简单地把 roles 附加给一个 user:

    附加多对多模型

    $user = User::find(1);$user->roles()->attach(1);
    ログイン後にコピー

    也可以传入要存在枢纽表中的属性数组:

    $user->roles()->attach(1, ['expires' => $expires]);
    ログイン後にコピー

    当然,有 attach 方法就会有相反的 detach 方法:

    $user->roles()->detach(1);
    ログイン後にコピー

    attach 和 detach 都可以接受ID数组作为参数:

    $user = User::find(1);$user->roles()->detach([1, 2, 3]);$user->roles()->attach([1 => ['attribute1' => 'value1'], 2, 3]);
    ログイン後にコピー

    使用 Sync 方法同时附加一个以上多对多关联

    您也可以使用 sync 方法附加关联模型。 sync 方法会把根据 ID 数组把关联存到枢纽表。附加完关联后,枢纽表里的模型只会关联到 ID 数组里的 id :

    $user->roles()->sync([1, 2, 3]);
    ログイン後にコピー
    Sync 时在枢纽表加入额外数据

    也可以在把每个 ID 加入枢纽表时,加入其他字段的数据:

    $user->roles()->sync([1 => ['expires' => true]]);
    ログイン後にコピー

    有时您可能想要使用一个命令,在建立新模型数据的同时附加关联。可以使用 save 方法达成目的:

    $role = new Role(['name' => 'Editor']);User::find(1)->roles()->save($role);
    ログイン後にコピー

    上面的例子里,新的 Role 模型对象会在储存的同时关联到 user 模型。也可以传入属性数组把数据加到关联数据库表:

    User::find(1)->roles()->save($role, ['expires' => $expires]);
    ログイン後にコピー
    関連ラベル:
    ソース:php.cn
    このウェブサイトの声明
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
    人気のチュートリアル
    詳細>
    最新のダウンロード
    詳細>
    ウェブエフェクト
    公式サイト
    サイト素材
    フロントエンドテンプレート