[Laravel] 16 - DB: Eloquent

前言


一、大纲

写后端API,与数据库打交道无疑是很重要的角色。

PHP数据库操作:从MySQL原生API到PDO

PHP数据库操作:使用ORM

Ref: [PHP] 07 - Json, XML and MySQL

 

 

二、细节

SQL 教程

 

 

三、初识 Eloquent ORM

Eloquent ORM是Laravel框架使用的ORM。Laravel 的 Eloquent ORM 提供了更优雅的ActiveRecord 实现来和数据库的互动。每个数据库表对应一个模型文件。

Goto: Eloquent: 入门【2.完整复习版本】

From: Eloquent ORM笔记【1.基础篇】

新增
# 新建了一条 id, name
$user = new User; $user->name = 'John'; $user->save(); $insertedId = $user->id;//从对象取得 id 属性值

使用模型的 Create 方法 class User extends Model { protected $guarded = ['id', 'account_id'];  //黑名单,不会被更新 } // 建立一个用户 $user = User::create(['name' => 'John']); // 以属性找用户 - 若没有则新增并取得新的实例... $user = User::firstOrCreate(['name' => 'John']); // 以属性找用户 - 若没有则建立新的实例... $user = User::firstOrNew(['name' => 'John']);


辨析
firstOrCreate:判断之后直接入库
firstOrNew :判断之后还要做其他业务流程,之后再入库
$student=Student::firstOrNew(['vip_name'=>'mmm']); 
$student->save();  # 就是需要再单独save一下

删除
$this->where($where)->delete(); 或者 $user = User::find(1); $user->delete();


更新
return $this->where($where)->update($data); 或者 $user = User::find(1); $user->update($data);


查找
//取出所有记录 - 可以遍历 $this->all()->toArray(); //取出一条数据 $one = $this->find('2'); return array( $one->id, $one->title, $one->content, ); //查找id=2的第一条数据 $this->where('id', 2)->first()->toArray(); //查找id>0的所有数据 $this->where('id', '>', '0')->get()->toArray();
  • For find(n), you retrieve a row based on the primary key which is 'n'.
  • For first(), you retrieve the first row among all rows that fit the where clauses.
  • For get()  , you retrieve all the rows that fit the where clauses. (Please note that loops are required to access all the rows or you will get some errors).
//降序排列                                                                                      
$this->where('id', '>', '0')->orderBy('id', 'desc')->get()->toArray();

//降序排列,计数                                                                                 
$this->where('id', '>', '0')->orderBy('id', 'desc')->count();

//从offset起始算起的limit条数据                                                                   
$this->where('id', '>', '0')->orderBy($order[0], $order[1])->skip($offset)->take($limit);
//等同于
$this->where('id', '>', '0')->orderBy($order[0], $order[1])->offset($offset)->limit($limit);


条件类
//条件类 where('id', '>', '0') where('id', '>=', '0') where('id', '<', '0') where('id', '<=', '0') where('id', 'like', 'name%') whereIn($key, $array) whereNotIn($key, $array) whereBetween($key, $array) whereNotBetween($key, $array) orWhereIn($key, $array) orWhereNotIn($key, $array) orWhereBetween($key, $array) orWhereNotBetween($key, $array)


结果方法
//结果方法:Illuminate\Database\Query\Builder first()取第一个 get()取所有 all()取所有(无条件)


数学统计
//聚合方法 count() avg() sum() max() min()

 

 

  

Pure SQL


RDBMS 指关系型数据库管理系统,全称 Relational Database Management System。

(1). 登录:mysql -u root -p 

(2). phpMyAdmin创建数据库,并导入.sql文件。

(3). 支持中文:set names utf8;

 

详见:[MySQL] 01- Basic sql 

 

 

 

Eloquent: 入门


{tip} 由于 Eloquent 模型是查询构造器,因此你应当去阅读所有 查询构造器 中可用的方法。

 

一、模型

  • 生成模型、迁移文件
php artisan make:model User --migration
php artisan make:model User -m 
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;  # 所有的 Eloquent 模型都继承自这个

class Flight extends Model
{
    // 用它从 flights 数据表中取回与保存信息
}

 

  • 默认常用四属性

[1] 表名

除非数据表明确地指定了其它名称,否则将使用类的「蛇形名称」、复数形式名称来作为数据表的名称。自定义对应表名:

protected $table = 'my_flights'; 

 

 [2] 主键

设每个数据表都有一个叫做 id 的主键字段。你也可以定义一个 $primaryKey 属性来重写这个约定。

默认是递增;否则,必须在你的模型 public $incrementing=false

 

[3] 时间戳

默认情况下,Eloquent 会认为在你的数据库表有 created_at 和 updated_at 字段。

如果你不希望让 Eloquent 来自动维护这两个字段,可在模型内将 $timestamps=false

决定了日期应如何在数据库中存储,以及当模型被序列化成数组或 JSON 格式 protected $dateFormat = 'U';

 

[4] 连接数据库 

默认情况下,所有的 Eloquent 模型会使用应用程序中默认的数据库连接设置。

如果你想为模型指定不同的连接,可以使用 protected $connection = 'connection-name'; 

 

[5] 通过模型获取结果

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

 

  • 模型创建与删除

详见:"三、初识 Eloquent ORM"

补充:软删除

模型上设置一个 deleted_at 属性并将其添加到数据库。如果模型有一个非空值 deleted_at,代表模型已经被软删除了。

[1] 设置字段

当模型被软删除时,它们并不会真的从数据库中被移除。而是会在模型上设置一个 deleted_at 属性并将其添加到数据库。

如果模型有一个非空值 deleted_at,代表模型已经被软删除了。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Flight extends Model
{
    use SoftDeletes;

    /**
     * 需要被转换成日期的属性。
* 添加 deleted_at 字段到你的 $dates 属性。 * * @var array
*/ protected $dates = ['deleted_at']; }

 

[2] 添加 deleted_at 字段到数据表 

Schema::table('flights', function ($table) {
    $table->softDeletes();
});

当查询有启用软删除的模型时,被软删除的模型将会自动从所有查询结果中排除。

 

[3] 查询 deleted_at 字段

确认指定的模型实例是否已经被软删除,可以使用 trashed 方法:

if ($flight->trashed()) {
    //
}

 

[4] 查询捎带上 '软删除‘ 集合

$flights = App\Flight::withTrashed()
                ->where('account_id', 1)
                ->get();

 

[5] 只查询 ‘软删除’ 集合

$flights = App\Flight::onlyTrashed()
                ->where('airline_id', 1)
                ->get();

 

[6] 恢复 '软删除'

$flight->restore();

App\Flight::withTrashed() ->where('airline_id', 1) ->restore();

 

[7] 永久地删除模型

// 强制删除单个模型实例...
$flight->forceDelete();

// 强制删除所有相关模型...
$flight->history()->forceDelete();

 

 

二、集合

Collection 类提供 多种辅助函数 来处理你的 Eloquent 结果。 

 

  • 遍历 Chunk & Cursor 

[Chunk]

It will "paginate" your query, this way you use less memory. (1) Uses less memory (2) It takes longer

public function getData() {
    Contact::chunk(1000, function ($contacts) {
        foreach ($contacts as $contact) {
            //rest of your code...
        }
    });
}

[Cursor]

You will use PHP Generators to search your query items one by one. (1) It takes less time (2) Uses more memory

public function getData() {
    foreach (Contact::cursor() as $contact) {
        //rest of your code...
    }
}

 

  • 返回指定行
// 取回符合查询限制的第一个模型 ...
$flight  = App\Flight::where('active', 1)->first();

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

  

  • ‘未找到’ 处理
$model = App\Flight::findOrFail(1);
$model = App\Flight::where('legs', '>', 100)->firstOrFail();

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

 

  • 聚合函数

可以使用 countsummax和其它 查询构造器 提供的 聚合函数

这些方法会返回适当的标量值,而不是一个完整的模型实例

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

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

 

 

三、控制器

  • 添加一条记录

自动完成了例如对时间戳的记录。

<?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(); } }

 

  • 更新 & 批量更新
