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

翻译、衍生自: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');

本文章首发在 Laravel China 社区