Skip to content

Instantly share code, notes, and snippets.

@renalpha
Last active August 15, 2019 12:56
Show Gist options
  • Save renalpha/b82abbf55d6886daabe4d9c563d975af to your computer and use it in GitHub Desktop.
Save renalpha/b82abbf55d6886daabe4d9c563d975af to your computer and use it in GitHub Desktop.
DataComponent for Laravel-Vue-Pagination
<?php
namespace App\Queries;
use Spatie\QueryBuilder\QueryBuilder;
abstract class AbstractQuery extends QueryBuilder
{
/**
* @var array
*/
protected $filters = [];
/**
* @var array
*/
protected $columns = [];
/**
* @param array $filters
* @throws \Exception
*/
protected function validateFilters(array $filters)
{
$requiredKeys = [
'type',
'title',
];
$errors = [];
foreach ($filters as $filter) {
foreach ($requiredKeys as $key) {
if (!array_key_exists($key, $filter)) {
$errors[$key] = 'Filter ' . $key . ' parameter does not exists.';
}
}
}
if (count($errors) > 0) {
throw new \Exception(implode(', ', $errors));
}
}
/**
* @param array $filters
* @return \App\Queries\AbstractQuery
* @throws \Exception
*/
public function setFilters(array $filters)
{
$this->validateFilters($filters);
$this->allowedFilters(array_keys($filters));
$this->filters = $filters;
return $this;
}
/**
* @param array $columns
* @return \App\Queries\AbstractQuery
*/
public function setColumns(array $columns)
{
$this->columns = $columns;
return $this;
}
/**
* @return array
*/
public function getFilters()
{
return ['filters' => $this->filters];
}
/**
* @return array
*/
public function getColumns()
{
return ['columns' => $this->filters];
}
}
<template>
<div>
<template v-for="(filters) in chunkedFilters">
<div class="row">
<div class="col-md-3" v-for="(value) in filters">
<div class="form-group">
<label>{{ value.title }}</label>
<input type="text" v-model="filter['filter['+value.key+']']" class="form-control"
@input="debounceInput" v-if="value.type === 'text'"/>
<select v-model="filter['filter['+value.key+']']" v-else-if="value.type === 'select'"
@input="debounceInput" class="form-control">
<option :value="undefined">-- Select --</option>
<option v-for="(value, key) in value.data" :value="key" :selected="value === ''">
{{ value }}
</option>
</select>
</div>
</div>
</div>
</template>
<div class="clearfix"></div>
<div class="float-right">
<pagination :data="dataResource" :limit="4" :show-disabled="true" @pagination-change-page="getResults">
<span slot="prev-nav">&lt; Previous</span>
<span slot="next-nav">Next &gt;</span>
</pagination>
</div>
<div class="float-left">
<p><span><strong>Total products:</strong> {{ dataResource.total }} | <strong>Per page:</strong> {{ dataResource.per_page }}</span>
</p>
</div>
<div class="clearfix"></div>
<div class="table-responsive">
<table class="table table-striped table-condensed">
<thead>
<tr>
<template v-for="(value, key) in columns">
<th>
<a href="#" @click="sortBy(key)" v-if="sorts.includes(key) === true">
{{ value }}
</a>
<span v-else>{{ value }}</span>
</th>
</template>
</tr>
</thead>
<tbody>
<tr v-for="data in dataResource.data">
<template v-for="(value, key) in columns">
<td>
<router-link v-bind:to="data['detail_url']" v-if="isLinkedItem(key)">
{{ data[key] }}
</router-link>
<span v-html="data[key]" v-else="!isLinkedItem(key)">{{ data[key] }}</span></td>
</template>
</tr>
</tbody>
</table>
</div>
<div class="clearfix"></div>
<div class="float-left">
<pagination :data="dataResource" :limit="4" :show-disabled="true" @pagination-change-page="getResults">
<span slot="prev-nav">&lt; Previous</span>
<span slot="next-nav">Next &gt;</span>
</pagination>
</div>
<div class="float-right">
<p><span><strong>Total products:</strong> {{ dataResource.total }} | <strong>Per page:</strong> {{ dataResource.per_page }}</span>
</p>
</div>
<div class="clearfix"></div>
</div>
</template>
<script>
import _ from 'lodash';
export default {
mounted() {
},
props: {
path: {
type: String,
default: ''
},
linked: {
default: function () {
return ['id']
}
},
},
data() {
return {
// Our data object that holds the Laravel paginator data
dataResource: {
total: 0,
},
search: null,
filter: {},
sorts: {},
columns: {},
queryUrl: null,
sortAsc: true,
column: null,
}
},
created() {
},
mounted() {
this.getResults();
},
computed: {
chunkedFilters() {
return _.chunk(_.map(this.dataResource.filters, (value, key) => ({
key,
...value
})), 4);
}
},
methods: {
getResults(page = 1) {
let path = this.setQuery(page);
this.query(path)
},
setQuery(page = 1) {
let path = this.path + '?page=' + page;
if (Object.keys(this.filter).length !== 0) {
path += '&' + decodeURIComponent(this.serialize(this.filter));
}
if (this.column !== null) {
path += '&sort=' + this.column;
}
return path;
},
searchNow() {
this.removeEmptyFilterValues(this.filter);
let path = this.setQuery();
this.query(path)
},
sortBy(column) {
this.sortAsc = !this.sortAsc;
if (this.sortAsc === true) {
column = '-' + column;
}
this.column = column;
this.searchNow();
},
query(url) {
this.queryUrl = url;
axios.get(url)
.then(response => {
this.dataResource = response.data;
this.sorts = this.dataResource.sorts;
this.columns = this.dataResource.columns;
});
},
isLinkedItem(n) {
return this.linked.indexOf(n) > -1
},
debounceInput: _.debounce(function (e) {
this.searchNow();
}, 400),
serialize(obj) {
var str = [];
for (var p in obj)
if (obj.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
return str.join("&");
},
removeEmptyFilterValues(obj) {
Object.keys(obj).forEach(function (key) {
if (obj[key] && typeof obj[key] === 'object') {
this.removeEmptyFilterValues(obj[key])
} else if (obj[key] === null || obj[key] === undefined) {
delete obj[key]
}
});
},
}
}
</script>
<?php
/**
* List of all theses available for this student.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws \Exception
*/
public function index(Request $request)
{
$thesisQuery = (new ThesisQuery(auth()
->user()
->theses()
->getQuery()))
->setFilters([
'title' => ['type' => 'text', 'title' => 'Title'],
'status' => ['type' => 'text', 'title' => 'Status'],
'academic_year' => ['type' => 'text', 'title' => 'Academic year'],
'created_at' => ['type' => 'text', 'title' => 'Created at'],
'updated_at' => ['type' => 'text', 'title' => 'Updated at'],
])
->setColumns([
'title' => 'Title',
'name' => 'Name',
'academic_year' => 'Academic year',
'status' => 'Status',
'created_at' => 'Created at',
'updated_at' => 'Updated at',
]);
if ($request->wantsJson()) {
return response()
->json($thesisQuery->paginate(self::DEFAULT_PAGINATE_SIZE)->toArray() + $thesisQuery->getColumns() + $thesisQuery->getFilters() + ['title' => 'Theses']);
}
return abort(403);
}
<?php
namespace App\Queries;
use App\Models\Thesis;
final class ThesisQuery extends AbstractQuery
{
/**
* ThesisQuery constructor.
*
* @param null $builder
*/
public function __construct($builder = null)
{
parent::__construct($builder ?? Thesis::query());
}
}
@renalpha
Copy link
Author

renalpha commented Aug 12, 2019

Usage

npm install laravel-vue-pagination
<data-component path="/json-url-api-pagination" :columns="json_encode(['title','created_at'])></data-component>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment