模型通常在 app 目录中,但你可以根据 composer.json 文件将他们放置在可以被自动加载的任意位置。所有的 Eloquent 模型都继承至 Illuminate\Database\Eloquent\Model 类。

  1. php artisan make:model Flight m

模型类基本属性

<?php
  namespace App;
  use Illuminate\Database\Eloquent\Model;
  class Flight extends Model {
      /*
      ** 与模型关联的表名 *
      * @var string
      */
      protected $table = 'my_flights';
      //定义主键
      protected $primaryKey = 'flight_id';
      //定义主键是否自增
      public $incrementing = false;
      //定义主键类型
      protected $keyType = 'string';
      //是否自动维护时间戳
      //维护字段为 created_at 和 updated_at
      public $timestamps = false;
      //模型日期列格式
      protected $dateFormat = 'U';
      //自定义维护时间戳的字段
      const CREATED_AT = 'creation_date';
      const UPDATED_AT = 'last_update';
      //数据库连接
      protected $connection = 'connection‐name';
      //默认属性值
      protected $attributes = [
        'delayed' => false,
      ];
  }

模型基本操作

模型检索

当模型和 它关联的数据库表 后,就可以从使用模型类从数据库中查询数据

$flights = App\Flight::where('active', 1) ‐>orderBy('name', 'desc') ‐>take(10) ‐>get();
$flight = App\Flight::where('number', 'FR 900')‐>first();
$flight‐>number = 'FR 456';
$flight‐>refresh();
$flight‐>number; // "FR 900"

Eloquent 的 all 和 get 方法可以查询到多个结果,返回一个 Illuminate\Database\Eloquent\Collection 实例。 Collection 类提供了 大量的辅助函数 来处理 Eloquent 结果。

//筛选数据
$flights = $flights‐>reject(function ($flight) { return $flight‐>cancelled; });
//像数组一样遍历
foreach ($flights as $flight) { echo $flight‐>name; }
// 通过主键检索一个模型...
$flight = App\Flight::find(1); // 检索符合查询限制的第一个模型...
$flight = App\Flight::where('active', 1)‐>first();
$flights = App\Flight::find([1, 2, 3]);
//检索结果不存在时抛出异常
//如果没有捕获异常,会自动返回404响应头
$model = App\Flight::findOrFail(1);
$model = App\Flight::where('legs', '>', 100)‐>firstOrFail();

模型插入/更新

<?php
  namespace App\Http\Controllers;
  use App\Flight;
  use Illuminate\Http\Request;
  use App\Http\Controllers\Controller;
  class FlightController extends Controller {
    /*
    ** 创建一个新的航班实例
    ** @param Request $request
    * @return Response
    */
    public function store(Request $request) {
      // 校验请求...
      $flight = new Flight;
      $flight‐>name = $request‐>name;
      $flight‐>save();
    }
  }

也可以使用 create 方法来保存新模型,此方法会返回模型实例。不过,在使用之前,你需要在模型上指定 fillableguarded 属性,因为所有的 Eloquent 模型都默认不可进行批量赋值。

<?php
  namespace App;
  use Illuminate\Database\Eloquent\Model;
  class Flight extends Model {
    /*
    ** 可以被批量赋值的属性。
    ** @var array
    */
    protected $fillable = ['name'];
    //不可以被批量赋值的属性
    protected $guarded = ['price'];
  }

$flight = App\Flight::create(['name' => 'Flight 10']);
//如果已经有一个模型示例,可以通过FILL赋值
$flight‐>fill(['name' => 'Flight 22']);
//单个更新
$flight = App\Flight::find(1);
$flight‐>name = 'New Flight Name';
$flight‐>save();
//批量更新
App\Flight::where('active', 1)
  ‐>where('destination', 'San Diego')
  ‐>update(['delayed' => 1]); // update 方法接受一个键为字段名称数据为值的数组。

模型删除

App\Flight::destroy(1);
App\Flight::destroy(1, 2, 3);
App\Flight::destroy([1, 2, 3]);
App\Flight::destroy(collect([1, 2, 3]));
//通过模型删除
$deletedRows = App\Flight::where('active', 0)‐>delete();

Eloquent 也可以「软删除」模型。软删除的模型并不是真的从数据库中删除了。事实上,是在模型上设置了 deleted_at 属性并将其值写入数据库。如果 deleted_at 值非空,代表这个模型已被软删除。如果要开启模型软删除功能,你需要在模型上使用 Illuminate\Database\Eloquent\SoftDeletes trait。

