ThinkPHP5模型的扩展应用

Published on 2016 - 12 - 20

查询范围

可以对模型的查询和写入操作进行封装,例如:

namespace app\index\model;

use think\Model;

class User extends Model
{
    protected function scopeThinkphp($query)
    {
        $query->where('name','thinkphp')->field('id,name');
    }

    protected function scopeAge($query)
    {
        $query->where('age','>',20)->limit(10);
    }    
}

就可以进行下面的条件查询:

// 查找name为thinkphp的用户
User::scope('thinkphp')->get();
// 查找年龄大于20的10个用户
User::scope('age')->all();
// 查找name为thinkphp的用户并且年龄大于20的10个用户
User::scope('thinkphp,age')->all();

可以直接使用闭包函数进行查询,例如:

User::scope(function($query){
    $query->where('age','>',20)->limit(10);
})->all();

支持动态调用的方式,例如:

$user = new User;
// 查找name为thinkphp的用户
$user->thinkphp()->get();
// 查找年龄大于20的10个用户
$user->age()->all();
// 查找name为thinkphp的用户并且年龄大于20的10个用户
$user->thinkphp()->age()->all();

命名范围方法之前不能调用查询的连贯操作方法,必须首先被调用。

全局查询范围

如果你的所有查询都需要一个基础的查询范围,那么可以在模型类里面定义一个静态的base方法,例如:

namespace app\index\model;

use think\Model;

class User extends Model
{
    // 定义全局的查询范围
    protected function base($query)
    {
        $query->where('status',1);
    }
}

全局查询范围方法在5.0.2版本之前必须定义为static静态方法。

然后,执行下面的代码:

$user = User::get(1);

最终的查询条件会是

status = 1 AND id = 1

如果需要动态关闭/开启全局查询访问,可以使用:

// 关闭全局查询范围
User::useGlobalScope(false)->get(1);
// 开启全局查询范围
User::useGlobalScope(true)->get(2);

模型分层

ThinkPHP支持模型的分层 ,除了Model层之外,我们可以根据项目的需要设计和创建其他的模型层。

通常情况下,不同的分层模型仍然是继承系统的\think\Model类或其子类,所以,其基本操作和Model类的操作是一致的。

例如在index模块的设计中需要区分数据层、逻辑层、服务层等不同的模型层,我们可以在模块目录下面创建model、logic和service目录,把对用户表的所有模型操作分成三层:

  • 数据层:app\index\model\User 用于定义数据相关的自动验证和自动完成和数据存取接口
  • 逻辑层:app\index\logic\User 用于定义用户相关的业务逻辑
  • 服务层:app\index\service\User 用于定义用户相关的服务接口等

三个模型层的定义如下:

app\index\model\User.php

namespace app\index\model;

use think\Model;

class User extends Model
{
}

实例化方法:\think\Loader::model('User')

Logic类:app\index\logic\User.php

namespace app\index\logic;

use think\Model;

class User extends Model
{
}

实例化方法:\think\Loader::model('User','logic');

Service类:app\index\service\User.php

namespace app\index\service;

use think\Model;

class User extends Model
{
}

实例化方法:\think\Loader::model('User','service');

数组访问和转换

数组访问

模型对象支持数组方式访问,例如:

$user = User::find(1);
echo $user->name ; // 有效
echo $user['name'] // 同样有效
$user->name = 'thinkphp'; // 有效
$user['name'] = 'thinkphp'; // 同样有效
$user->save();

转换为数组

可以使用toArray方法将当前的模型实例输出为数组,例如:

$user = User::find(1);
dump($user->toArray());

支持设置不输出的字段属性:

$user = User::find(1);
dump($user->hidden(['create_time','update_time'])->toArray());

数组输出的字段值会经过获取器的处理。

也可以支持追加其它获取器定义的字段,例如:

$user = User::find(1);
dump($user->append(['status_text'])->toArray());

支持设置允许输出的属性,例如:

$user = User::find(1);
dump($user->visible(['id','name','email'])->toArray());

JSON序列化

可以调用模型的toJson方法进行JSON序列化

$user = User::get(1);
echo $user->toJson();

可以设置无需输出的字段,例如:

$user = User::get(1);
echo $user->hidden(['create_time','update_time'])->toJson();

或者追加其它的字段:

$user = User::get(1);
echo $user->append(['status_text'])->toJson();

设置允许输出的属性:

$user = User::get(1);
echo $user->visible(['id','name','email'])->toJson();

模型对象可以直接被JSON序列化,例如:

echo json_encode(User::get(1));

输出结果类似于:

{"id":"1","name":"","title":"","status":"1","update_time":"1430409600","score":"90.5"}

或者也可以直接echo 一个模型对象,例如:

echo User::get(1);

输出的结果和上面是一样的。

事件

模型类支持before_delete、after_delete、before_write、after_write、before_update、after_update、before_insert、after_insert事件行为,使用方法如下:

User::event('before_insert',function($user){
    if($user->status != 1){
        return false;
    }
});

注册的回调方法支持传入一个参数(当前的模型对象实例),并且before_write、before_insert、 before_update 、before_delete事件方法如果返回false,则不会继续执行。

支持给一个位置注册多个回调方法,例如:

User::event('before_insert',function($user){
    if($user->status != 1){
        return false;
    }
});
// 注册回调到beforeInsert函数
User::event('before_insert','beforeInsert');

可以在模型类的init方法里面统一注册模型事件,例如:

namespace app\index\model;

use think\Model;

class User extends Model
{
    protected static function init()
    {
      User::event('before_insert',function($user){
          if($user->status != 1){
              return false;
          }
      });
    }
}

参考文档