前言:l5-repository应用于抽象数据层,使我们的应用程序更易于维护。今天我们通过一个例子来说明如何使用l5-repository动态地查询关联数据,避免过度查询
一、laravel代码层面--一个完整的l5-repository使用流程和解决方案
描述:以目标清单列表all接口请求过慢为例子(直接获取所有的目标清单,目标清单数据在1800条左右,数据很少,但是请求需要30秒左右,原因在于Transformer层(转化者)里默认关联获取user信息,user表有近千万条的数据,慢的原因在于查询此表,但是在现有的业务逻辑是不需要此参数的,属于过度查询)
1.model层(数据操作者)
<?php
namespace App\Repositories;
use Illuminate\Database\Eloquent\Model;
// 目标清单列表模型
class TargetList extends BaseModel
{
public function user()
{
return $this->belongsTo(\Wunsun\Cms\Auth\Models\User::class, 'user_id');
}
}
2.repository层(仓库管理者,作为model的辅助者)
<?php
namespace App\Repositories;
use Prettus\Repository\Eloquent\BaseRepository
use App\Models\TargetList;
use App\Presenters\TargetListPresenter;
class TargetListRepository extends BaseRepository
{
public function model()
{
return TargetList::class;
}
public function presenter()
{
return TargetListPresenter::class;
}
}
3.Presenter层(演示者,也就是数据演示)
namespace App\Presenters;
use App\Transformers\TargetListTransformer;
class TargetListPresenter extends BasePresenter
{
public function getTransformer()
{
return new TargetListTransformer();
}
}
4.Transformer层(转化者,辅助演示者进行数据转化)
(1)初始的粗糙的关联查询User信息用法(会默认每次进行关联查询)
<?php
namespace App\Transformers;
use App\Transformers\UserTransformer;
use League\Fractal\TransformerAbstract;
use App\Models\TargetList;
class TargetListTransformer extends TransformerAbstract
{
public function transform(TargetList $model)
{
return [
'id' => (int)$model->id,
'name' => (string)$model->name,
'status' => (boolean)$model->status,
'user' => $model->user,
'created_at' => (string)$model->created_at
];
}
public function includeUser(TargetList $model)
{
$user = $model->user;
return $this->item($user, new UserTransformer);
}
}
(2) 支持动态引入关联User信息(使用availableIncludes参数)
<?php
namespace App\Transformers;
use App\Transformers\UserTransformer;
use League\Fractal\TransformerAbstract;
use App\Models\TargetList;
class TargetListTransformer extends TransformerAbstract
{
protected $availableIncludes = [
'user',
];
public function transform(TargetList $model)
{
return [
'id' => (int)$model->id,
'name' => (string)$model->name,
'status' => (boolean)$model->status,
'created_at' => (string)$model->created_at
];
}
public function includeUser(TargetList $model)
{
$user = $model->user;
return $this->item($user, new UserTransformer);
}
}
5.目标清单列表all接口逻辑代码
(1)使用availableIncludes参数
/**
* 获取所有目标清单列表
*
* @Get("/all")
*
* @return \Illuminate\Http\Response
*/
public function all(Request $request)
{
return $this->response->array($this->repository->pushCriteria(
new TargetListCriteria(
$request->user()->id,
$request->input('city_id', 0)
))->all());
}
6.解决描述
(1)在Transformer转化层内使用availableIncludes参数设置关联User,且此字段是属于动态获取。all接口是不需要使用user信息的。
那么,如何动态获取呢。在接口路由添加参数include(在获取大量数据且关联表数据极大的情况下不推荐)
http://xxx/xx/all?include=user
(2)或者直接再创建一个新的演示者(Presenter)和对应的转化者(Transformer),
public function all(Request $request)
{
return $this->response->array($this->repository->pushCriteria(
new TargetListCriteria(
$request->user()->id,
$request->input('city_id', 0)
))->setPresenter(TargetListSimplifyPresenter::class)->all());
}
class TargetListSimplifyPresenter extends BasePresenter
{
public function getTransformer()
{
return new TargetListSimplifyTransformer();
}
}
class TargetListSimplifyTransformer extends TransformerAbstract
{
public function transform(TargetList $model)
{
return [
'id' => (int)$model->id,
'name' => (string)$model->name,
];
}
}
二、总结
- 在需要获取大量数据的接口中,根据业务的需求查询时应该建立比较有效的索引,避免全表搜索。
- 避免获取大量数据的接口,还进行关联查询,特别是关联的表是大数据表
- 根据业务,只返回业务所需字段,避免做全字段查询