<?php
  namespace App;
  use Illuminate\Database\Eloquent\Model;
  use Illuminate\Database\Eloquent\SoftDeletes;
  class Flight extends Model {
    use SoftDeletes;
  }
//执行软删除
Schema::table('flights', function (Blueprint $table) { $table‐>softDeletes(); });
if ($flight‐>trashed()) { // }
$flights = App\Flight::withTrashed() ‐>where('account_id', 1) ‐>get();
$flight‐>history()‐>withTrashed()‐>get();
$flights = App\Flight::onlyTrashed() ‐>where('airline_id', 1) ‐>get()
$flight‐>restore();
App\Flight::withTrashed() ‐>where('airline_id', 1) ‐>restore();
$flight‐>history()‐>restore();
// 强制删除单个模型实例...
$flight‐>forceDelete(); // 强制删除所有相关模型...
$flight‐>history()‐>forceDelete();

模型事件

<?php
  namespace App;
  use App\Events\UserSaved;
  use App\Events\UserDeleted;
  use Illuminate\Notifications\Notifiable;
  use Illuminate\Foundation\Auth\User as Authenticatable;
  class User extends Authenticatable {
    use Notifiable;
    /*
    ** 模型的事件映射
    ** @var array
    */
    protected $dispatchesEvents = [
      'saved' => UserSaved::class,
      'deleted' => UserDeleted::class,
    ];
  }

如果在一个模型上监听了多个事件,可以使用观察者来将这些监听器组织到一个单独的类中。观察者类的方法名映射到你希望监听的 Eloquent 事件。 这些方法都以模型作为其唯一参数。 make:observer Artisan 命令可以快速建立新的观察者类。

php artisan make:observer UserObserver ‐‐model=User
<?php
  namespace App\Observers;
  use App\User;
  class UserObserver {
    /*
    ** 处理 User「新建」事件
    ** @param \App\User $user
    * @return void
    */
    public function created(User $user) {
      //
    }
    /*
    ** 处理 User「更新」事件
    ** @param \App\User $user
    * @return void
    */
    public function updated(User $user) {
      //
    }
    /*
    ** 处理 User「删除」事件
    ** @param \App\User $user
    * @return void
    */
    public function deleted(User $user) {
      //
    }
  }
<?php
  namespace App\Providers;
  use App\User;
  use App\Observers\UserObserver;
  use Illuminate\Support\ServiceProvider;
  class AppServiceProvider extends ServiceProvider {
    /*** 注册服务提供者 ** @return void */
    public function register() {
      //
    }
    /*** 引导应用程序服务. ** @return void */
    public function boot() {
      User::observe(UserObserver::class);
    }
  }

模型关联

<?php
  namespace App;
  use Illuminate\Database\Eloquent\Model;
  class User extends Model {
      /*
      ** 获取与用户关联的电话记录。
      ** 使用用户表的UserUserID关联电话表的PhoneUserID
      */
      public function phone() {
        //$this‐>hasOne('App\Phone', 'foreign_key', 'local_key');
        return $this‐>hasOne('App\Phone', 'PhoneUserID', 'UserUserID);
      }
      /*
      ** 获取拥有该号码的用户。
      ** 使用用户表的UserPhone关联电话表的Phone
      */
      public function userHasphone() {
        //反向关联
        return $this‐>belongsTo('App\User', 'Phone', 'UserPhone');
      }
}
<?php
  namespace App;
  use Illuminate\Database\Eloquent\Model;
  class Post extends Model {
    /*** 获取博客文章的评论 */
    public function comments() {
      return $this‐>hasMany('App\Comment');
    }
    /*** 方向关联 */
    public function post() {
      return $this‐>belongsTo('App\Post');
    }
  }
//增加筛选条件
$comment = App\Post::find(1)‐>comments()‐>where('title', 'foo')‐>first();
<?php
  namespace App;
  use Illuminate\Database\Eloquent\Model;
  class User extends Model {
    /*** 用户拥有的角色 */
    public function roles() {
      return $this‐>belongsToMany('App\Role');
    }
  }
<?php
  namespace App;
  use Illuminate\Database\Eloquent\Model;
  class Role extends Model {
    /*** 拥有此角色的所有用户 */
    public function users() {
      return $this‐>belongsToMany('App\User')‐>using('App\RoleUser');
    }
  }
<?php
  namespace App;
  use Illuminate\Database\Eloquent\Relations\Pivot;
  class RoleUser extends Pivot {
    //
  }
<?php
  namespace App;
  use Illuminate\Database\Eloquent\Model;
  class Role extends Model {
    /*** 拥有此角色的用户。 */
    public function users() {
      return $this‐>belongsToMany('App\User')
        ‐>using('App\RoleUser')
        ‐>withPivot([ 'created_by', 'updated_by', ]);
    }
  }
// 获取至少存在一条评论的所有文章...
$posts = App\Post::has('comments')‐>get();
// 获取评论超过三条的文章...
$posts = App\Post::has('comments', '>=', 3)‐>get();
// 获取拥有至少一条带有投票评论的文章...
$posts = App\Post::has('comments.votes')‐>get();

use Illuminate\Database\Eloquent\Builder;
// 获取至少带有一条评论内容包含 foo% 关键词的文章...
$posts = App\Post::whereHas('comments', function (Builder $query) {
  $query‐>where('content', 'like', 'foo%');
})‐>get();
// 获取至少带有十条评论内容包含 foo% 关键词的文章...
$posts = App\Post::whereHas('comments', function (Builder $query) {
  $query‐>where('content', 'like', 'foo%');
}, '>=', 10)‐>get();

当以属性方式访问 Eloquent 关联时,关联数据「懒加载」。这意味着直到第一次访问属性时关联数据才会被真实加载。不过 Eloquent 能在查询父模型时「预先载入」子关联。预加载可以缓解 N + 1 查询问题。为了说明 N + 1 查询问题,考虑 Book 模型关联到 Author 的情形:

<?php
  namespace App;
  use Illuminate\Database\Eloquent\Model;
  class Book extends Model {
    /*** 获取书籍作者。 */
    public function author() {
      return $this‐>belongsTo('App\Author');
    }
  }
//获取所有书籍
$books = App\Book::all();
foreach ($books as $book) {
  echo $book‐>author‐>name;
}

此循环将执行一个查询,用于获取全部书籍,然后为每本书执行获取作者的查询。如果我们有 25 本书,此循环将运行 26 个查询:1 个用于查询书籍,25 个附加查询用于查询每本书的作者。
可以使用预加载将操作压缩到只有 2 个查询。在查询时,可以使用 with 方法指定想要预加载的关联:

$books = App\Book::with('author')‐>get();
foreach ($books as $book) {
  echo $book‐>author‐>name;
}
//等效于
select * from books
select * from authors where id in (1, 2, 3, 4, 5, ...)

//预加载多个实例
$books = App\Book::with(['author', 'publisher'])‐>get();
$comment = new App\Comment(['message' => 'A new comment.']);
$post = App\Post::find(1);
$post‐>comments()‐>save($comment);

//保存多个关联表
$post = App\Post::find(1);
$post‐>comments()‐>saveMany([
   new App\Comment(['message' => 'A new comment.']),
   new App\Comment(['message' => 'Another comment.']),
]);

$post = App\Post::find(1);
//插入单个数据
$comment = $post‐>comments()‐>create([ 'message' => 'A new comment.', ]);
//插入多个数据
$post‐>comments()‐>createMany([
  [ 'message' => 'A new comment.', ],
  [ 'message' => 'Another new comment.', ],
]);

当更新 belongsTo 关联时,可以使用 associate 方法。此方法将会在子模型中设置外键:

$account = App\Account::find(10);
$user‐>account()‐>associate($account);
$user‐>save();

当移除 belongsTo 关联时,可以使用 dissociate 方法。此方法会将关联外键设置为 null :

$user‐>account()‐>dissociate();
$user‐>save();

Eloquent 也提供了一些额外的辅助方法,使相关模型的使用更加方便。例如,我们假设一个用户可以拥有多个角色,并且每个角色都可以被多个用户共享。给某个用户附加一个角色是通过向中间表插入一条记录实现的,可以使用 attach 方法完成该操作:

$user = App\User::find(1);
$user‐>roles()‐>attach($roleId);
// 移除用户的一个角色...
$user‐>roles()‐>detach($roleId);
// 移除用户的所有角色...
$user‐>roles()‐>detach();

访问器

<?php namespace App;
  use Illuminate\Database\Eloquent\Model;
  class User extends Model {
    /*
    ** 获取用户的姓名.
    ** @param string $value
    * @return string
    */
    public function getFirstNameAttribute($value) {
      return ucfirst($value);
    }
  }

//访问访问器属性
$user = App\User::find(1);
$firstName = $user‐>first_name;
/*** 获取用户的姓名. ** @return string */
public function getFullNameAttribute() {
  return "{$this‐>first_name} {$this‐>last_name}";
}

//访问访问器属性
$user = App\User::find(1);
$firstName = $user‐>full_name;

修改器

<?php
  namespace App;
  use Illuminate\Database\Eloquent\Model;
  class User extends Model {
    /*
    ** 设置用户的姓名.
    ** @param string $value
    * @return void
    */
    public function setFirstNameAttribute($value) {
      $this‐>attributes['first_name'] = strtolower($value);
    }
  }
<?php
  namespace App;
  use Illuminate\Database\Eloquent\Model;
  class User extends Model {
    /*
    ** 应该转换为日期格式的属性.
    ** @var array
    */
    protected $dates = [ 'seen_at'];
    //设置日期格式
    protected $dateFormat = 'U';
  }
<?php
  namespace App;
  use Illuminate\Database\Eloquent\Model;
  class User extends Model {
    /*
    ** 这个属性应该被转换为原生类型.
    ** @var array
    */
    protected $casts = [
      'is_admin' => 'boolean',
      'options'  => 'array',
      'created_at' => 'datetime:Y‐m‐d','
    ];
  }

API资源

当构建 API 时,你往往需要一个转换层来联结你的 Eloquent 模型和实际返回给用户的 JSON 响应。Laravel 的资源类能够让你以更直观简便的方式将模型和模型集合转化成 JSON。

php artisan make:resource User
<?php
  namespace App\Http\Resources;
  use Illuminate\Http\Resources\Json\JsonResource;
  class User extends JsonResource {
    /*
    ** 将资源转换成数组。
    ** @param \Illuminate\Http\Request $request
    * @return array
    */
    public function toArray($request) {
      return [
        'id' => $this‐>id,
        'name' => $this‐>name,
        'email' => $this‐>email,
        'created_at' => $this‐>created_at,
        'updated_at' => $this‐>updated_at,
      ];
    }
  }
use App\User;
use App\Http\Resources\User as UserResource;
Route::get('/user', function () {
  return new UserResource(User::find(1));
})
/*
** 将资源转换成数组。
** @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request) {
  return [
    'id' => $this‐>id,
    'name' => $this‐>name,
    'email' => $this‐>email,
    'posts' => PostResource::collection($this‐>posts),
    'created_at' => $this‐>created_at,
    'updated_at' => $this‐>updated_at,
  ];
}

资源是将单个模型转换成数组,而资源集合是将多个模型的集合转换成数组。所有的资源都提供了一个 collection 方法来生成一个 「临时」 资源集合,所以没有必要为每一个模型类型都编写一个资源集合类:

use App\User;
use App\Http\Resources\User as UserResource;
Route::get('/user', function () {
  return UserResource::collection(User::all());
});

不过,如果需要自定义返回集合的元数据,则仍需要定义一个资源集合:

<?php
  namespace App\Http\Resources;
  use Illuminate\Http\Resources\Json\ResourceCollection;
  class UserCollection extends ResourceCollection {
    /*
    ** 将资源集合转换成数组
    ** @param \Illuminate\Http\Request $request
    * @return array
    */
    public function toArray($request) {
      return [
        'data' => $this‐>collection,
        'links' => [
          'self' => 'link‐value',
        ],
      ];
    }
  }
/*
** 将资源转换成数组
** @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request) {
  return [
    'id' => $this‐>id,
    'name' => $this‐>name,
    'email' => $this‐>email,
    //只有isAdmin返回是true才会返回secret值
    'secret' => $this‐>when(Auth::user()‐>isAdmin(), 'secret‐value'),
    'created_at' => $this‐>created_at,
    'updated_at' => $this‐>updated_at,
  ];
}
/*
** 将资源转换成数组
** @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request) {
  return [
    'id' => $this‐>id,
    'name' => $this‐>name,
    'email' => $this‐>email,
    $this‐>mergeWhen(Auth::user()‐>isAdmin(),
    [
        'first‐secret' => 'value',
        'second‐secret' => 'value',
    ]),
    'created_at' => $this‐>created_at,
    'updated_at' => $this‐>updated_at,
  ];
}
<?php
  namespace App\Http\Resources;
  use Illuminate\Http\Resources\Json\JsonResource;
  class User extends JsonResource {
    /*
    ** 资源转换成数组
    ** @param \Illuminate\Http\Request $request
    * @return array
    */ 
    public function toArray($request) {
      return [ 'id' => $this‐>id, ];
    }
    /*
    ** 自定义响应
    ** @param \Illuminate\Http\Request $request
    * @param \Illuminate\Http\Response $response
    * @return void
    */
    public function withResponse($request, $response) {
      $response‐>header('X‐Value', 'True');
    }
  }