【问题标题】:Laravel saving ordered list of eloquent modelsLaravel 保存 eloquent 模型的有序列表
【发布时间】:2016-04-07 09:03:01
【问题描述】:

我正在创建一个食物菜单,管理员可以通过拖放来订购/排序。该菜单由多个类别(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 将向 Laravel 发送一个 JSON 字符串,其中包含排序后的类别 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 中)的简洁灵活的解决方案?

【问题讨论】:

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


    【解决方案1】:

    常见的约定是Weight,在products表中添加一个名为(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);
        }
    }
    

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

    附注确保使用它的任何模型在创建模型后立即创建该关系

    我不推荐这种方法,如果你在 Products 表中明确定义 weight 字段会更好,我理解你希望你的代码是动态的,但一切都是有代价的

    性能下降,一旦建立了多态关系就不容易可视化你的代码,更像是开始使用跳转而不是函数

    【讨论】:

    • 感谢您的回答,但我已经知道了。我想我实际上正在寻找一种多态解决方案,其中“重量”列不会存储在 Product 模型本身中,所以感谢您指出这一点。
    • 我将编辑答案以包含多态解决方案
    【解决方案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 列)。

    【讨论】:

    • 您正在更新产品模型中的category_id,我看不出这与我的“可排序”问题有何关系。
    • @JasonK 您在询问如何保存用户选择的产品和类别的组合。如果你问的是不同的问题,你真的应该仔细检查并修改你对问题的表述方式
    【解决方案3】:

    我只是使用这个库来实现这个行为: 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,
    ];
    
    ...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-06
      • 2018-01-23
      • 2018-10-20
      • 2014-08-13
      • 2020-01-08
      相关资源
      最近更新 更多