Laravel保存了有序模型的有序列表

时间:2016-01-03 17:36:33

标签: laravel model drag-and-drop eloquent sorted

我正在创建一个食物菜单,管理员可以通过拖放来订购/排序。此菜单包含多个类别(ProductCategory)和产品(Product)。

我在客户端使用HTML5Sortable来允许嵌套d& d。标记非常简单:

<div class="categories">
    @foreach($categories as $category)
    <div class="category">
        @foreach($category->products as $product)
        <div class="products">
            <div class=""product" data=id="{{ $product->id }}">
                 {{ $product->name }}
            </div>
        </div><!-- /products !-->
        @endforeach
    </div><!-- /category !-->
    @endforeach
</div>

和相应的javascript:

$('.categories').sortable({
    items: '.category'
});
$('.products').sortable({
    items: '.product'
});

// Will be called when the user is done repositioning the products and categories
function getOrderedList() {
    var data = {};

    $('.categories').find('.category').map(function(i) {
        var category = $(this);
        data[i] = {};
        data[i].id = category.data('id');
        data[i].products = category.find('.product').map(function() {
            return $(this).data('id');
        }).get();
    });

    data = JSON.stringify(data); // Send data to server
}

函数getOrderedList将JSON字符串发送回Laravel,其中包含已排序的类别ID和产品ID:

{"0":{"id":1,"products":[2,3,1,4,5,6,7,8,9,10]},"1":{"id":2,"products":[11,12,13,14]},"2":{"id":3,"products":[15,16,17,18]}}

我如何在后端进行此项工作?我想我必须将这个数组存储在数据库的某个地方,然后通过id找到并订购模型?

简而言之:什么是排序(嵌套)模型的清洁灵活的解决方案(在Laravel中)?

3 个答案:

答案 0 :(得分:6)

常见的约定是权重,在产品表上添加一个名为(Int)Weight的字段,用于定义项目的顺序。

一旦订单发生变化,您只需更新权重字段。

检索项目时,按重量对其进行排序。

它变得类似于数组

Id        Name            Weight
01        'product 1'     2
02        'product 2'     0
03        'product 3'     1

当你按重量订购时,你会得到

product 2
product 3
product 1

它类似于数组,因为

$products[0] = 'product 2'
$products[1] = 'product 3'
$products[2] = 'product 1'

请注意,如果您想要使其更加动态,您可以创建一个可以满足多个模型的多态模型。

请参阅https://laravel.com/docs/5.1/eloquent-relationships#many-to-many-polymorphic-relations

多态关系示例

创建表权重(迁移示例)

$table->increments('id');
$table->integer('value');
$table->integer('weightable_id')->unsigned();
$table->string('weightable_type');

创建模型重量

class Weight extends Eloquent
{
    public function weightable()
    {
        return $this->morphTo();
    }
}

现在使用任何其他模型

class Products extends Eloquent
{
    ...
    public function weight()
    {
        return $this->morphOne(Weight::class);
    }
}

通过这种方式,您可以将该方法添加到您想要的任何模型中,然后就可以使用它对模型进行排序。

<强> P.S。确保使用它的任何模型在创建模型后立即创建该关系

我不推荐这种方法,如果你明确定义Products表中的权重字段会更好,我知道你希望你的代码有多大动态,但一切都需要付出代价

性能下降,一旦建立多态关系就不容易想象您的代码,更像是开始使用Jumps而不是函数

答案 1 :(得分:2)

首先,您生成的JSON不应该是键只是数组索引的对象。相反,它应该是一个对象数组,如下所示:

[{"id":1,"products":[2,3,1,4,5,6,7,8,9,10]},{"id":2,"products":[11,12,13,14]},{"id":3,"products":[15,16,17,18]}]

由于products表到product_categories表具有明显的多对一关系,因此您只需使用products表上的 product_categories_id 外键表示JSON中列出的关系。

在JSON的嵌套对象中, products 键数组中的每个值都将具有与同一嵌套对象中 id 键值对应的外键(这是products表上的 product_category_id 列。

您的API端点函数将如下所示:

public function myApiEndpoint(){

    $input = Input::get('input');
    $input = json_decode($input, true);
    foreach($input as $category){
        Product::whereIn('id', $category['products'])->update(array(
            'product_category_id' => $category['id']
        ));
    }

}

我在这里直接在API控制器中更新模型,但您应该通过同时实现接口的存储库进行任何模型更改。

如果你只有一个菜单(带有它的类别和产品),上面的方法就可以了。如果您需要多个菜单,那么您需要一个menus表以及一个三向数据透视表(包含menu_idproduct_idproduct_category_id列。

答案 2 :(得分:0)

我只是使用此库实现此行为: https://github.com/spatie/eloquent-sortable

实现非常简单,基本上,您需要一个额外的列来保持顺序,而库将完成其余工作,这是文档的一部分:

  • 实施Spatie \ EloquentSortable \ Sortable接口。
  • 使用特征Spatie \ EloquentSortable \ SortableTrait。
  • (可选)指定将哪个列用作订单列。默认值为order_column。

使用Spatie \ EloquentSortable \ Sortable; 使用Spatie \ EloquentSortable \ SortableTrait;

Class MyModel扩展了Eloquent实现的Sortable {

use SortableTrait;

public $sortable = [
    'order_column_name' => 'order_column',
    'sort_when_creating' => true,
];

...
}