Eloquent ORM

目录

基础介绍

ORM是 对象关系映射的简称, 你一定会爱上Laravel的叫做"Eloquent"的ORM.因为它可以让你用非常口语化的表达式语法操作数据库的对象和关系.一般情况下, 你会为每个表定义一个Eloquent模型. 让我们定义一个简单的模型:

class User extends Eloquent {}

很好! 注意, 我们的类集成了Eloquent类. 这个类将提供了利用Eloquent操作数据库的所有功能.

提示: 通常情况下, Eloquent模型保存在application/models目录下.

约定

使用Eloquent要使对你的数据库结构有以下约定:

  • 每个表都应该有一个名为id的主.
  • 每个表的名称应该是其相应的模型名称的复数形式.

有时你可能想使用你的模型的复数形式以外的名字作为表名. 没问题. 只需添加一个静态的table属性就好:

class User extends Eloquent {

     public static $table = 'my_users';

}

检索模型

使用Eloquent来检索模型是非常简单的. 最基本的检索Eloquent模型的方式是使用静态方法find. 此方法将返回一个单一的模型, 其中的属性名称对应表中的列名:

$user = User::find(1);

echo $user->email;

find方法将执行一个查询, 看起来像这样:

SELECT * FROM "users" WHERE "id" = 1

需要检索整个表吗?使用静态all方法:

$users = User::all();

foreach ($users as $user)
{
     echo $user->email;
}

当然, 检索整个表不是很符合业务需求. 庆幸的是, 通过流畅的查询生成器(fluent query builder)可用使用的方法Eloquent中都可以使用, 利用查询生成器中的方法名来作为静态方法查询模型就可以, 或者使用get或者first方法来执行查询. GET方法将返回一个模型数组, 而第一种方法将只返回一个模型:

$user = User::where('email', '=', $email)->first();

$user = User::where_email($email)->first();

$users = User::where_in('id', array(1, 2, 3))->or_where('email', '=', $email)->get();

$users = User::order_by('votes', 'desc')->take(10)->get();

提示: If no results are found, the first method will return NULL. The all and get methods return an empty array.

聚集

需要取得MIN, MAX, AVG, SUM,或者COUNT的值吗?只要把字段名传入适当的方法就好:

$min = User::min('id');

$max = User::max('id');

$avg = User::avg('id');

$sum = User::sum('id');

$count = User::count();

当然,你也许希望用WHERE子句来进行条件限制:

$count = User::where('id', '>', 10)->count();

插入或更新模型

把Eloquent插入到数据表是非常简单的. 首先, 实例化一个新的实例. 第二, 设置其属性. 第三使用save方法:

$user = new User;

$user->email = 'example@gmail.com';
$user->password = 'secret';

$user->save();

另外, 你可以使用*create方法, 这将在数据库中插入一个新的记录, 并返回新插入的记录模型实例, 如果插入失败返回false**.

$user = User::create(array('email' => 'example@gmail.com'));

更新模型同样简单. 实例化一个新的模型, 从数据库中检索并填充他. 然后设置其属性并保存:

$user = User::find(1);

$user->email = 'new_email@gmail.com';
$user->password = 'new_secret';

$user->save();

需要创建和更新时间戳到数据库记录中?与口才, 不必担心. 只需添加一个静态的timestamps属性到的模型就好

class User extends Eloquent {

     public static $timestamps = true;

}

然后,添加,created_atupdated_at字段到表中. 每当您保存模型, 建立和更新的时间戳将自动设置.很好!.

提示:application/config/application.php文件中, 你可以改变你的应用程序的默认时区.

关系

你的数据库中的表可能是彼此相关的. 例如, 一个订单可能属于一个用户. 或一个博客可能有很多的评论. Eloquent使定义和检索相关的模型简单而直观, 直观. laravel支持三种类型的关系

要定义Eloquent模型的关系,你只需要定义返回has_one, has_many, belongs_to, or has_many_and_belongs_to 方法.下面是每一种的详细介绍.

一对一(One-To-One)

一到一对一的关系, 是关系的最基本的形式. 例如, 让我们假设一个用户拥有一部手机. 简单的描述Eloquent的这种关系:

class User extends Eloquent {

     public function phone()
     {
          return $this->has_one('Phone');
     }

}

请注意, 相关的模型的名称传递给了has_one方法. 现在, 您可以通过phone方法检索用户的电话了:

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

看下后台SQL运行的SQL语句. 将执行两个查询: 一个检索用户, 一个检索用户的手机:

SELECT * FROM "users" WHERE "id" = 1

SELECT * FROM "phones" WHERE "user_id" = 1

