Skip to content

Latest commit

 

History

History
196 lines (155 loc) · 4.55 KB

ordering.md

File metadata and controls

196 lines (155 loc) · 4.55 KB

Ordering

Sometimes you want to customize order of the resources in the collection. In this case you need complex solution.

Thirst of all it must be a field in a table to store position number.

// migration

Schema::create('posts', static function (Blueprint $table) {
    $table->unsignedInteger('id')->autoIncrement();
    $table->string('title');
    $table->date('pub_date');
    $table->boolean('active')->default(true);
    $table->string('slug')->unique();
    $table->unsignedInteger('position')->default(1); // ordering resource
    $table->timestamps();
    $table->softDeletes();
});

Then add ModelOrder trait to the model.

<?php
declare(strict_types=1);

namespace App;

use SP\Admin\Models\Model;
use SP\Admin\Traits\ModelOrder;

class Post extends Model
{
    use ModelOrder;
    
    public function orderable(): string
    {
        // change field name
        // by default it is "position"
        return 'position';
    }
    
    public function orderGroups(): array
    {
        // grouping attributes
        // if they exists
        return ['parent_id', 'type'];
    }
    
}

Define observer to auto-reorder models on create and delete events.

<?php
declare(strict_types=1);

namespace App\Observers;

use App\Post;

/**
 * Class PostObserver.
 *
 * @package App\Observers
 */
final class PostObserver
{
    /**
     * Handle the post "creating" event.
     *
     * @param Post $post
     */
    public function creating(Post $post): void
    {
        $post->lastPosition(false);
    }

    /**
     * Handle the post "force deleted" event.
     *
     * @param Post $post
     */
    public function forceDeleted(Post $post)
    {
        $post->removeAndReorder();
    }

}

Create handler to save changes with positions.

// routes/web.php

use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Support\Facades\Route;
use SP\Admin\Http\Middleware\Locale;

Route::prefix('admin')
    ->name('admin.')
    ->middleware([
        'web',
        Locale::class,
        AuthenticateSession::class,
    ])->group(static function () {
        // posts
        Route::match(['put', 'patch'], 'posts/sort', 'Admin\PostController@sort')->name('posts.sort');
        Route::resource('posts', 'Admin\PostController');
    });
<?php
declare(strict_types=1);

namespace App\Http\Controllers\Admin;

use App\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use SP\Admin\Http\Controllers\AdminController;

class PostController extends AdminController
{
    public function sort(Request $request): RedirectResponse
    {
        $data = $request->validate([
            'sorted_ids' => 'json',
        ]);

        $ids = \json_decode($data['sorted_ids'], true, 512, JSON_THROW_ON_ERROR);

        foreach ($ids as $index => $id) {
            $model = Post::withTrashed()->find($id);
            if ($model !== null) {
                $model->moveToPosition($index + 1); // reorder and save
            }
        }

        return redirect()
            ->route('admin.posts.index')
            ->with('success', trans('The Records has been sorted.'));
    }
}

Finally add sortable grid to the view.

// resources/views/admin/posts/index.blade.php

<x-model-sort input-name="sorted_ids" grid-columns="5" :action="route('admin.posts.sort')">
    @each('admin.posts._sort_item', $sorting_models, 'sorting_model')
</x-model-sort>

Where input_name is the post request field with ordered list and grid_columns is the number of columns in each grid row. This settings are optional.

@each directive contains the view name. This view is markup for each grid cell.

// resources/views/admin/posts/_sort_item.blade.php

@php $item_class = '' @endphp
@if(!$sorting_model['active']) @php $item_class = 'bg-light border-light text-muted' @endphp @endif
@if($sorting_model['trashed']) @php $item_class = 'bg-light border-danger text-muted' @endphp @endif

<div class="sortable-item" data-id="{{ $sorting_model['id'] }}">
    <div class="card {{ $item_class }}">
        <div class="card-body">
            <h5 class="card-title">{{ $sorting_model['title'] }}</h5>
        </div>
        <div class="card-footer">
            {{ $sorting_model['position'] }}
            @if(!$sorting_model['active']) ({{ __('Not Active') }}) @endif
            @if($sorting_model['trashed']) ({{ __('Deleted') }}) @endif
        </div>
    </div>
</div>

Necessarily you need to define container with sortable-item class and data-id attribute with the resource ID.


Table of contents