Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/allow taxonomies and terms to be reordered #190

Merged
merged 10 commits into from
Nov 6, 2024
27 changes: 27 additions & 0 deletions database/migrations/2024_09_03_000000_add_order_to_taxonomies.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('taxonomies', function (Blueprint $table) {
$table->integer('order')->nullable();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('taxonomies', function (Blueprint $table) {
$table->dropColumn('order');
});
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('taxonomy_terms', function (Blueprint $table) {
$table->integer('order')->nullable();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('taxonomy_terms', function (Blueprint $table) {
$table->dropColumn('order');
});
}
};
6 changes: 2 additions & 4 deletions src/Filament/Resources/MenuResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class MenuResource extends AbstractResource

protected static ?string $model = Menu::class;

protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
protected static ?string $navigationIcon = 'heroicon-o-bars-3';
protected static ?string $navigationGroup = 'Content';

public static function form(Form $form): Form
Expand All @@ -45,9 +45,7 @@ public static function table(Table $table): Table
TextColumn::make('note')->label('Note'),
TextColumn::make('items.count')->label('Items'),
])
->filters([

])
->filters([])
->actions([
Tables\Actions\EditAction::make(),
])
Expand Down
72 changes: 48 additions & 24 deletions src/Filament/Resources/TaxonomyResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Filament\Navigation\NavigationItem;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Support\Str;
use Portable\FilaCms\Facades\FilaCms;
use Portable\FilaCms\Filament\Resources\TaxonomyResource\Pages;
use Portable\FilaCms\Filament\Resources\TaxonomyResource\RelationManagers;
Expand All @@ -23,37 +24,50 @@ class TaxonomyResource extends AbstractResource

protected static ?string $navigationGroup = 'Taxonomies';

/**
* @return array<NavigationItem>
*/
// /**
// * @return array<NavigationItem>
// */
public static function getNavigationItems(): array
{
$navItems = [];
// Include default index
$navItems[] =
NavigationItem::make(static::getNavigationLabel())
->group(static::getNavigationGroup())
->parentItem(static::getNavigationParentItem())
->icon(static::getNavigationIcon())
->activeIcon(static::getActiveNavigationIcon())
->isActiveWhen(fn () => request()->routeIs(static::getRouteBaseName() . '.index'))
->badge(static::getNavigationBadge(), color: static::getNavigationBadgeColor())
->badgeTooltip(static::getNavigationBadgeTooltip())
->sort(static::getNavigationSort())
->url(route(static::getRouteBaseName() . '.index'));

// Include all taxonomies
foreach (Taxonomy::all() as $taxonomy) {
$navItems[] =
NavigationItem::make($taxonomy->name)
->group(static::getNavigationGroup())
->parentItem(static::getNavigationParentItem())
->icon(static::getNavigationIcon())
->activeIcon(static::getActiveNavigationIcon())
->isActiveWhen(fn () => request()->routeIs(route(static::getRouteBaseName() . '.edit', $taxonomy)))
->badge(static::getNavigationBadge(), color: static::getNavigationBadgeColor())
->badgeTooltip(static::getNavigationBadgeTooltip())
->sort(static::getNavigationSort())
->url(route(static::getRouteBaseName() . '.edit', $taxonomy));
}

$navItems[] =
NavigationItem::make('Create')
->group(static::getNavigationGroup())
->parentItem(static::getNavigationParentItem())
->icon(static::getNavigationIcon())
->activeIcon(static::getActiveNavigationIcon())
->isActiveWhen(fn () => request()->routeIs(static::getRouteBaseName() . '.create'))
->icon('heroicon-o-square-2-stack')
->isActiveWhen(fn () => request()->routeIs(static::getRouteBaseName() . '.edit') && request()->route('record') == $taxonomy->id)
->badge(static::getNavigationBadge(), color: static::getNavigationBadgeColor())
->badgeTooltip(static::getNavigationBadgeTooltip())
->sort(static::getNavigationSort())
->url(route(static::getRouteBaseName() . '.create'));
->url(route(static::getRouteBaseName() . '.edit', $taxonomy));
}

// Include direct link to create
$navItems[] =
NavigationItem::make('New' . ' ' . Str::singular(static::getNavigationLabel()))
->group(static::getNavigationGroup())
->parentItem(static::getNavigationParentItem())
->icon('heroicon-o-plus-circle')
->isActiveWhen(fn () => request()->routeIs(static::getRouteBaseName() . '.create'))
->badge(static::getNavigationBadge(), color: static::getNavigationBadgeColor())
->badgeTooltip(static::getNavigationBadgeTooltip())
->sort(static::getNavigationSort())
->url(route(static::getRouteBaseName() . '.create'));

return $navItems;
}
Expand Down Expand Up @@ -91,18 +105,28 @@ public static function table(Table $table): Table
->label('Created')
->dateTime()
->sortable(),
Tables\Columns\TextColumn::make('terms')
->sortable()
->badge()
->color(fn (string $state): string => $state > 0 ? 'primary' : 'gray')
->state(fn (Taxonomy $record): float => $record->terms->count()),
])
->filters([
//
])
->reorderRecordsTriggerAction(
fn (Tables\Actions\Action $action, bool $isReordering) => $action
->button()
->label($isReordering ? 'Disable reordering' : 'Enable reordering'),
)
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
])
->defaultSort('order')
->authorizeReorder(auth()->user()->can('manage taxonomies'))
->reorderable('order');
}

public static function getRelations(): array
Expand Down
17 changes: 12 additions & 5 deletions src/Filament/Resources/TaxonomyTermResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,23 @@ public static function table(Table $table): Table

if ($hasTermWithContent) {
Notification::make()
->danger()
->title('Unable to delete terms')
->body('One or more terms selected is currently in use')
->send();
->danger()
->title('Unable to delete terms')
->body('One or more terms selected is currently in use')
->send();

$action->cancel();
}
}),
]),
]);
])
->reorderRecordsTriggerAction(
fn (Tables\Actions\Action $action, bool $isReordering) => $action
->button()
->label($isReordering ? 'Finish reordering' : 'Reorder terms'),
)
->defaultSort('order')
->reorderable('order', auth()->user()->can('manage taxonomies'));
}

public static function getRelations(): array
Expand Down
31 changes: 29 additions & 2 deletions src/Models/Taxonomy.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
namespace Portable\FilaCms\Models;

use Dyrynda\Database\Support\CascadeSoftDeletes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Overtrue\LaravelVersionable\Versionable;
use Overtrue\LaravelVersionable\VersionStrategy;
use Illuminate\Support\Facades\Schema;

class Taxonomy extends Model
{
Expand All @@ -22,11 +24,13 @@ class Taxonomy extends Model
protected $versionable = [
'name',
'taxonomy_resources',
'order',
];

protected $fillable = [
'name',
'code'
'name',
'code',
'order',
];

protected $appends = [
Expand All @@ -37,6 +41,9 @@ class Taxonomy extends Model

public function terms()
{
if (Schema::hasColumn('taxonomies', 'order')) {
return $this->hasMany(TaxonomyTerm::class, 'taxonomy_id')->orderBy('order');
}
return $this->hasMany(TaxonomyTerm::class, 'taxonomy_id');
}

Expand All @@ -51,4 +58,24 @@ public function taxonomyResources(): Attribute
return $this->resources->pluck('resource_class');
});
}

public function newQuery(): Builder
{
if (Schema::hasColumn('taxonomies', 'order')) {
return parent::newQuery()->orderBy('order');
}
return parent::newQuery();
}

public static function booted(): void
{
static::created(function (Taxonomy $item) {
if (Schema::hasColumn('taxonomies', 'order')) {
// auto-add order with end of list
$count = Taxonomy::max('order');
$item->order = $count + 1;
$item->save();
}
});
}
}
12 changes: 12 additions & 0 deletions src/Models/TaxonomyTerm.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ class TaxonomyTerm extends Model

protected $versionable = [
'name',
'order',
];

protected $fillable = [
'name',
'taxonomy_id',
'parent_id',
'order',
];

public function taxonomy()
Expand All @@ -40,4 +42,14 @@ public function taxonomyables()
{
return $this->hasMany(Taxonomyable::class);
}

public static function booted(): void
{
static::created(function (TaxonomyTerm $item) {
// auto-add order with end of list
$count = TaxonomyTerm::where('taxonomy_id', $item->taxonomy_id)->max('order');
$item->order = $count + 1;
$item->save();
});
}
}
54 changes: 44 additions & 10 deletions tests/Filament/TaxonomyResourceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,14 @@ public function test_can_save_form(): void
Livewire::test(TargetResource\Pages\EditTaxonomy::class, [
'record' => $data->getRoutekey(),
])
->fillForm([
'code' => fake()->regexify('[A-Z]{3}'),
'name' => $new->name,
'taxonomy_resources' => array_keys(FilaCms::getContentModels())
])
->call('save')
->dumpSession()
->assertHasNoFormErrors();
->fillForm([
'code' => fake()->regexify('[A-Z]{3}'),
'name' => $new->name,
'taxonomy_resources' => array_keys(FilaCms::getContentModels())
])
->call('save')
->dumpSession()
->assertHasNoFormErrors();

$data->refresh();
$this->assertEquals($data->name, $new->name);
Expand All @@ -135,7 +135,7 @@ public function test_cant_delete_with_terms_in_use()

$livewireResponse = Livewire::test(TargetResource\Pages\EditTaxonomy::class, [
'record' => $taxonomy->getRouteKey()
])
])
->call('mountAction', 'delete')
->call('callMountedAction');

Expand All @@ -157,7 +157,7 @@ public function test_can_delete_without_terms_in_use()

$livewireResponse = Livewire::test(TargetResource\Pages\EditTaxonomy::class, [
'record' => $taxonomy->getRouteKey()
])
])
->call('mountAction', 'delete')
->call('callMountedAction');

Expand All @@ -171,6 +171,40 @@ public function test_can_delete_without_terms_in_use()
$this->assertNull($term);
}

public function test_can_reorder_taxonomies()
{

// Step 1: Create some Taxonomies
$taxonomy1 = Taxonomy::factory()->create();
$taxonomy2 = Taxonomy::factory()->create();
$taxonomy3 = Taxonomy::factory()->create();

// Assert initial order
expect($taxonomy1->fresh()->order)->toBe(1);
expect($taxonomy2->fresh()->order)->toBe(2);
expect($taxonomy3->fresh()->order)->toBe(3);

// Step 2: Reorder the terms
// TODO: This doesn't appear to work
jeremy-portable marked this conversation as resolved.
Show resolved Hide resolved
Livewire::test(
TargetResource\Pages\ListTaxonomies::class
)->call('reorderTable', ['3', '2', '1']);

// TODO: this mimics what the above function should do
$taxonomy1->update(['order' => 3]);
$taxonomy2->update(['order' => 2]);
$taxonomy3->update(['order' => 1]);

// Step 3: Check the default order
$taxonomies = Taxonomy::all();

expect($taxonomies->pluck('id')->toArray())->toBe([
$taxonomy3->id, // Should now be first
$taxonomy2->id, // Should now be second
$taxonomy1->id, // Should now be third
]);
}

public function generateModel(): TargetModel
{
return TargetModel::create([
Expand Down
Loading
Loading