Skip to content

Instantly share code, notes, and snippets.

@Insolita
Last active April 23, 2019 12:37
Show Gist options
  • Save Insolita/a3d3acfa8713850dcd70a9bb4d880dd3 to your computer and use it in GitHub Desktop.
Save Insolita/a3d3acfa8713850dcd70a9bb4d880dd3 to your computer and use it in GitHub Desktop.
Laravel grid
let ajaxRequest = function (method, url) {
$.ajax({
url: url,
method: method,
dataType: 'json',
success: function (response) {
if (response.state === true) {
notifySuccess(response.message);
} else {
notifyError(response.message);
}
},
error: function (err, response) {
console.log(response);
notifyError('Что-то пошло не так. Ошибка сервера!')
}
});
};
let inputReset = function (container) {
let inputs = $(container);
inputs.find(':radio, :checkbox').removeAttr('checked').end()
.find('textarea, :text, [type="search"], [type="email"], [type="tel"], [type="url"], [type="number"], select').val('');
inputs.find('[type="date"],[type="datetime"]').each(function () {
$(this).valueAsDate = null;
$(this).val('');
});
};
//-----------
//-----------
$(document).ready(function () {
$(document).on('click', 'a[data-method]', function (e) {
e.preventDefault();
let method = $(this).data('method');
let needConfirm = $(this).data('confirm');
let url = $(this).attr('href');
if(needConfirm!==undefined){
notifyConfirm(needConfirm, function () {
ajaxRequest(method,url);
});
}else{
ajaxRequest(method, url);
}
});
$(document).on('keypress', '.enter-submit', function (e) {
if (e.which === 10 || e.which === 13) {
$('#' + $(this).attr('form')).submit();
}
});
$('button[data-reset]').on('click', function (e) {
e.preventDefault();
let inputRow = $(this).data('selector');
let autoSubmit = $(this).data('submit');
let form = $(this).attr('form');
inputReset(inputRow);
if (autoSubmit !== '0') {
$('#' + form).submit();
}
return false;
});
$('button[type="reset"]').on('click', function (e) {
let autoSubmit = $(this).data('submit');
let form = $(this).closest('form');
inputReset(form.attr('id'));
if (autoSubmit !== '0') {
form.submit();
}
return false;
});
});
<?php
/**BaseRequest class for grid request **/
class BaseFilterRequest extends FormRequest
{
const SORT_ASC = 'asc';
const SORT_DESC = 'desc';
public static $sortable
= [
'id',
'title',
'created_at',
];
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* @param $sortBy
*
* @return string
*/
public function buildSortUrl($sortBy)
{
$query = $this->query();
if ($this->get('sort') == $sortBy) {
$order = $this->get('order') == self::SORT_DESC ? self::SORT_ASC : self::SORT_DESC;
} else {
$order = self::SORT_DESC;
}
return $this->fullUrlWithQuery(array_merge($query, ['sort' => $sortBy, 'order' => $order]));
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'search' => ['max:255|alpha_dash'],
'sort' => [Rule::in(static::$sortable)],
'order' => [Rule::in([self::SORT_ASC, self::SORT_DESC])],
'pageSize' => 'numeric|min:5|max:100',
'period_from' => 'nullable|date',
'period_to' => 'nullable|date',
];
}
<?php
abstract class BaseSearchModel
{
const PERIOD_DATETIME = 'datetime';
const PERIOD_DATE = 'date';
const PERIOD_UNIXTIME = 'timestamp';
public $selectAttributes = ['*'];
public $defaultPageSize = 15;
public $defaultOrder = 'desc';
public $defaultSortField = 'id';
public $periodAttribute = 'created_at';
public $periodFormat = self::PERIOD_DATETIME;
/**
* @var \Eloquent
*/
protected $model;
/**
* @param string $modelClass
*/
public function __construct($modelClass)
{
$this->model = new $modelClass;
}
/**
* @param \App\Base\BaseFilterRequest|\Illuminate\Foundation\Http\FormRequest $filter
*
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Collection|[]
*/
public function apply($filter)
{
/**@var Builder $builder * */
$builder = $this->model->newQuery();
$this->applyFilter($builder, $filter);
if ($this->periodAttribute !== null) {
$this->applyPeriod($builder, $filter);
}
$builder->orderBy($filter->get('sort', $this->defaultSortField), $filter->get('order', $this->defaultOrder));
return $builder->paginate($filter->get('pageSize', $this->defaultPageSize));
}
/**
* @param Builder $builder
* @param \App\Base\BaseFilterRequest|\Illuminate\Foundation\Http\FormRequest $filter
*
* @return Builder
*/
abstract protected function applyFilter(Builder $builder, $filter): Builder;
/**
* @param Builder $builder
* @param \App\Base\BaseFilterRequest|\Illuminate\Foundation\Http\FormRequest $filter
*
* @return Builder
*/
protected function applyPeriod(Builder $builder, $filter): Builder
{
if ($filter->has('period_from') and $filter->has('period_to')) {
$builder->whereBetween(
$this->periodAttribute,
[
$this->formatPeriodValue($filter->get('period_from')),
$this->formatPeriodValue($filter->get('period_to')),
]
);
} elseif ($filter->has('period_from')) {
$builder->where(
$this->periodAttribute,
'>=',
$this->formatPeriodValue($filter->get('period_from'))
);
} elseif ($filter->has('period_to')) {
$builder->where(
$this->periodAttribute,
'<=',
$this->formatPeriodValue($filter->get('period_to'))
);
}
return $builder;
}
/**
* @param $value
*
* @return int|string
*/
protected function formatPeriodValue($value)
{
switch ($this->periodFormat) {
case self::PERIOD_DATETIME:
return (new Carbon($value))->toDateTimeString();
case self::PERIOD_DATE:
return (new Carbon($value))->toDateString();
case self::PERIOD_UNIXTIME:
return (new Carbon($value))->timestamp;
default:
return $value;
}
}
}
<?php
class NewsFilter extends BaseFilterRequest
{
public static $sortable = [
'id','title','visible','created_at','public_at'
];
public function rules()
{
$rules = parent::rules();
return array_merge(
$rules,
[
'id' => 'nullable|numeric|min:1',
'visible'=>'nullable|boolean'
]
);
}
}
<?php
class NewsSearch extends BaseSearchModel
{
public $periodFormat = self::PERIOD_DATETIME;
/**
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \App\Base\BaseFilterRequest|\Illuminate\Foundation\Http\FormRequest $filter
*
* @return Builder
*/
protected function applyFilter(Builder $builder, $filter): Builder
{
$builder->select($this->selectAttributes);
if ($filter->has('search')) {
$search = '%' . $filter->get('search') . '%';
$builder
->where('title', 'ilike', $search);
}
if ($filter->has('id')) {
$builder
->where('id', '=', (int) $filter->get('id'));
}
if ($filter->has('visible')) {
$builder
->where('visible', '=', (int) $filter->get('visible'));
}
return $builder;
}
}
<?php
//------------
public function index(NewsFilter $filter)
{
$searchModel = new NewsSearch(News::class);
$news = $searchModel->apply($filter);
$news->appends($filter->except('page'));
return view('backend.news.index', compact('searchModel', 'news', 'filter'));
}
//--------------View news.index
@extends('backend.layout')
@section('content')
@component('backend.inc.panelbox') //AdminLte box wrapper
@slot('title') Список новостей @endslot
@include('backend.inc.tabs',['tabs'=>'','routeBase'=>'backend.news'])
<!-- Optional - External filter form -->
@include('backend.news._filter')
<!-- Inline filter form declaration -->
<form id="grid_filter">{{csrf_field()}} @include('_partials.form_invalids')</form>
<!--// Inline filter form declaration -->
<div class="table-responsive">
<table class="table table-bordered table-condensed news-table">
<thead>
<!-- Sortable headers-->
<tr>
@include('backend.grid.sortableTh',['name'=>'#','attribute'=>'id','filter'=>$filter])
@include('backend.grid.sortableTh',['name'=>'Заголовок','attribute'=>'title','filter'=>$filter])
@include('backend.grid.sortableTh',['name'=>'Дата публикации','attribute'=>'public_at',
'filter'=>$filter])
@include('backend.grid.sortableTh',['name'=>'Создано','attribute'=>'created_at',
'filter'=>$filter])
@include('backend.grid.sortableTh',['name'=>'Статус','attribute'=>'visible','filter'=>$filter])
<th>Операции</th>
</tr>
</thead>
<!-- inline table filters -->
<tr id="grid_filter_row">
@include('backend.grid.filters.input_filter',['attribute'=>'id','type'=>'number'])
@include('backend.grid.filters.input_filter',['attribute'=>'search'])
<td></td>
@include('backend.grid.filters.input_filter',['attribute'=>'period_from','type'=>'date'])
@include('backend.grid.filters.select_filter',
['attribute'=>'visible','variants'=>[0=>'Черновик',1=>'Ок']])
@include('backend.grid.filter_actions')
<!--// inline table filters -->
</tr>
@forelse ($news as $item)
<tr id="item-{{$item->id}}">
<td data-column="pk">{{$item->id}}</td>
<td data-column="searchable">{{$item->title}}</td>
<td data-column="public_at"> {{$item->public_at->format('d.m.Y H:i')}}</td>
<td data-column="crated_at"> {{$item->created_at->format('d.m.Y H:i')}}</td>
<td>{{ $item->visible?'Ок':'Черновик' }}</td>
<td data-column="actions">
@include('backend.grid.actions.edit',['url'=>route('backend.news.edit',['id'=>$item->id])])
@include('backend.grid.actions.view',['url'=>route('backend.news.show',['id'=>$item->id])])
@include('backend.grid.actions.delete',['url'=>route('backend.news.destroy',['id'=>$item->id])])
</td>
</tr>
@empty
<tr>
<td colspan="5">Пока ничего не добавлено</td>
</tr>
@endforelse
</table>
</div>
@slot('footer')
{{$news->links()}}
@endslot
@endcomponent
@endsection
//---------sort head partial
<th>
<a href="{{ $filter->buildSortUrl($attribute)}}" title="Сортировать по столбцу">
{{$name}}
@if(request()->get('sort')===$attribute)
@if(request()->get('order')==='asc')
<i class="fa fa-sort-down"></i>
@else
<i class="fa fa-sort-up"></i>
@endif
@endif
</a>
</th>
//---------action partial
<a href="{{$url}}" class="btn btn-xs btn-default" data-title="Редактировать"> <i class="fa fa-edit"></i></a>
//--------filter actions partial
<td>
<button class="btn btn-sm bg-purple" form="{{$formId or 'grid_filter'}}"><i class="fa fa-search"></i> Найти</button>
<button form="{{$formId or 'grid_filter'}}" data-reset="true"
data-selector="#{{$formId or 'grid_filter'}}_row" data-submit="0"
class="btn btn-sm btn-default">Сброс</button>
</td>
//------- inline-filter input partial
<td>
<input type="{{$type or 'text'}}" name="{{$attribute}}" value="{{request()->get($attribute,'')}}"
form="{{$formId or 'grid_filter'}}" class="form-control enter-submit">
</td>
//------- inline-filter select partial
<td>
<select name="{{$attribute}}" form="{{$formId or 'grid_filter'}}" class="form-control enter-submit">
<option value="" {{request()->get($attribute,null)===''?'selected':''}}>---</option>
@foreach($variants as $index => $variant)
<option value="{{$index}}" {{request()->get($attribute,null)===$index?'selected':''}}>{{$variant}}</option>
@endforeach
</select>
<td>
//-------External filter form
<div id="grid_filter" class="grid_filter">
@include('_partials.form_invalids')
<form class="form-inline" method="get" id="grid_filter_form">
{{csrf_field()}}
<div class="form-group">
<label for="filter_search">Поиск</label>
<input type="text" name="search" id="filter_search" value="{{$filter->get('search','')}}"
class="form-control">
</div>
<div class="form-group">
<label for="filter_period_from">С даты</label>
<input type="date" name="period_from" id="filter_period_from" value="{{$filter->get('period_from','')}}"
class="form-control">
</div>
<div class="form-group">
<label for="filter_period_to">По дату</label>
<input type="date" name="period_to" id="filter_period_to" value="{{$filter->get('period_to','')}}"
class="form-control">
</div>
<div class="form-group">
<label for="filter_pagesize">На страницу</label>
<input type="number" step="5" min="5" max="100" name="pageSize" id="filter_pagesize"
value="{{$filter->get('pageSize',20)}}"
class="form-control">
</div>
<div class="form-group">
<button class="btn btn-sm bg-purple"><i class="fa fa-search"></i> Найти</button>
<button type="reset" class="btn btn-sm btn-default">Сброс</button>
</div>
</form>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment