欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

Laravel Eloquent ORM--整理

Laravel Eloquent ORM--整理
 

介绍

Laravel 的 Eloquent ORM 提供了漂亮、简洁的 ActiveRecord 实现来和数据库的互动。 每个数据库表会和一个对应的「模型」互动。

在开始之前,记得把 config/database.PHP 里的数据库连接配置好。

基本用法

我们先从建立一个 Eloquent 模型开始。模型通常放在 app 目录下,但是您可以将它们放在任何地方,只要能通过 composer.json 自动载入。所有的 Eloquent 模型都继承于 Illuminate\Database\Eloquent\Model .

  • 例如我放在app/Models下,则在composer.json中的配置如下:
"autoload-dev": {
    "classmap": ["tests/TestCase.php", "app/Models"]
},
  • 1
  • 2
  • 3

1. 创建[模型]

手动创建

在app/Models下新建文件Logo.php

<?php namespace App\Model;
use Illuminate\Database\Eloquent\Model;
class Logo extends Model {}
  • 1
  • 2
  • 3

命令创建

php artisan make:model Models/Logo -m
  • 1
  • 命令创建的好处是:同时会在database/migrations 下生成对应的数据库迁移文件。 
    注意我们并没有告诉 Eloquent User 模型会使用哪个数据库表。若没有特别指定,系统会默认自动对应名称为「类名称的小写复数形态」的数据库表。所以,在上面的例子中, Eloquent 会假设 User 模型将把数据存在 users 数据库表。您也可以在类中定义 table 属性自定义要对应的数据库表。
class Logo extends Model {
    protected $table = 'my_logo';
}
  • 1
  • 2
  • 3
  • 注意: Eloquent 也会假设每个数据库表都有一个字段名称为 id 的主键。您可以在类里定义 primaryKey 属性来重写。同样的,您也可以定义 connection 属性,指定模型连接到指定的数据库连接。 
    定义好模型之后,您就可以从数据库表新增及获取数据了。注意在默认情况下,在数据库表里需要有 updated_at 和 created_at 两个字段。如果您不想设定或自动更新这两个字段,则将类里的 $timestamps 属性设为 false即可。
public $timestamps = false;
  • 1

2. 使用[模型]

使用模型的方法

取出所有记录:$logos = Logo::all();

根据主键取出一条数据:$logos = Logo::find(1);

拆分查询:传到方法里的第一个参数表示每次「拆分」要取出的数据数量。第二个参数的闭合函数会在每次取出数据时被调用。

