模型通常在 app 目录中,但你可以根据 composer.json 文件将他们放置在可以被自动加载的任意位置。所有的 Eloquent 模型都继承至 Illuminate\Database\Eloquent\Model 类。
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 方法来保存新模型,此方法会返回模型实例。不过,在使用之前,你需要在模型上指定 fillable 或 guarded 属性,因为所有的 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');
}
}