Laravel 文档阅读:Eloquent 起步(上篇)
20

翻译、衍生自:https://laravel-china.org/docs/laravel/5.4/eloquent

简介

Laravel 的 ORM 实现称为「Eloquent」,又称「Eloquent ORM」。Laravel 中的 Model 是 Eloquent 功能的载体,称为 「Eloquent Model」。一个 Model 对应的是数据库里的一张表,与 Model 的交互,就相当于是和数据库表交互。

在开始讲 Eloquent 前,先要把数据库配置文件 config/database.php 写好了,不会的话,就看 文档

定义模型

Laravel 中的 Model 放在 app 目录下,当然你可以按你的心意放,只要 Composer 自动加载的时候能找到就 OK。所有的 Eloquent Model 扩展自 Illuminate\Database\Eloquent\Model 类。

创建 Model 最简单的方式就是使用 Artisan 命令 make:model

php artisan make:model User

如果在创建 Model 的时候,想顺便把迁移文件也创建喽,命令里加上 --migration 或者 -m 选项就可以了:

php artisan make:model User --migration

php artisan make:model User -m

Eloquent Model 约定

我们以 Flight Model 为例,默认,它会从我们的 flights 数据库表里查询数据:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    //
}

表格名

注意,我们并没有告诉 Flight Model 要用哪张表。按照约定,类名的「snake_case」& 复数形式就是 Model 默认使用的表名,除非你在 Model 中手动通过 table 属性指定了表名:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'my_flights';
}

主键

Eloquent 默认认为每张表的主键字段名都是 id,当然,这也可以在 Model 中设定 $primaryKey 属性覆盖这一约定。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The primary key for the model.
     *
     * @var string
     */
    protected $primaryKey = 'my_id';
}

此外,Eloquent 默认认为主键都是自增的数值类型的。如果你使用的不是自增的或者非数值类型的主键字段,必须 要在 Model 设置里设置 $incrementingfalse

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * Indicates if the IDs are auto-incrementing.
     *
     * @var bool
     */
    public $incrementing = false;
}

时间戳

默认,Eloquent 认为每张表里还有 created_atupdated_at 字段。如果你的表格里不需要这两个字段,要在 Model 里将 $timestamps 属性设置为 false

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * Indicates if the model should be timestamped.
     *
     * @var bool
     */
    public $timestamps = false;
}

如果要自定义时间戳保存在数据库里的格式,就需要设置 $dateFormat 属性。这个属性决定时间戳字段保存在数据库里的格式,还有保存在序列化数组 / JSON 里的格式:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The storage format of the model's date columns.
     *
     * @var string
     */
    protected $dateFormat = 'U';
}

如果你表格里创建字段不叫 created_at,或更新字段不叫 updated_at,那也是可以设置的。这是通过在 Model 中设置 CREATED_ATUPDATED_AT 属性实现的:

<?php

class Flight extends Model
{
    const CREATED_AT = 'creation_date';
    const UPDATED_AT = 'last_update';
}

数据库连接

Eloquent 默认使用的是默认的数据库连接。如果要为一个 Model 设定不同的连接,通过 $connection 属性实现:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The connection name for the model.
     *
     * @var string
     */
    protected $connection = 'connection-name';
}

获取模型

一旦你创建了 Model 和关联的数据库表,就可以从数据库表里取数据啦!别忘记 Eloquent Model 可以当查询语句构造器使用的呀。

<?php

use App\Flight;

$flights = App\Flight::all();

foreach ($flights as $flight) {
    echo $flight->name;
}

添加额外约束条件

Eloquent all 方法可以获得数据库表里所有数据。当 Eloquent Model 作为查询语句构造器使用,添加了查询条件后,就要用 get 方法获得数据了呦。

$flights = App\Flight::where('active', 1)
               ->orderBy('name', 'desc')
               ->take(10)
               ->get();

Collection 实例

Eloquent 方法的 allget 方法获得的结果,是 Illuminate\Database\Eloquent\Collection 实例,Collection 类提供了 大量丰富的有用方法

$flights = $flights->reject(function ($flight) {
    return $flight->cancelled;
});

你也可以像循环数组那样循环 Collection 实例:

foreach ($flights as $flight) {
    echo $flight->name;
}

分块输出数据

处理数以千计的 Eloquent 记录,可以使用 chunk 方法。chunk 方法一次取出指定数量的记录,然后交给 Closure 处理。在处理大数量数据时,可以节省不少内存空间:

Flight::chunk(200, function ($flights) {
    foreach ($flights as $flight) {
        //
    }
});

chunk 方法的第一个参数是每次分块取出的记录条数,第二个参数是一个闭包,每次取出的数据就是在这里处理的。每次向数据库请求分块数据时,都要向数据库发起一次请求。类似这样:

select * from `flights` order by `flights`.`id` asc limit 200 offset 0  
select * from `flights` order by `flights`.`id` asc limit 200 offset 200  
select * from `flights` order by `flights`.`id` asc limit 200 offset 400  

使用游标

cursor 方法允许你用游标来遍历数据库数据记录,而且只执行一个 SQL 查询。当处理大量数据时,cursor 方法可以用来极大地减少内存的消耗:

foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
    //
}

获得单个 Model / 聚合数据

除了获得所有表格数据,还可以用 findfirst 方法获得一条数据。

// Retrieve a model by its primary key...
$flight = App\Flight::find(1);

// Retrieve the first model matching the query constraints...
$flight = App\Flight::where('active', 1)->first();

find 方法还接收由主键值组成的数组,返回对应的匹配记录集合:

$flights = App\Flight::find([1, 2, 3]);

Not Found 异常

有时,当一个 Model 没找到时,我们希望抛出一个异常,这在路由和控制器类里是非常有用的。这时可以使用 findOrFail 或者 firstOrFail 方法,它们也是从数据库里获得一条数据,但是与 findfirst 方法不同的是,如果没找到结果的话,一个 Illuminate\Database\Eloquent\ModelNotFoundException 异常会被抛出:

$model = App\Flight::findOrFail(1);

$model = App\Flight::where('legs', '>', 100)->firstOrFail();

如果异常未被捕捉,一个 404 HTTP 响应就会自动发送给用户。

Route::get('/api/flights/{id}', function ($id) {
    return App\Flight::findOrFail($id);
});

获得聚合数据

你也可以使用 countsummax 或者查询语句构造器提供的什么其他方法。注意,这些方法返回是对应适当的值而不是完整的 Model 实例:

$count = App\Flight::where('active', 1)->count();

$max = App\Flight::where('active', 1)->max('price');
本帖由 Summer 于 1年前 加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 4

使用油标的那个foreach每次都要查询一次吧?

1年前
zhangbao

@shaowangzhen 不是的,只有一个查询语句。

1年前
Summer

总结的很棒

1年前
zhangbao

@Summer 其实这里面有我自己理解的部分,因为有时如果按照文档翻译的话,会不太好懂,所以有些地方就按照自己理解的方式说,这样日后查看起来会很方便,但大部分内容还是忠于原文档的。

如果里面有理解不到位的地方,还望指正呀 :)

1年前

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!