rutorika / sortable
为 Laravel Eloquent 模型添加可排序行为和排序。支持分组和多对多。
Requires
- php: >=7.2.5
- illuminate/support: ^7.0|^8.0|^9.0|^10.0|^11.0
Requires (Dev)
- orchestra/testbench: ~5|~6
- phpunit/phpunit: ~8
This package is auto-updated.
Last update: 2024-09-20 12:02:42 UTC
README
Laravel 5 - 示例
https://github.com/boxfrommars/rutorika-sortable-demo5
安装
通过 Composer 安装包
composer require rutorika/sortable
版本兼容性
可排序特质
为 Eloquent (Laravel) 模型添加可排序行为
用法
将 position
字段添加到您的模型中(下面将展示如何更改此名称)
// schema builder example public function up() { Schema::create('articles', function (Blueprint $table) { // ... other fields ... $table->integer('position'); // Your model must have position field: }); }
将 \Rutorika\Sortable\SortableTrait
添加到您的 Eloquent 模型。
class Article extends Model { use \Rutorika\Sortable\SortableTrait; }
如果您想使用自定义的位置列名,设置 $sortableField
class Article extends Model { use \Rutorika\Sortable\SortableTrait; protected static $sortableField = 'somefield'; }
现在您可以使用 moveBefore($entity)
和 moveAfter($entity)
方法移动您的实体(您不需要在之后保存模型,它已经保存过了)
$entity = Article::find(1); $positionEntity = Article::find(10) $entity->moveAfter($positionEntity); // if $positionEntity->position is 14, then $entity->position is 15 now
此外,此特质会自动在 create
事件上定义实体位置,因此您不需要手动添加 position
,只需像往常一样创建实体即可
$article = new Article(); $article->title = $faker->sentence(2); $article->description = $faker->paragraph(); $article->save();
此实体将位于 entitiesMaximumPosition + 1
位置
要获取排序后的实体,请使用 sorted
范围
$articles = Article::sorted()->get();
** 注意 **:删除记录后不会重新排序。位置值中的间隙不会影响列表的排序。然而,如果您希望防止间隙,可以使用
deleting
事件重新定位模型。例如:
// YourAppServiceProvider YourModel::deleting(function ($model) { $model->next()->decrement('position'); });
您需要 rutorika-sortable >=2.3 才能使用
->next()
可排序组
如果您想按字段对实体排序进行分组,请将其添加到模型中
protected static $sortableGroupField = 'fieldName';
现在移动和排序将由此字段封装。
如果您想按多个字段对实体排序进行分组,请使用数组
protected static $sortableGroupField = ['fieldName1','fieldName2'];
可排序多对多
假设您的数据库结构如下
posts
id
title
tags
id
title
post_tag
post_id
tag_id
并且您想为每个 post 排序 tags
将 position
列添加到关联表中(您可以使用任何您想要的名称,但默认使用 position
)
post_tag
post_id
tag_id
position
将 \Rutorika\Sortable\BelongsToSortedManyTrait
添加到您的 Post
模型中,并定义此特质提供的 belongsToSortedMany
关系
class Post extends Model { use BelongsToSortedManyTrait; public function tags() { return $this->belongsToSortedMany('\App\Tag'); } }
注意:
$this->belongsToSortedMany
与$this->belongsToMany
的签名不同--此方法的第二个参数是$orderColumn
(默认为'position'
),后面的参数相同
使用 save
/sync
/attach
方法将标签附加到帖子时,将设置正确的位置
$post->tags()->save($tag) // or $post->tags()->attach($tag->id) // or $post->tags()->sync([$tagId1, $tagId2, /* ...tagIds */])
获取相关模型是按位置排序的
$post->tags; // ordered by position by default
您可以为给定的帖子重新排序标签
$post->tags()->moveBefore($entityToMove, $whereToMoveEntity); // or $post->tags()->moveAfter($entityToMove, $whereToMoveEntity);
多对多示例:http://sortable5.boxfrommars.ru/posts (代码)
您还可以通过使用 MorphsToSortedManyTrait
特质并从关系方法返回 $this->morphToSortedMany()
来使用可排序行为的多态多对多关系。
遵循 Laravel 多态多对多表关系,您的表应如下所示
posts
id
title
tags
id
title
taggables
tag_id
position
taggable_id
taggable_type
并且您的模型如下
class Post extends Model { use MorphToSortedManyTrait; public function tags() { return $this->morphToSortedMany('\App\Tag', 'taggable'); } }
可排序控制器
此外,此包还提供了 \Rutorika\Sortable\SortableController
,用于处理对实体排序的请求
用法
将服务提供者添加到 config/app.php
'providers' => array( // providers... 'Rutorika\Sortable\SortableServiceProvider', )
发布配置
php artisan vendor:publish
将您需要排序的模型添加到配置 config/sortable.php
'entities' => array( 'articles' => '\App\Article', // entityNameForUseInRequest => ModelName // or 'articles' => ['entity' => '\App\Article'], // or for many to many 'posts' => [ 'entity' => '\App\Post', 'relation' => 'tags' // relation name (method name which returns $this->belongsToSortedMany) ] ),
将路由添加到控制器的 sort
方法
Route::post('sort', '\Rutorika\Sortable\SortableController@sort');
现在,如果您向此路由发送有效数据
$validator = \Validator::make(\Input::all(), array( 'type' => array('required', 'in:moveAfter,moveBefore'), // type of move, moveAfter or moveBefore 'entityName' => array('required', 'in:' . implode(',', array_keys($sortableEntities))), // entity name, 'articles' in this example 'positionEntityId' => 'required|numeric', // id of relative entity 'id' => 'required|numeric', // entity id )); // or for many to many $validator = \Validator::make(\Input::all(), array( 'type' => array('required', 'in:moveAfter,moveBefore'), // type of move, moveAfter or moveBefore 'entityName' => array('required', 'in:' . implode(',', array_keys($sortableEntities))), // entity name, 'articles' in this example 'positionEntityId' => 'required|numeric', // id of relative entity 'id' => 'required|numeric', // entity id 'parentId' => 'required|numeric', // parent entity id ));
则具有 \Input::get('id')
id 的实体将与具有 \Input::get('positionEntityId')
id 的实体相对移动。
例如,如果请求数据是
type:moveAfter
entityName:articles
id:3
positionEntityId:14
然后,ID为3的文章将被移动到ID为14的文章之后。
jQuery UI可排序示例
注意:Laravel 5 默认启用了CSRF中间件,因此您应该设置ajax请求:https://laravel.net.cn/docs/5.0/routing#csrf-protection
模板
<table class="table table-striped table-hover"> <tbody class="sortable" data-entityname="articles"> @foreach ($articles as $article) <tr data-itemId="{{{ $article->id }}}"> <td class="sortable-handle"><span class="glyphicon glyphicon-sort"></span></td> <td class="id-column">{{{ $article->id }}}</td> <td>{{{ $article->title }}}</td> </tr> @endforeach </tbody> </table>
多对多排序的模板
<table class="table table-striped table-hover"> <tbody class="sortable" data-entityname="posts"> @foreach ($post->tags as $tag) <tr data-itemId="{{ $tag->id }}" data-parentId="{{ $post->id }}"> <td class="sortable-handle"><span class="glyphicon glyphicon-sort"></span></td> <td class="id-column">{{ $tag->id }}</td> <td>{{ $tag->title }}</td> </tr> @endforeach </tbody> </table>
/** * * @param type string 'insertAfter' or 'insertBefore' * @param entityName * @param id * @param positionId */ var changePosition = function(requestData){ $.ajax({ 'url': '/sort', 'type': 'POST', 'data': requestData, 'success': function(data) { if (data.success) { console.log('Saved!'); } else { console.error(data.errors); } }, 'error': function(){ console.error('Something wrong!'); } }); }; $(document).ready(function(){ var $sortableTable = $('.sortable'); if ($sortableTable.length > 0) { $sortableTable.sortable({ handle: '.sortable-handle', axis: 'y', update: function(a, b){ var entityName = $(this).data('entityname'); var $sorted = b.item; var $previous = $sorted.prev(); var $next = $sorted.next(); if ($previous.length > 0) { changePosition({ parentId: $sorted.data('parentid'), type: 'moveAfter', entityName: entityName, id: $sorted.data('itemid'), positionEntityId: $previous.data('itemid') }); } else if ($next.length > 0) { changePosition({ parentId: $sorted.data('parentid'), type: 'moveBefore', entityName: entityName, id: $sorted.data('itemid'), positionEntityId: $next.data('itemid') }); } else { console.error('Something wrong!'); } }, cursor: "move" }); } });
开发
sudo docker build -t rutorika-sortable .
sudo docker run --volume $PWD:/project --rm --interactive --tty --user $(id -u):$(id -g) rutorika-sortable vendor/bin/phpunit