Reputation: 4675
I'm making a simple gallery using OctoberCMS, based on Laravel and Twig.
How can I filter records by category?
Here is how I'm doing it now, but I don't think this is a good solution:
I filter the html, but the full records still exists for all the items outside the category, causing extra pagination and blank pages.
Database
A Table 'gallery' holds list of category names.
A Table 'images' holds the image names and their tagged categories.
Image List
The URL parameters are /gallery/:category?/:page?
Visiting a url like /gallery/nature/1 will filter images by category using a for loop.
<!-- Gallery Category Record Variable -->
{% set category = record.category %}
<!-- Image List -->
{% for record in records %}
<!-- If Image Category Tag matches Gallery Category Name -->
{% if record.category_tag == category %}
<img src="/{{ record.name }}.jpg">
{% endif %}
{% endfor %}
Records
Results for 'records' and 'record'.
The for loop is showing 'gallery→category' record in 'images→category_tag' records.
The pagination is showing all 'images→name' records.
{% for record in records %} = (All in 'images')
{{ records }} = {<ul><li>...<a href="gallery/nature?page=2">2</a>} (All in 'images')
{{ record }} = {"category":"nature","id":7} (Current category in 'gallery')
Solution?
Is there a way to filter the 'records' by category before generating the html list?
Upvotes: 1
Views: 1382
Reputation: 3195
This is how I would approach it :
Let's say Gallery Component :
URL : page.com/:cat/:page?
public function defineProperties()
{
return [
// Page # for pagination..
'pageNumber' => [
'title' => 'Page #',
'description' => 'Gallery Page #',
'type' => 'string',
'default' => '{{ :page }}',
],
// Category slug
'category' => [
'title' => 'Category',
'description' => 'Gallery Cat',
'type' => 'string',
'default' => '{{ :cat }}',
],
// Images to show per page
'perPage' => [
'title' => 'Images per page',
'type' => 'string',
'validationPattern' => '^[0-9]+$', // validation
'validationMessage' => 'VValidation Error',
'default' => '15',
],
// if you want to add sorting
'sortOrder' => [
'title' => 'Sort Order',
'description' => 'Images Sort Order',
'type' => 'dropdown',
'default' => 'updated_at desc'
],
];
}
public function getSortOrderOptions()
{
return Image::$allowedSortingOptions;
}
public function init()
{
$this->pageNumber = empty($this->property('pageNumber')) ? 1 : $this->property('pageNumber');
$this->perPage = $this->property('perPage');
$this->sortOrder = $this->property('sortOrder');
$this->category = $this->property('category');
}
public function onRun()
{
// here you may want to do some checks
// and add logic before querying your DB
return $this->listImages($this->pageNumber , $this->sortOrder, $this->perPage, $this->category);
}
public function listImages($pageNumber, $sortOrder, $perPage, $category){
// this is the scope you will define in your Images Model
// to handle pagination, sorting, category filtering ect..
$images = Images::listFrontEnd([
'page' => $pageNumber,
'sort' => $sortOrder,
'perPage' => $perPage,
'category' => $category,
]);
// small helper if the pagination # is > than last page
// redirect to last page..
$lastPage = $images->lastPage();
if ($this->pageNumber > $lastPage && $this->pageNumber > 1){
return Redirect::to($this->currentPageUrl(["page" => $lastPage]));
}
$this->images = $this->page['images'] = $images;
}
in your Image Model :
// list the allowed sorting options that will show up in your component
public static $allowedSortingOptions = array(
'created_at asc' => 'Images Created (ascending)',
'created_at desc' => 'Images Created (descending)',
'updated_at asc' => 'Images Updated (ascending)',
// ect....
);
// Scope for your component
public function scopelistImages($query, $options)
{
/*
* Default options
*/
extract(array_merge([
'page' => 1,
'perPage' => 15,
'sort' => 'updated_at',
'category' => null
], $options));
// SORTING
if (!is_array($sort)) {
$sort = [$sort];
}
foreach ($sort as $_sort) {
if (in_array($_sort, array_keys(self::$allowedSortingOptions))) {
$parts = explode(' ', $_sort);
if (count($parts) < 2) {
array_push($parts, 'desc');
}
list($sortField, $sortDirection) = $parts;
if ($sortField == 'random') {
$sortField = Db::raw('RAND()');
}
$query->orderBy($sortField, $sortDirection);
}
}
// Filter by category
........
return $query->paginate($perPage, $page);
}
Answer inspired from the RainLab Blog Plugin
Upvotes: 2