注意Eloquent默认外键的关系是 user_id. 大多数的外键遵循model_id约定; 然而, 如果你想用其他的字段名称做外键, 把字段名称传入第二个参数:

return $this->has_one('Phone', 'my_foreign_key');

想获取用户的电话而不调用first方法. 没问题,使用动态的phone属性就好. Eloquent会自动加载数据关系, 甚至会知道合适调用这个属性的get方法(用于一对多)或者first(用于一对一)方法.

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

如果你想查询使用该电话的用户该怎么办呢? 因为外键 (user_id)在phones表中,我们需要设置belongs_to方法.电话属于用户, 所以使用belongs_to方法, 方法名称应该和外键名称相同 (外键形如:*_id). 因为外键名称是user_id**, 所以关联方法也应该起名为user:

class Phone extends Eloquent {

     public function user()
     {
          return $this->belongs_to('User');
     }

}

很好! 现在你可以通过Phone模型访问User模型了. 既可以使用关联方法, 也可以使用关联属性:

echo Phone::find(1)->user()->first()->email;

echo Phone::find(1)->user->email;

一对多(One-To-Many)

假设一个博客后有很多评论. 使用has_many方法来定义这种关系是很容易的:

class Post extends Eloquent {

     public function comments()
     {
          return $this->has_many('Comment');
     }

}

现在, 只需通过关系方法或关系的动态属性的访问评论:

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

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

这些操作都将执行下列SQL:

SELECT * FROM "posts" WHERE "id" = 1

SELECT * FROM "comments" WHERE "post_id" = 1

要加入不同的外键? 只要传入参数就好:

return $this->has_many('Comment', 'my_foreign_key');

你可能会想:既然动态属性可以返回关系和数据为什么还要用方法呢?实际上方法是非常强大的呢?他们允许你使用关系的链式查询方法. 看下这个的使用方法:

echo Post::find(1)->comments()->order_by('votes', 'desc')->take(10)->get();

多对多(Many-To-Many)

多对多是三个关系中最复杂的一个. 不过不用担心, 你可以这样做的. 例如, 假设一个用户拥有多个角色, 但角色也可以属于很多用户. 完成这种关系必须建立三个数据库表:一个用户表, 角色表和角色用户关系(ROLE_USER)表. 每个表的结构看起来像这样:

Users:

id    - INTEGER
email - VARCHAR

Roles:

id   - INTEGER
name - VARCHAR

Roles_Users:

user_id - INTEGER
role_id - INTEGER

现在, 可以使用has_many_and_belongs_to方法来定义你的模型的关系:

class User extends Eloquent {

     public function roles()
     {
          return $this->has_many_and_belongs_to('Role');
     }

}

太棒了!现在是时候来检索用户的角色了:

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

或者, 像往常一样, 你可以通过动态的角色属性检索关系了:

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

正如你可能已经注意到, 中间表的默认名称是按表名的字母顺序的, 表名之间有道下划线. 然而, 你也可以自由指定自己的表名. 只要在has_and_belongs_to_many方法方法的第二个参数传入表名就好:

class User extends Eloquent {

     public function roles()
     {
          return $this->has_many_and_belongs_to('Role', 'user_roles');
     }

}

插入相关的模型

假设你有一个文章模型, 他有许多评论. 通常情况下, 你可能想要在给定文章上插入一个新评论, 而不是手工设置评论模型上post_id外键, 像这个样子:

$comment = new Comment(array('message' => 'A new comment.'));

$post = Post::find(1);

$post->comments()->insert($comment);

当通过他们的父模型插入相关的模型时, 外键将被自动设置. 因此, 在这种情况下, 新插入的评论的"post_id"自动设置为"1".

当处理对多关系时, 你可以使用save方法插入或更新(insert / update)相关模型:

$comments = array(
    array('message' => 'A new comment.'),
    array('message' => 'A second comment.'),
);

$post = Post::find(1);

$post->comments()->save($comments);

插入相关模型 (多对多)

处理多对多的关系时这种方式更有效. 例如, 考虑一个User模型, 有很多的角色. 同样, Role模型可能有许多用户. 因此, 这种关系的中间表有"user_id"和"role_id"字段. 现在, 让我们插入一个新的用户角色:

$role = new Role(array('title' => 'Admin'));

$user = User::find(1);

$user->roles()->insert($role);

然而, 也许你想附加到用户的角色已经存在, 所以你可能往往只需要一个新的记录插入到中间表. 这时使用附加(attach)方法:

$user->roles()->attach($role_id);

另外, 你可以使用'sync'的方法, 该方法接受一个ID数组"同步"中间表数据. 此操作完成后, 只有数组中的ID将存在中间表中(以前的数据会被删除).

$user->roles()->sync(array(1, 2, 3));

操作中间表

正如你所知, 多对多关系需要一个中间表的存在. Eloquent使得维护中间表变得轻而易举. 例如, 假设我们有一个用户模型, 有许多角色. , 同样, 一个角色模式有许多用户. 因此, 中间表包含"user_id"和"role_id"列. 我们可以访问关系的数据透视表:

$user = User::find(1);

$pivot = $user->roles()->pivot();

一旦我们有一个数据透视表的实例, 我们可以使用它, 就像任何其他的Eloquent模型一样:

foreach ($user->roles()->pivot()->get() as $row)
{
    //
}

你也可以访问给定的记录特定的中间表的列:

$user = User::find(1);

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

请注意, 我们查询的各相关模型角色自动分配一个pivot属性. 此属性包含一个模型代表中间表中的记录.

有时你可能想从一个给定的模型关系的中间表中删除所有的记录. 例如, 也许你要删除所有从用户指定的角色. 下面展示如何做到这一点:

$user = User::find(1);

$user->roles()->delete();

请注意, 这并不从"roles"表中删除的角色, 只删除给定用户的角色的中间表的记录.

预先加载

预先加载的存在是为了减轻N +1查询的问题. 究竟是什么这个问题呢? 好吧, 假设每本书都有所属于作者. 我们将描述这种关系, 像这样:

class Book extends Eloquent {

     public function author()
     {
          return $this->belongs_to('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 "books"

SELECT * FROM "authors" WHERE "id" IN (1, 2, 3, 4, 5, ...)

显然, 聪明的预先加载的使用可以大大提高应用程序的性能. 在上面的例子中, 预先加载减少了一半的执行时间.

预先加载多个关系?这个很容易:

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

提示:使用预先加载时, with要在正式查询之前调用.

有时甚至需要级联的预先加载. 例如, 让我们假设我们的作者模型有"联系人"这一关系模型. 我们可以从的Book模型中预先加载这些关系:

$books = Book::with(array('author', 'author.contacts'))->get();

预先加载的条件

有时你可能想进行预先加载时传入条件. 这很简单,这个样子的:

$users = User::with(array('posts' => function($query)
{
    $query->where('title', 'like', '%first%');

}))->get();

在这个例子中, 我们预先加载了用户的文章, 要求文章的"title"字段只包含"first"单词.

Setter和Getter方法

Setters让你用自定义方法来处理属性分配. allow you to handle attribute assignment with custom methods.在属性名称前面增加"set_"属性定义setter方法.

public function set_password($password)
{
    $this->set_attribute('hashed_password', Hash::make($password));
}

调用setter方法和直接给属性赋值的效果一样.

$this->password = "my new password";

Getters 方法非常简单. 他们可以用来在取值之前修改这个值. 定义一个getter方法, 只需在属性名称前面增加"get_".

public function get_published_date()
{
    return date('M j, Y', $this->get_attribute('published_at'));
}

调用getter方法和直接取属性值的效果一样(方法里的其他语句也会执行).

echo $this->published_date;

批量赋值

是传入一个关联数组, 然后从数组中填充数据到. 批量赋值, 可以通过一个数组来初始化构造模型:

$user = new User(array(
    'username' => 'first last',
    'password' => 'disgaea'
));

$user->save();

或者, 批量赋值也可以通过fill方法完成.

$user = new User;

$user->fill(array(
    'username' => 'first last',
    'password' => 'disgaea'
));

$user->save();

默认情况下, 批量赋值的数据存储在属性的键/值对. 然而, 可能创建一个将被设置的属性的白名单. 批量赋值时白名单属性被赋值后其他的属性不会被赋值.

你可以在$accessible静态数组上指定访问属性. 每个属性都被添加打牌白名单.

public static $accessible = array('email', 'password', 'name');

另外, 你可以从你的模型外使用accessible方法:

User::accessible(array('email', 'password', 'name'));

提示: 批量赋值被用作用户输入时应当极为谨慎. 技术的疏忽可能会导致严重的安全漏洞.

Converting Models To Arrays

当构建JSON时, 你会经常需要把模型转换到数组, 这样他们就可以很容易地序列化, 他非常简单.

模型转换为数组:

return json_encode($user->to_array());

to_array方法会自动抓取您的模型上的所有属性, 同样也会装载模型的关系.

有时你可能想限定在你的模型数组中包含的属性, 例如密码(passwords)通常不想显示. 添加hidden来实现:

从数组中排除的属性:

class User extends Eloquent {

    public static $hidden = array('password');

}