$flight = App\Flight::find(1);

$flight->name = 'New Flight Name';
$flight->save();

两个where并列条件,然后更新字段:delayed

App\Flight::where('active', 1)
          ->where('destination', 'San Diego')
          ->update(['delayed' => 1]);

{note} 当通过“Eloquent”批量更新时,savedupdated模型事件将不会被更新后的模型代替。这是因为批量更新时,模型从来没有被取回。

 

  • 批量赋值

注意批量赋值是有风险的,所有的 Eloquent 模型都针对批量赋值(Mass-Assignment)做了保护。

方案:$fillable 作为一个可以被批量赋值的属性「白名单」;$guarded 是「黑名单」,设置为空代表大门敞开。 

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * 可以被批量赋值的属性。
* 使 Flight 模型的 name 属性可以被批量赋值 * * @var array */
protected $fillable = ['name']; }

赋值:

$flight = App\Flight::create(['name' => 'Flight 10']);

$flight->fill(['name' => 'Flight 22']);

 

 

四、查询作用域

  • 编写 全局作用域

自定义全局作用域很简单,首先定义一个实现 Illuminate\Database\Eloquent\Scope 接口的类,该接口要求你实现一个方法:apply

<?php

namespace App\Scopes;

use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class AgeScope implements Scope
{
    /**
     * 应用作用域
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     * @param  \Illuminate\Database\Eloquent\Model    $model
     * @return void
     */
    public function apply(Builder $builder, Model $model)
    {
        return $builder->where('age', '>', 200);              # select * from `users` where `age` > 200
    }
}

