Skip to content

Commit

Permalink
Merge pull request #31 from reachweb/sort-filter-values
Browse files Browse the repository at this point in the history
Add sort capability
  • Loading branch information
afonic authored Oct 27, 2024
2 parents 4438b5e + c11c51d commit d3a8021
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 2 deletions.
14 changes: 14 additions & 0 deletions src/Exceptions/FieldOptionsCannotFindTaxonomyField.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Reach\StatamicLivewireFilters\Exceptions;

use Exception;

class FieldOptionsCannotFindTaxonomyField extends Exception
{
public function __construct($sortyBy, $handle)
{
$message = "Cannot find field [{$sortyBy}] in the taxonomy [{$handle}] in order to sort the filter options.";
parent::__construct($message);
}
}
14 changes: 14 additions & 0 deletions src/Exceptions/FieldOptionsCannotSortException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Reach\StatamicLivewireFilters\Exceptions;

use Exception;

class FieldOptionsCannotSortException extends Exception
{
public function __construct($sortyBy, $handle)
{
$message = "Cannot sort field [{$handle}] by [{$sortyBy}]. Maybe you have mixed up the field type?";
parent::__construct($message);
}
}
2 changes: 1 addition & 1 deletion src/Http/Livewire/LfCheckboxFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

class LfCheckboxFilter extends Component
{
use Traits\HandleEntriesCount, Traits\IsLivewireFilter;
use Traits\HandleEntriesCount, Traits\IsLivewireFilter, Traits\IsSortable;

public $view = 'lf-checkbox';

Expand Down
2 changes: 1 addition & 1 deletion src/Http/Livewire/LfRadioFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

class LfRadioFilter extends Component
{
use Traits\HandleEntriesCount, Traits\IsLivewireFilter;
use Traits\HandleEntriesCount, Traits\IsLivewireFilter, Traits\IsSortable;

public $view = 'lf-radio';

Expand Down
108 changes: 108 additions & 0 deletions src/Http/Livewire/Traits/IsSortable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php

namespace Reach\StatamicLivewireFilters\Http\Livewire\Traits;

use Livewire\Attributes\Locked;
use Reach\StatamicLivewireFilters\Exceptions\FieldOptionsCannotFindTaxonomyField;
use Reach\StatamicLivewireFilters\Exceptions\FieldOptionsCannotSortException;
use Statamic\Facades\Taxonomy;

trait IsSortable
{
#[Locked]
public $sort;

public function mountIsSortable(): void
{
if ($this->sort === null) {
return;
}

[$sortBy, $sortDirection] = explode(':', $this->sort);
$fieldType = $this->statamic_field['type'];
$fieldHandle = $this->statamic_field['handle'];

switch ($sortBy) {
case 'key':
case 'label':
if ($fieldType !== 'checkboxes' && $fieldType !== 'radio' && $fieldType !== 'select') {
throw new FieldOptionsCannotSortException($sortBy, $fieldHandle);
}
if ($sortBy === 'key') {
$this->sortOptionsByKey($sortDirection);
} else {
$this->sortOptionsByValue($sortDirection);
}
break;
case 'slug':
case 'title':
if ($fieldType !== 'terms') {
throw new FieldOptionsCannotSortException($sortBy, $fieldHandle);
}
if ($sortBy === 'slug') {
$this->sortOptionsByKey($sortDirection);
} else {
$this->sortOptionsByValue($sortDirection);
}
break;
default:
$this->sortCustomField($sortBy, $sortDirection);
}
}

protected function sortOptionsByKey($sortDirection): void
{
$options = collect($this->statamic_field['options']);

if ($sortDirection === 'asc') {
$options = $options->sortKeys();
} else {
$options = $options->sortKeysDesc();
}

$this->statamic_field['options'] = $options->all();
}

protected function sortOptionsByValue($sortDirection): void
{
$options = collect($this->statamic_field['options']);

if ($sortDirection === 'asc') {
$options = $options->sort();
} else {
$options = $options->sortDesc();
}

$this->statamic_field['options'] = $options->all();
}

// If the sortyBy is not one of the default ones, we need to search the terms of the field
protected function sortCustomField($sortBy, $sortDirection): void
{
$fieldType = $this->statamic_field['type'];
$fieldHandle = $this->statamic_field['handle'];

// If it's not a terms field, it can't have custom fields.
if ($fieldType !== 'terms') {
throw new FieldOptionsCannotSortException($sortBy, $fieldHandle);
}

$terms = $this->getTaxonomyTermsSortedBy($fieldHandle, $sortBy, $sortDirection);
}

protected function getTaxonomyTermsSortedBy($handle, $sortBy, $sortDirection): void
{
$taxonomy = Taxonomy::findByHandle($handle);

// Check if the field exists
if (! $taxonomy->termBlueprint()->fields()->all()->has($sortBy)) {
throw new FieldOptionsCannotFindTaxonomyField($sortBy, $handle);
}

$this->statamic_field['options'] = $taxonomy->queryTerms()->orderBy($sortBy, $sortDirection)->get()->flatMap(function ($term) {
return [
$term->slug() => $term->title(),
];
})->all();
}
}
128 changes: 128 additions & 0 deletions tests/Feature/LfCheckboxFilterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,134 @@ public function it_clears_the_value_when_clear_option_is_fired()
->assertSet('selected', []);
}

/** @test */
public function it_can_reorder_term_filter_values_by_slug()
{
Livewire::test(LfCheckboxFilter::class, ['field' => 'colors', 'blueprint' => 'clothes.clothes', 'condition' => 'taxonomy', 'sort' => 'slug:asc'])
->assertViewHas('statamic_field', function ($statamic_field) {
return array_keys($statamic_field['options']) === ['black', 'red', 'yellow'];
});
}

/** @test */
public function it_can_reorder_term_filter_values_by_title()
{
Livewire::test(LfCheckboxFilter::class, ['field' => 'colors', 'blueprint' => 'clothes.clothes', 'condition' => 'taxonomy', 'sort' => 'title:desc'])
->assertViewHas('statamic_field', function ($statamic_field) {
return array_keys($statamic_field['options']) === ['yellow', 'red', 'black'];
});
}

/** @test */
public function it_can_reorder_checkboxes_filter_values_by_key()
{
Livewire::test(LfCheckboxFilter::class, ['field' => 'item_options', 'blueprint' => 'pages.pages', 'condition' => 'is', 'sort' => 'key:desc'])
->assertViewHas('statamic_field', function ($statamic_field) {
return array_keys($statamic_field['options']) === ['option3', 'option2', 'option1'];
});
}

/** @test */
public function it_can_reorder_checkboxes_filter_values_by_value()
{
Livewire::test(LfCheckboxFilter::class, ['field' => 'item_options', 'blueprint' => 'pages.pages', 'condition' => 'is', 'sort' => 'label:desc'])
->assertViewHas('statamic_field', function ($statamic_field) {
return array_keys($statamic_field['options']) === ['option3', 'option2', 'option1'];
});
}

/** @test */
public function it_throws_an_exception_for_wrong_sort_parameter()
{
$this->expectExceptionMessage('Cannot sort field [item_options] by [slug]');

Livewire::test(LfCheckboxFilter::class, ['field' => 'item_options', 'blueprint' => 'pages.pages', 'condition' => 'is', 'sort' => 'slug:desc']);
}

/** @test */
public function it_throws_an_exception_for_wrong_sort_parameter_terms()
{
$this->expectExceptionMessage('Cannot sort field [colors] by [key]');
Livewire::test(LfCheckboxFilter::class, ['field' => 'colors', 'blueprint' => 'clothes.clothes', 'condition' => 'taxonomy', 'sort' => 'key:desc']);
}

/** @test */
public function it_can_reorder_term_filter_values_by_custom_field()
{
Facades\Blueprint::make()->setContents([
'sections' => [
'main' => [
'fields' => [
[
'handle' => 'order',
'field' => [
'type' => 'text',
'display' => 'Order',
],
],
],
],
],
])->setHandle('brand')->setNamespace('taxonomies.brand')->save();

Facades\Taxonomy::make('brand')->save();
Facades\Term::make()->taxonomy('brand')->inDefaultLocale()->slug('nike')->data(['title' => 'Nike', 'order' => '3'])->save();
Facades\Term::make()->taxonomy('brand')->inDefaultLocale()->slug('adidas')->data(['title' => 'Adidas', 'order' => '1'])->save();
Facades\Term::make()->taxonomy('brand')->inDefaultLocale()->slug('reebok')->data(['title' => 'Reebok', 'order' => '2'])->save();

// add to clothers blueprint
$clothesBlueprint = $this->blueprint = Facades\Blueprint::make()->setContents([
'sections' => [
'main' => [
'fields' => [
[
'handle' => 'title',
'field' => [
'type' => 'text',
'display' => 'Title',
],
],
[

'handle' => 'colors',
'field' => [
'type' => 'terms',
'taxonomies' => [
'colors',
],
],
],
[

'handle' => 'brand',
'field' => [
'type' => 'terms',
'taxonomies' => [
'brand',
],
],
],
],
],
],
]);
$clothesBlueprint->setHandle('clothes')->setNamespace('collections.clothes')->save();

// By default they are sorted by creation date
Livewire::test(LfCheckboxFilter::class, ['field' => 'brand', 'blueprint' => 'clothes.clothes', 'condition' => 'taxonomy'])
->assertViewHas('statamic_field', function ($statamic_field) {
return array_keys($statamic_field['options']) === ['nike', 'adidas', 'reebok'];
});

Livewire::test(LfCheckboxFilter::class, ['field' => 'brand', 'blueprint' => 'clothes.clothes', 'condition' => 'taxonomy', 'sort' => 'order:asc'])
->assertViewHas('statamic_field', function ($statamic_field) {
return array_keys($statamic_field['options']) === ['adidas', 'reebok', 'nike'];
});

$this->expectExceptionMessage('Cannot find field [something] in the taxonomy [brand]');
Livewire::test(LfCheckboxFilter::class, ['field' => 'brand', 'blueprint' => 'clothes.clothes', 'condition' => 'taxonomy', 'sort' => 'something:asc']);
}

protected function makeEntry($collection, $slug)
{
return EntryFactory::id($slug)->collection($collection)->slug($slug)->make();
Expand Down

0 comments on commit d3a8021

Please sign in to comment.