Laravel saving liste des modèles éloquents commandés

je crée un menu alimentaire que l'administrateur peut commander/trier en glissant et en laissant tomber. Ce menu se compose de plusieurs catégories (catégorie de produits) et de Produits (Produit).

j'utilise HTML5Sortable sur le côté client pour permettre imbriquée d&d. Le balisage est assez simple:

<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>

Et le 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
}

La fonction getOrderedList renvoie une chaîne JSON à Laravel, qui contient la catégorie triée id et l'id du produit:

{"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]}}

Comment faire pour que ça marche à l'arrière? Je suppose que je dois stocker ce tableau quelque part dans la base de données et plus tard trouver et commander les modèles par les id's?

en bref: Qu'est-ce qu'une solution propre et flexible pour trier (emboîter) les modèles (au sein de Laravel)?

12
demandé sur JasonK 2016-01-03 20:36:32

2 réponses

Une convention commune est de Poids, ajouter un champ appelé (Int)Poids sur la table produits, qui est utilisé pour définir l'ordre des éléments.

une Fois qu'un changement dans l'ordre se produit, vous ne mettez à jour le champ de poids.

lorsque vous récupérez les articles, vous les Triez par poids.

il devient semblable à un Tableau

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

quand vous le commandez par poids vous obtenez

product 2
product 3
product 1

c'est similaire à un tableau parce que

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

Notez que si vous souhaitez rendre encore plus dynamique, vous pouvez créer un polymorphe modèle qui peut satisfaire plusieurs modèles.

prière de consulter https://laravel.com/docs/5.1/eloquent-relationships#many-to-many-polymorphic-relations

Polymorphes Relations exemple

Create table Poids (exemple de migration)

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

Créer un modèle Poids

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

maintenant avec n'importe quel autre modèle

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

de cette façon, vous pouvez simplement ajouter que la méthode à tous les modèles que vous voulez, alors vous pouvez trier votre modèle.

P.S. assurez-vous que tout modèle qui l'utilise, crée cette relation immédiatement après avoir créé le modèle

Je ne recommande pas cette méthode, il est beaucoup mieux si vous définissez explicitement le champ de poids dans la table des produits, je comprends combien vous voulez que votre code soit dynamique, mais tout vient à un coût

les performances diminuent, il n'est pas facile de visualiser votre code une fois que vous établissez des relations polymorphiques, c'est plus comme commencer à utiliser des sauts au lieu de fonctions

5
répondu UX Labs 2016-01-17 21:05:10

tout d'abord, le JSON que vous produisez ne devrait pas être un objet où les clés ne sont que des indices de tableaux. Au lieu de cela, il devrait être un tableau d'objets qui ressemble à ceci:

[{"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]}]

Depuis le products tableau product_categories table a une évidente plusieurs à une relation, vous devez juste utiliser le product_categories_id clé étrangère sur la products table pour représenter les relations exposées dans votre JSON.

dans les objets imbriqués de votre JSON, chaque valeur de la produits le tableau de clés aura une clé étrangère qui correspond au id valeur clé dans le même objet imbriqué (c'est la product_category_id colonne products tableau).

votre fonction de paramètre API ressemblerait alors à quelque chose comme ceci:

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']
        ));
    }

}

je suis en train de mettre à jour le model directement dans le controller API ici, mais vous devriez vraiment faire toutes les modifications de model via un dépôt qui est aussi la mise en œuvre d'une interface.

ce qui précède fonctionnera si vous n'avez qu'un seul menu (avec ses catégories et produits). Si vous voulez plusieurs menus, puis vous aurez besoin d'un menus table ainsi qu'une table à trois axes (avec colonnes menu_id,product_id et product_category_id).

2
répondu Lloyd Banks 2016-01-15 22:10:21