User::chunk(200, function($users)
{   // 每次查询200条
    foreach ($users as $user)
    {
        //
    }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
其他方法就不一一列举了,可以访问:[eloquent ORM](http://laravel-china.org/docs/5.0/eloquent)
  • 如果没办法使用流畅接口产生出查询语句,也可以使用 whereRaw 方法:
$users = User::whereRaw('age > ? and votes = 100', [25])->get();
  • 1

软删除

通过软删除方式删除了一个模型后,模型中的数据并不是真的从数据库被移除。而是会设定 deleted_at时间戳。要让模型使用软删除功能,只要在模型类里加入 SoftDeletingTrait 即可:

use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Model {
    use SoftDeletes;
    protected $dates = ['deleted_at'];
}
  • 1
  • 2
  • 3
  • 4
  • 5

要加入 deleted_at 字段到数据库表,可以在迁移文件里使用 softDeletes 方法:

$table->softDeletes();
  • 1

现在当您使用模型调用 delete 方法时, deleted_at字段会被更新成现在的时间戳。在查询使用软删除功能的模型时,被「删除」的模型数据不会出现在查询结果里。

  • 普通查询方法只能查询到deleted_at为NULL的数据,也就是未被删除/有效的数据。要强制让已被软删除的模型数据出现在查询结果里,在查询时使用 withTrashed 方法:
$users = User::withTrashed()->where('account_id', 1)->get();
  • 1
  • 如果您只想查询被软删除的模型数据,可以使用 onlyTrashed 方法:
$users = User::onlyTrashed()->where('account_id', 1)->get();
  • 1
  • 要把被软删除的模型数据恢复,使用 restore 方法:
$user->restore();
  • 1
  • 您也可以结合查询语句使用 restore :
User::withTrashed()->where('account_id', 1)->restore();
  • 1
  • 如果想要真的从模型数据库删除,使用 forceDelete 方法:
$user->forceDelete();
  • 1
  • 要确认模型是否被软删除了,可以使用 trashed 方法:
if ($user->trashed())
{
    //
}
  • 1
  • 2
  • 3
  • 4

范围查询

定义范围查询

范围查询可以让您轻松的重复利用模型的查询逻辑。要设定范围查询,只要定义有 scope 前缀的模型方法:

class User extends Model {
    public function scopePopular($query)
    {
        return $query->where('votes', '>', 100);
    }
    public function scopeWomen($query)
    {
        return $query->whereGender('W');
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
使用范围查询
$users = User::popular()->women()->orderBy('created_at')->get();
  • 1
动态范围查询

有时您可能想要定义可接受参数的范围查询方法。只要把参数加到方法里:

class User extends Model {
    public function scopeOfType($query, $type)
    {
        return $query->whereType($type);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

然后把参数值传到范围查询方法调用里:

$users = User::ofType('member')->get();
  • 1

3. 关联

一对一

定义一对一关联

一对一关联是很基本的关联。例如一个 User 模型会对应到一个 Phone 。 在 Eloquent 里可以像下面这样定义关联:

class User extends Model {
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

传到 hasOne 方法里的第一个参数是关联模型的类名称。定义好关联之后,就可以使用 Eloquent 的动态属性取得关联对象:

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

SQL 会执行如下语句:

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

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

class Phone extends Model {
    public function user()
    {
        return $this->belongsTo('App\User');
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

class Phone extends Model {
    public function user()
    {
        return $this->belongsTo('App\User', 'local_key');
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

class Phone extends Model {
    public function user()
    {
        return $this->belongsTo('App\User', 'local_key', 'parent_key');
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

一对多

定义一对多关联

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

class Post extends Model {
    public function comments()
    {
        return $this->hasMany('App\Comment');
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

$comments = Post::find(1)->comments;
  • 1

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

$comments = Post::find(1)->comments()->where('title', '=', 'foo')->first();
  • 1

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

return $this->hasMany('App\Comment', 'foreign_key');

return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
  • 1
  • 2
  • 3
定义相对的关联

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

class Comment extends Model {
    public function post()
    {
        return $this->belongsTo('App\Post');
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

多对多

定义多对多关联

多对多关联更为复杂。这种关联的例子如,一个用户( 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');
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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

$roles = User::find(1)->roles;
  • 1

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

return $this->belongsToMany('App\Role', 'user_roles');
  • 1

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

return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'role_id');
  • 1

Has Many Through 远层一对多关联

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

countries
    id - integer
    name - string

users
    id - integer
    country_id - integer
    name - string

posts
    id - integer
    user_id - integer
    title - string
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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

class Country extends Model {
    public function posts()
    {
        return $this->hasManyThrough('App\Post', 'App\User');
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

class Country extends Model {
    public function posts()
    {
        return $this->hasManyThrough('App\Post', 'App\User', 'country_id', 'user_id');
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

关联查询

根据关联条件查询

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

$posts = Post::has('comments')->get();
  • 1

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

$posts = Post::has('comments', '>=', 3)->get();
  • 1

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

$posts = Post::has('comments.votes')->get();
  • 1

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

$posts = Post::whereHas('comments', function($q)
{
    $q->where('content', 'like', 'foo%');
})->get();
  • 1
  • 2
  • 3
  • 4

动态属性

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

class Phone extends Model {
    public function user()
    {
        return $this->belongsTo('App\User');
    }
}
$phone = Phone::find(1);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

您可以不用像下面这样打印用户的 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');
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

现在考虑下面的代码:

foreach (Book::all() as $book)
{
    echo $book->author->name;
}
  • 1
  • 2
  • 3
  • 4

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

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

foreach (Book::with('author')->get() as $book)
{
    echo $book->author->name;
}
  • 1
  • 2
  • 3
  • 4

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

select * from books

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

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

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

$books = Book::with('author', 'publisher')->get();
  • 1

预载入条件限制

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

$users = User::with(['posts' => function($query)
{
    $query->where('title', 'like', '%first%');
}])->get();
  • 1
  • 2
  • 3
  • 4

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

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

$users = User::with(['posts' => function($query)
{
    $query->orderBy('created_at', 'desc');
}])->get();
  • 1
  • 2
  • 3
  • 4

新增关联模型

附加一个关联模型

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

$comment = new Comment(['message' => 'A new comment.']);
$post = Post::find(1);
$comment = $post->comments()->save($comment);
  • 1
  • 2
  • 3

上面的例子里,新增的 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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

从属关联模型 ( Belongs To )

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

$account = Account::find(10);
$user->account()->associate($account);
$user->save();
  • 1
  • 2
  • 3

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

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

附加多对多模型

$user = User::find(1);
$user->roles()->attach(1);
  • 1
  • 2

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

$user->roles()->attach(1, ['expires' => $expires]);
  • 1

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

$user->roles()->detach(1);
  • 1

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

$user = User::find(1);
$user->roles()->detach([1, 2, 3]);
$user->roles()->attach([1 => ['attribute1' => 'value1'], 2, 3]);
  • 1
  • 2
  • 3

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

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

$user->roles()->sync([1, 2, 3]);
  • 1
Sync 时在枢纽表加入额外数据

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

$user->roles()->sync([1 => ['expires' => true]]);
  • 1

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

$role = new Role(['name' => 'Editor']);

User::find(1)->roles()->save($role);
  • 1
  • 2
  • 3

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

User::find(1)->roles()->save($role, ['expires' => $expires]);
来自 http://blog.csdn.net/a437629292/article/details/46312695
普通分类: