Skip to content

Commit

Permalink
Merge pull request #190 from PortableStudios/feat/allow-taxonomies-an…
Browse files Browse the repository at this point in the history
…d-terms-to-be-reordered

Feat/allow taxonomies and terms to be reordered
  • Loading branch information
jeremy-portable authored Nov 6, 2024
2 parents 63d5aaf + deeddc1 commit 681248f
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 45 deletions.
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();
});
}
}
48 changes: 38 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,34 @@ 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
Livewire::test(
TargetResource\Pages\ListTaxonomies::class
)->call('reorderTable', ['3', '2', '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

0 comments on commit 681248f

Please sign in to comment.