Package Data | |
---|---|
Maintainer Username: | RagPanda |
Maintainer Contact: | ragpanda@163.com (Keith.Wang) |
Package Create Date: | 2016-10-19 |
Package Last Update: | 2017-02-21 |
Language: | PHP |
License: | MIT |
Last Refreshed: | 2024-11-21 03:00:45 |
Package Statistics | |
---|---|
Total Downloads: | 4 |
Monthly Downloads: | 0 |
Daily Downloads: | 0 |
Total Stars: | 0 |
Total Watchers: | 2 |
Total Forks: | 0 |
Total Open Issues: | 0 |
####1.01.1 2016-10-19
连表递归改为迭代,查询次数为 (m[n]表示第n层的link数) m[0] * m[1] * m[2] ......
create,createOrUpdate方法加入,适应laravel的习惯
提供了几个laravel model兼容的接口
####1.01.2 2016-10-20
提供了一个适配类Quick,用来在老代码中使用模型
select返回码变为200
增加filter,用来过滤字段
为了实现Quick加入了一些方法在ModelExtend中。
在Helper中加入了触发错误的一些函数
####1.01.3 2016-10-24
修改了select中数据过滤resultConvert的Bug,现在只会传一条数据了
完善了Helper中的一些方法
####1.01.4 2016-10-25
select可以使用laravel自带的分页了,返回值中的page
####1.01.5 2016-10-26
将select内部所有的isset改为!empty
不再支持field兼容方法
####1.01.6 2016-10-27
select增加first 并支持link
select时 link name若为空,则下一级别数据将嵌入到当前条中,如join一样
解决多连接查询的冲突情况,两个连接会重叠在一起,
####1.01.7 2016-11-2 同步类改版 select删除选项修复bug
####1.01.8 2016-11-7 修改一些同步类的功能,使得其适应最新同步逻辑 修复link二级查询时候first的问题
####1.02 2016-11-9 稳定之前修复的功能,优化内部代码 ####1.03 2016-12 修复了部分Bug,加入了过滤器
初始化时,直接继承ModelExtend,并且覆盖几个参数 这些参数是必须要覆盖的:
/**
* 定义模型数据库连接
* @var
*/
static protected $connection;
/**
* 定义表
* @var
*/
static protected $table;
/**
* 定义主键名
* @var
*/
static protected $primaryKey;
这里给出一个例子:
class Product extends ModelExtend
{
static protected $connection = "tour";
static protected $table = "product";
static protected $primaryKey = "product_id";
}
ModelExtend提供了足够动态的查询构造器,只需要简单的定义一个数组,就能够查询到指定内容
$queryLimit =
[
"desc" => true, //倒序
"start" => 0, //从第几条开始
"num" => 10, //需要多少条
"sort" => "provider_id", //按照什么字段排序
"where"=> //where条件
[
["and","provider_id","=",4],
["or","provider_id","=",5]
],
"whereIn" => //whereIn条件
[
"option_id",
[33, 34, 35]
]
],
$data = TestModel::select($queryLimit);
dump($data["data"]);
支持以下参数,参数不填的话会使用默认值或者不设条件。
$queryLimit
|-sort = 排序字段
|-desc = 是否倒序true/false
|-id = 按照某个id查询 //ud
|-start = 查询开始条目
|-num = 查询多少条
|-select = 需要哪些字段,不填是所有,如["xx as new","count(*) a snum"]
|-paginate = 是否使用laravel默认分页机制,需要使用填入每页条数 (不可以和连表一起使用,数据结构不同,第二维是对象,不建议使用)
|-where = [] //ud
|- ["or","field","=","value"] //第一个and或者or是无效的(单个 and和 or是没有意义的)
|- ["field,"=","value] //默认使用and语法
|- ...
|-whereIn = ["id",[1,2,3]] //组合使用whereIn是加载where末尾,如果最后一个条件是or,那么很可能不是你要的效果,最好不要混用 //ud
|-link = []
|-["name","selfFiled","connection.table.field1"["queryLimit"]]
|- ...
|-resultConvert = function(&$dataArray){} //对结果进行转换,会传入本条结果的引用
|-pk = 手动设定主键,id字段将按照这个字段查询,仅在使用id的时候有效
|-deleteEmpty =["name1","name2"...]那些如果为空删除,删除条数时total不会跟着删除。(前端分页的时候不建议使用这个功能)
|-first = true 只查询第一条数据,可以和link一起使用,效果最佳
返回结果数组
return
[
"status":200, //是否成功
"message":"", //返回信息
"data":[], //返回数据,全数组格式
"total":10 //按照此条件匹配,共计多少条
]
对于复杂的业务逻辑,通常需要多重连表查询,且数据结构需要层级的关系,使用link字段可以达到这个要求
|-link = []
|-["name","selfField","connection.table.field1"["queryLimit"]]
|- ...
使用了这个结构并传入select以后,下一张表的数据会拼接到本次返回数据中的name字段里
关联关系是selfField == connection.table.field1(表示意思是 数据库连接.表.字段 连接字段可以省略,默认使用当前连接)
如果还需要附加关系,将新的queryLimit传入即可(新的queryLimit也可以包含link,实现递归查询)
$queryLimit["link"] =
[
[
"value",//子数组名
"option_id",//本方关联
"tour.product_upgrade_option_value.option_id",//对方关联
[
"link" => [
["operation", "value_id", "product_upgrade_option_value_operation.value_id"]
]
] //queryLimit
],
];
SomeModel::select($queryLimit);
上面是一个例子,连表查询了两次,获取数据后,新的子数据在本数据的value底下,每一条value底下有一个operation字段
里面有有匹配的operation数据:
我们可以通过继承selectExtra方法来自定义查询构造参数,注意会传入两个参数,$queryLimit必须使用引用&,不然不会起作用
//在某个模型类中
public static function selectExtra(&$queryLimit, $query)
{
if (isset($queryLimit["specialDate"]))
{
$query->where("create_at", ">", "1476322090");
}
}
这里我们新增了一个specialDate参数,只要传入了specialDate,我们就会查询大于一个时间的数据
通过这种方法,可以根据不同表的需求自定义自己模型
这个函数在排序,决定查询条数之前会被调用
有时候我们需要对查询出的每一条结果进行参数过滤,可以通过覆写selectFilter实现,下面这个例子我们对每一条数据的时间进行了转换,变成字符串
public static function selectFilter(&$data)
{
$data["created_at"] = static::timeToSecondStr($data["created_at"]);
$data["updated_at"] = static::timeToSecondStr($data["updated_at"]);
}
为了照顾laravel使用者习惯,可以使用以下几个传统查询
Model::field("name",'stock_product_id')->get(); //相当于原来的select
Model::where("stock_product_id",'=',1)->get();
Model::where("stock_product_id",1)->get();
Model::orderBy("stock_product_id","desc")->get();
Model::orderBy("stock_product_id")->get();
查询,删除,修改动作基本沿用laravel的习惯,这里直接给出例子
$r = TestModel::add(
[
"provider_id" => 5,
"name" => "xxx",
"tips" => "xxx",
"can_multi_select" => 0,
"required_no" => 0,
"old_parent_id" => 30166,
"subname" => "xx"
]);
$r->update(["provider_id" => 6]);
$r->delete();
同时也支持多更新,删除方法,会根据queryLimit匹配条目,并执行操作
/**
* 批量删除数据,按照queryLimit查询,删除匹配到的查询
* @param $queryLimit //匹配查询限制
* @param null $query //可以选择传入一个构造器,自定义连接和表
* @param null $key //自定义连接和表以后,需要指定主键
*/
static function deleteMultiple($queryLimit, $query = null, $key = null)
//someModel::deleteMultiple($ql)
/**
* 按照QueryLimit多更新,匹配数据会被更新
* @param $queryLimit //限制
* @param $updateData //更新数据
* @param null $query //可以选择传入一个构造器,自定义连接和表
* @param null $key //自定义连接和表以后,需要指定主键
*/
static function updateMultiple($queryLimit, $updateData, $query = null, $key = null)
//someModel::updateMultiple($ql)
同样,增删改也有自定义方法 定义额外的附加方法:
/**
* 额外添加,在添加之前调用
* @param $data //会被添加的数据
* @param $query //查询构造器
*/
protected static function addExtra(&$data, $query)
{
}
/**
* 额外更新,在更新之前调用
* @param $data
* @param $query
*/
protected function updateExtra(&$data, $query)
{
}
/**
* 额外删除,在删除之前调用
* @param $query
*/
protected function deleteExtra($query)
{
}
同时为了适应laravel原有model的习惯,我们也有createOrUpdate函数等提供习惯的调用
/**
* add函数的一个包装,为了和以前的laravel模型保持一致
* @param $data
* @return ModelExtend
*/
public static function create($data)
/**
* add 和 update函数的封装 ,如果一个数据不存在(按照主键判定),则创建,否则修改
* @param $data
* @return ModelExtend
*/
public static function createOrUpdate($data)
提供一个过滤函数,用来过滤添加数据时的字段
/**
* 过滤字段
* @param $data //过滤的数据
* @param $fieldList //需要过滤字段
* @param bool $isForbid true //删除这些字段,false保留这些字段
* @return array //过滤后的数据
* @throws \Exception
*/
//例子
ModelExtend::filter($price,["product_type_id","gross_profit","transaction_fee","price","product_price_id"]);
$data是引用传入的,不是必须要写返回值来接受过滤后数据。
SomeModel::getQuery() 获取本连接表的查询
$someModel->syncFromDataBase() 刷新数据
$someModel->getData() 取出本条数据
$someModel->getId() 取出主键
SomeModel::timeFromSecondStr("1970-01-02 00:00:00") 转换具体秒到时间戳
SomeModel::timeFromDayStr("1970-01-02") 转换天数到时间戳
SomeModel::timeToSecondStr($time) 从时间戳转换为 秒 格式 1970-01-02 00:00:00
SomeModel::timeToDayStr($time) 从时间戳转换为 天 格式 1970-01-02
时区使用env中的TIMEZONE,默认为Asia/Chongqing
数据同步是一个令人头疼的话题,特别是不同的数据结构之间的同步,ModelExtend可以用一种优雅且自动化的方式来进行数据同步
而使用的方法就是简单的继承,请看下面的类
class TestModelDelSync extends ModelExtend
{
static protected $connection = "test";
static protected $table = "src_2";
static protected $primaryKey = "src_id";
//返回映射
public static function loadSyncMap()
{
return
[
"src_product.poduct_id" =>
[
"f1" => "some_field_1",
"f2" => "some_field_2",
"src_2_id" => "src_id"
"stock.stock_product.stock_product_id" => function (&$data, &$insert, $selfObj)
{
//这种方法完全使用了自己的同步策略,执行了这些代码块后,自带的同步逻辑不会运行,参数为将要本方数据,插入的数据,本方对象
//smome logic
}
],
"src_3.src_3_id" =>
[
"f1" => "some_field_1",
"src_2_id" => function (&$data, &$insert, $selfObj){return 1} //可以对一个数据传闭包,参数同上
]
];
}
public function __construct($id)
{
parent::__construct($id);
//设定条件
$srcProductLimit["whereIn"] = ["src_2_id", [$this->getId()]];
$this->appendSyncCondition("src_product.poduct_id",$srcProductLimit); //设置和product的关系是src_2_id字段与自己的src_id字段相同
$src3Limit["where"] = [ ["or", "src_2_id", "=", $this->getData()["src_id"]] ];
$this->appendSyncCondition("src_3.src_3_id",$src3Limit); //设置和src_3的关系是其中的src_2_id字段与自己的src_id相同
//条件添加时第四个参数可以递归的加入另一个条件,其将会在该条件之后运行
$this->appendSyncCondition("essential.product.id", $esLimit, function ($condition, $thisObj = null) use ($id)
{
$tickeLimit["id"] = 1;
$thisObj->appendSyncCondition("ticket.ticket_product.product_id", $tickeLimit);
});
}
}
我们通过覆盖loadSyncMap()方法,为模型设定映射,返回一个映射数组,映射遵循如下格式(连接可以省略)
"connection.table.主键" = ["对端字段"=>"本方字段",....],
"table.主键" = ["对端字段"=>["本方字段",function(&$data){}],....] //如果需要对数据修改,将会把本条数据传入
设定了映射后,在每次实例化模型时,我们在构造函数中设定匹配条件,设定条件遵循如下格式,这里的$queryLimit请不要使用link
$this->appendSyncCondition("essential.product.id", $queryLimit,function($condition, $thisObj = null){}); //后两个参数可选
一个条件必须要有对应的一条映射,否则会抛出错误,但是一个映射不一定需要一个条件,因为在内部实现中,是根据条件来进行同步的,有了条件以后,才回去寻找映射
然后在调用方.如某个控制器,需要同步增删改时,可以如下操作
//在某个控制器
//同步调用
$model = TestModelDelSync::syncAdd(["some_field_1" => "x10"]);
$model->syncUpdate(["some_field_2" => "x64"]);
$model->syncDelete();
//同时提供异步调用
//在异步端,先实例化本对象,然后调用异步同步方法,这里提供每种调用的例子
//要在模型中设定成员变量 static protected $async = true;
//添加
//在同步端
$model = TestModelDelSync::syncAdd(["some_field_1" => "x10"]);
//通过通讯机制传递$id
//在异步端
$mode =new SomeModel($id)
$model->asyncRunAdd();
//更新
//在同步端
$model->syncUpdate(["some_field_2" => "x64"]);
//通过通讯机制传递$condition和$id
//在异步端
$mode = new SomeModel($id);
$model->loadSyncCondition(); //需要加载条件
$model->asyncRunUpdate($condition);
//删除
//在同步端
$model->syncDelete();
//通过通讯机制传递$condition,不需要id,因为可能这个模型实际上已经被删除,无法实例化
//在异步端
SomeModel::asyncRunDelete($condition);//异步调用也是用静态方法来实现
通过定义好上面的映射,设定条件,在增删改查时使用对应的同步数据版的函数,就会触发数据同步,其内部运行机制有以下步骤:
1.匹配数据(删除,更新)
2.建立映射(添加,更新)
3.执行写入数据策略(添加:直接按照映射添加,删除:删除匹配数据,更新:有匹配 数据修改,无匹配 数据添加)
对于需要异步同步数据的场景,你只需要按照示范, 设定成员变量
static protected $async = true;
并且重写发送函数,告诉类该如何发送这个异步请求(比如发送到nsq,或者redis)
/**
* @param int $id //当前这条数据主键
* @param string $condition //条件字符串
*/
public function asyncSend($id,$condition)
{
}
然后还是按照原来的语法操作进行增删改