hyperf-view/builder/View/Grid/Model.php

519 lines
14 KiB
PHP

<?php
declare(strict_types=1);
namespace Builder\View\Grid;
use Hyperf\Database\Model\Builder;
use Hyperf\Database\Model\Collection;
use Hyperf\Database\Model\Model as EloquentModel;
use Hyperf\Database\Model\Relations\BelongsTo;
use Hyperf\Database\Model\Relations\HasOne;
use Hyperf\Database\Model\Relations\Relation;
use Hyperf\HttpServer\Request;
use Hyperf\Paginator\LengthAwarePaginator;
use Hyperf\Utils\Arr;
use Hyperf\Utils\Str;
use Builder\View\Grid;
/**
* Class Model
* @package Builder\View\Grid
*/
class Model
{
/**
* Eloquent model instance of the grid model.
*
* @var EloquentModel|Builder
*/
protected $model;
/**
* @var EloquentModel |Builder
*/
protected $sModel;
/**
* @var EloquentModel|Builder
*/
protected $originalModel;
/**
* Array of queries of the eloquent model.
*
*/
protected $queries;
/**
* Sort parameters of the model.
*
* @var array
*/
protected $sort;
/**
* @var
*/
protected $data;
/**
* 20 items per page as default.
* @var int
*/
protected $perPage = 20;
/**
* If the model use pagination.
*
* @var bool
*/
protected $usePaginate = true;
/**
* The query string variable used to store the per-page.
*
* @var string
*/
protected $perPageName = 'per_page';
/**
* The query string variable used to store the sort.
*
* @var string
*/
protected $sortName = '_sort';
/**
* Collection callback.
*
* @var \Closure
*/
protected $collectionCallback;
/**
* @var Grid
*/
protected $grid;
/**
* @var Relation
*/
protected $relation;
/**
* @var array
*/
protected $eagerLoads = [];
public function __construct(EloquentModel $model = null, Grid $grid = null)
{
$this->model = $model;
$this->sModel = $model;
$this->originalModel = $model;
$this->grid = $grid;
$this->queries = collect();
}
public function eloquent()
{
return $this->model;
}
public function usePaginate($use = true)
{
$this->usePaginate = $use;
}
/**
* @return bool
*/
public function isUsePaginate(): bool
{
return $this->usePaginate;
}
public function getModel()
{
return $this->model;
}
protected function findQueryByMethod($method)
{
return $this->queries->first(function ($query) use ($method) {
return $query['method'] == $method;
});
}
protected function resolvePerPage($paginate)
{
if ($perPage = request($this->perPageName)) {
if (is_array($paginate)) {
$paginate['arguments'][0] = (int)$perPage;
return $paginate['arguments'];
}
$this->perPage = (int)$perPage;
} else {
$this->perPage = $this->grid->getPerPage();
}
if (isset($paginate['arguments'][0])) {
return $paginate['arguments'];
}
return [$this->perPage];
}
/**
* 设置每页大小
*/
protected function setPaginate()
{
$paginate = $this->findQueryByMethod('paginate');
$this->queries = $this->queries->reject(function ($query) {
return $query['method'] == 'paginate';
});
if ($this->grid->isHidePage()) {
$query = [
'method' => 'get',
'arguments' => [],
];
} else {
$query = [
'method' => 'paginate',
'arguments' => $this->resolvePerPage($paginate),
];
}
$this->queries->push($query);
}
public function with($relations)
{
if (is_array($relations)) {
if (Arr::isAssoc($relations)) {
$relations = array_keys($relations);
}
$this->eagerLoads = array_merge($this->eagerLoads, $relations);
}
if (is_string($relations)) {
if (Str::contains($relations, '.')) {
$relations = explode('.', $relations)[0];
}
if (Str::contains($relations, ':')) {
$relations = explode(':', $relations)[0];
}
if (in_array($relations, $this->eagerLoads)) {
return $this;
}
$this->eagerLoads[] = $relations;
}
return $this->__call('with', (array)$relations);
}
/**
* 设置排序
*/
protected function setSort()
{
$column = request('sort_field', null);
$sort_field = request('sort_field', null);
$type = request('sort_order', null);
if ($sort_field && in_array($type, ['asc', 'desc'])) {
$this->sort = [
'column' => $sort_field,
'type' => $type
];
} else {
$defaultSort = $this->grid->getDefaultSort();
$this->sort = [
'column' => $defaultSort['sort_field'],
'type' => $defaultSort['sort_order']
];
}
if (!is_array($this->sort)) {
return;
}
if (empty($this->sort['column']) || empty($this->sort['type'])) {
return;
}
if (Str::contains($this->sort['column'], '.')) {
$this->setRelationSort($this->sort['column']);
} else {
$this->resetOrderBy();
// get column. if contains "cast", set set column as cast
if (!empty($this->sort['cast'])) {
$column = "CAST({$this->sort['column']} AS {$this->sort['cast']}) {$this->sort['type']}";
$method = 'orderByRaw';
$arguments = [$column];
} else {
$column = $this->sort['column'];
$method = 'orderBy';
$arguments = [$column, $this->sort['type']];
}
$this->queries->push([
'method' => $method,
'arguments' => $arguments,
]);
}
}
/**
* @param $column
* @throws \Exception
*/
protected function setRelationSort($column)
{
list($relationName, $relationColumn) = explode('.', $column);
if ($this->queries->contains(function ($query) use ($relationName) {
return $query['method'] == 'with' && in_array($relationName, $query['arguments']);
})) {
$relation = $this->model->$relationName();
$this->queries->push([
'method' => 'select',
'arguments' => [$this->model->getTable() . '.*'],
]);
$this->queries->push([
'method' => 'join',
'arguments' => $this->joinParameters($relation),
]);
$this->resetOrderBy();
$this->queries->push([
'method' => 'orderBy',
'arguments' => [
$relation->getRelated()->getTable() . '.' . $relationColumn,
$this->sort['type'],
],
]);
}
}
public function resetOrderBy()
{
$this->queries = $this->queries->reject(function ($query) {
return $query['method'] == 'orderBy' || $query['method'] == 'orderByDesc';
});
}
/**
* @param Relation $relation
* @return array
* @throws \Exception
*/
protected function joinParameters(Relation $relation)
{
$relatedTable = $relation->getRelated()->getTable();
if ($relation instanceof BelongsTo) {
return [
$relatedTable,
$relation->getForeignKeyName(),
'=',
$relatedTable . '.' . $relation->getRelated()->getKeyName(),
];
}
if ($relation instanceof HasOne) {
return [
$relatedTable, function ($join) use ($relation) {
$join->on($relation->getQualifiedParentKeyName(), "=", $relation->getQualifiedForeignKeyName())
->where(function (Builder $query) use ($relation) {
collect($relation->getBaseQuery()->wheres)->filter(function ($item) {
return $item['value'] ?? false;
})->each(function ($item) use ($query) {
if ($item['type'] == 'Basic') {
$query->where($item['column'], $item['value']);
}
});
});
}
];
}
throw new \Exception('Related sortable only support `HasOne` and `BelongsTo` relation.');
}
protected function handleInvalidPage(LengthAwarePaginator $paginator)
{
if ($paginator->lastPage() && $paginator->currentPage() > $paginator->lastPage()) {
$lastPageUrl = Request::fullUrlWithQuery([
$paginator->getPageName() => $paginator->lastPage(),
]);
}
}
public function addConditions(array $conditions)
{
foreach ($conditions as $condition) {
call_user_func_array([$this, key($condition)], current($condition));
}
return $this;
}
/**
* @param callable $callback
* @param int $count
*
* @return bool
*/
public function chunk($callback, $count = 100)
{
if ($this->usePaginate) {
return $this->buildData(false)->chunk($count)->each($callback);
}
$this->queries->reject(function ($query) {
return $query['method'] == 'paginate';
})->each(function ($query) {
$this->model = $this->model->{$query['method']}(...$query['arguments']);
});
return $this->model->chunk($count, $callback);
}
public function buildData($toArray = false)
{
if (empty($this->data)) {
$this->data = $this->get();
}
if ($toArray) {
return $this->data->toArray();
}
return $this->data;
}
public function displayData($data)
{
$columns = $this->grid->getColumns();
$items = collect();
foreach ($data as $key => $row) {
$item = [];
foreach ($this->grid->getAppendFields() as $field) {
data_set($item, $field, data_get($row, $field));
}
foreach ($columns as $column) {
if (Str::contains($column->getName(), '.')) {
list($relationName, $relationColumn) = explode('.', $column->getName());
//如果是集合
if (data_get($row, $relationName) instanceof Collection || is_array(data_get($row, $relationName))) {
$value = collect(data_get($row, $relationName))->pluck($relationColumn);
$c_value = $column->customValueUsing($row, $value);
data_set($item, $column->getName(), $c_value);
} else {
$value = data_get($row, $column->getName(), $column->getDefaultValue());
$c_value = $column->customValueUsing($row, $value);
data_set($item, $column->getName(), $c_value);
}
} else {
$dataValue = data_get($row, $column->getName(), $column->getDefaultValue());
if ($this->sModel != null && $dataValue instanceof \Carbon\Carbon) {
$dataValue = $dataValue->format($this->sModel->getDateFormat());
}
$n_value = $column->customValueUsing($row, $dataValue);
data_set($item, $column->getName(), $n_value);
}
}
if (!$this->grid->getHideActions()) {
data_set($item, 'grid_actions', $this->grid->getActions($row, $key));
}
data_set($item, $this->grid->getKeyName(), data_get($row, $this->grid->getKeyName()));
//如果存在下级
if ($TreeChildren = data_get($row, $this->grid->getTreeChildrenName())) {
//递归处理下级列表
data_set($item, $this->grid->getTreeChildrenName(), $this->displayData($TreeChildren));
}
$items->push($item);
}
return $items->all();
}
public function __call($method, $arguments)
{
$this->queries->push([
'method' => $method,
'arguments' => $arguments,
]);
return $this;
}
public function __get($key)
{
$data = $this->buildData();
if (array_key_exists($key, $data)) {
return $data[$key];
}
}
public function get()
{
if ($this->model instanceof LengthAwarePaginator) {
return $this->model;
}
if ($this->relation) {
$this->model = $this->relation->getQuery();
}
$this->setSort();
$this->setPaginate();
$this->queries->unique()->each(function ($query) {
$this->model = call_user_func_array([$this->model, $query['method']], $query['arguments']);
});
$data = $this->model;
if ($this->model instanceof Collection) {
if ($data->count() > 0) {
return $this->displayData($data);
} else {
return $data;
}
}
if ($this->model instanceof LengthAwarePaginator) {
return [
'current_page' => $this->model->currentPage(),
'per_page' => $this->model->perPage(),
'last_page' => $this->model->lastPage(),
'total' => $this->model->total(),
'data' => $this->displayData($data->items())
];
}
}
}