{tip} Laravel 没有规定你需要把这些类放置于哪个文件夹,你可以自由在 app 文件夹下创建 Scopes 文件夹来存放。

 

  • 应用全局作用域
<?php

namespace App;

use App\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 数据模型的启动方法
     *
     * @return void
     */
    protected static function boot()                    # 需要重写给定模型的 boot 方法
    {
        parent::boot();
        static::addGlobalScope(new AgeScope);            # 并使用 addGlobalScope 方法
    }
}

添加作用域后,如果使用 User::all() 查询则会生成如下SQL语句:select * from `users` where `age` > 200

 

  • 匿名的全局作用域

Eloquent 还允许我们使用闭包定义全局作用域,这在实现简单作用域的时候特别有用,这样的话,我们就没必要定义一个单独的类。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class User extends Model
{
    /**
     * 数据模型的启动方法
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();
        static::addGlobalScope('age', function(Builder $builder) {
            $builder->where('age', '>', 200);
        });
    }
}

 

  • 移除 全局作用域
User::withoutGlobalScope('age')->get();

User::withoutGlobalScope(AgeScope::class)->get();

// 移除多个
User::withoutGlobalScopes([FirstScope::class, SecondScope::class])->get();

  

  • 本地作用域

本地作用域允许我们定义通用的约束集合以便在应用中复用。

例如,你可能经常需要获取最受欢迎的用户,要定义这样的一个作用域,只需在对应 Eloquent 模型方法前加上一个 scope 前缀,作用域总是返回查询构建器:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 限制查询只包括受欢迎的用户。
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopePopular($query)
    {
        return $query->where('votes', '>', 100);
    }

    /**
     * 限制查询只包括活跃的用户。
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeActive($query)
    {
        return $query->where('active', 1);
    }
}

  

Then, 利用查询范围

一旦定义了范围,则可以在查询模型时调用范围方法。在进行方法调用时不需要加上 scope 前缀。你甚至可以链式调用不同的范围,如:

$users = App\User::popular()->active()->orderBy('created_at')->get();

    

  • 动态范围

定义一个可接受参数的范围。

这时只需给你的范围加上额外的参数即可。范围参数应该被定义在 $query 参数之后:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 限制查询只包括指定类型的用户。
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeOfType($query, $type)
    {
        return $query->where('type', $type);
    }
}

在范围调用时传递参数:

$users = App\User::ofType('admin')->get();

 

  

五、事件

  • 模型的事件映射

事件让你每当有特定的模型类在数据库保存或更新时,执行代码。

Eloquent 模型会触发许多事件,让你在模型的生命周期多个时间点进行监控:creatingcreatedupdatingupdatedsavingsaveddeletingdeletedrestoringrestored。

 

举例子:

当一个新模型被初次保存将会触发 creating 以及 created 事件

如果一个模型已经存在于数据库且调用了 save 方法,将会触发 updating 和 updated 事件

这两种情况下都会触发 saving 和 saved事件

<?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 $events = [
        'saved'   => UserSaved::class,
        'deleted' => UserDeleted::class,
    ];
}

  

  • 观察者

如果你在一个给定的模型中监听许多事件,您可以使用观察者将所有监听器变成一个类

观察者类里的方法名应该反映Eloquent想监听的事件。 每种方法接收 model 作为其唯一的参数。

Laravel不包括观察者默认目录,所以你可以创建任何你喜欢你的目录来存放:

<?php

namespace App\Observers;

use App\User;

class UserObserver
{
    /**
     * 监听用户创建的事件。
     *
     * @param  User  $user
     * @return void
     */
    public function created(User $user)
    {
        //
    }

    /**
     * 监听用户删除事件。
     *
     * @param  User  $user
     * @return void
     */
    public function deleting(User $user)
    {
        //
    }
}

AppServiceProvider注册观察者

<?php

namespace App\Providers;

use App\User;
use App\Observers\UserObserver;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * 运行所有应用.
     *
     * @return void
     */
    public function boot()
    {
        User::observe(UserObserver::class);
    }

    /**
     * 注册服务提供.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

  

Ref: Laravel 数据库之:数据库请求构建器【日后继续补充】

 

posted @ 2018-07-13 17:18  郝壹贰叁  阅读(512)  评论(0编辑  收藏  举报