Laravel 5

Table pivot, bonne pratique ?!

  • Avatar de BrunoCarrulla
    Membre depuis :
    25/04/2018
    Messages :
    4

    Bonjour,

    Je suis face à une problématique que j'aimerais bien gérer selon les bonnes pratiques de Laravel.

    J'ai une table "prestations", une table "products" et une table pivot "prestation_product". Une prestation contient 0,n product et un produit peut être contenu dans 0 ou n prestations.

    Par exemple la prestation "Forfait A", contient "produit 1" ET "produit 3" et "produit 8".

    Dans la vue qui permet de gérer les prestations, j'édite le "Forfait A", et un DataTables m'affiche l'ensemble des products disponibles avec un checkbox sur les products 1, 3 et 8.

    Jusque là tout va bien.

    Maintenant je veux modifier ce "Forfait A" en ajoutant le "product 2", lorsque je valide le formulaire c'est la méthode "update" du controleur "PrestationsController" qui agit.

    En ce qui concerne les informations générales de la prestation, comme son nom, sa version, sa description, sa catégorie etc, aucun soucis je met à jour avec :

    $prestation->update($request->except(['products_table_length']));

    Au passage je suis obligé de faire un except 'products_table_length' qui est une entrée générée par DataTables, mais ça c'est OK ça fonctionne.

    Par contre quelle est la bonne méthode pour mettre à jour la table pivot pour ajouter le "produit 2" que j'ai coché?

    Dois-je mémoriser les valeurs cochées dans un array au niveau de la vue et faire un foreach attach/detach dans le controleur?

    Ou existe-t-il une méthode appropriée dans Laravel?

    Merci !

  • Avatar de BrunoCarrulla
    Membre depuis :
    25/04/2018
    Messages :
    4

    J'ai mis en place un mécanisme qui fonctionne, c'est juste que j'aimerais savoir si c'est la bonne façon de procéder.

    Dans la vue "edit" j'ai ajouté un input hidden :

    {!! Form::hidden('chk_products', null, array('id' => 'chk_products')) !!}

    Ensuite un petit javascript qui actualise la valeur de cet input à chaque fois qu'on ajoute/supprime un produit :

    $('#products_table').on('click', function (e) {
    var chk_products = [];
    $('#products_table tr').each(function(index) {
    if($(this).hasClass( "selected" ))
    chk_products.push($(this).attr('data-entry-id'));
    });
    $('#chk_products').val(chk_products);
    });

    Puis le controleur qui gère l'update (on détache tout, puis on attache chaque produit sélectionné) :

    if($request->input('chk_products')){
    $prestation->linked_products()->detach();
    $chk_products = explode(',', $request->input('chk_products'));
    foreach ($chk_products as $chk_product){
    $prestation->linked_products()->attach($chk_product);
    }
    }

    Pour info le modèle qui contient la méthode linked_product()

    public function linked_products()
    {
    return $this->belongsToMany(Product::class);
    }
  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    1998

    Salut,

    En général on se simplifie la vie avec la méthode sync.

  • Avatar de BrunoCarrulla
    Membre depuis :
    25/04/2018
    Messages :
    4

    Salut Momo the best !

    effectivement j'ai vu cette méthode sync mais je crois que je n'ai pas su l'adapter à mon exemple. Je vais creuser la question.

    Merci à toi

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    1998

    Plutôt que de tout détacher à l'arrivée et de faire une boucle pour attacher les produits sélectionnés la méthode sync permet de synchroniser en appliquant directement le tableau des produits sélectionnés.

  • Avatar de BrunoCarrulla
    Membre depuis :
    25/04/2018
    Messages :
    4

    Parfait j'ai compris la technique, en fait j'ai besoin de mettre à jour 2 champs dans la table pivot, voilà comment j'ai procédé :

    Dans la vue "edit"

    {!! Form::hidden('checked_products', null, array('id' => 'checked_products')) !!}

    Le javascript qui récupère l'ensemble des produits cochés pour la prestation éditée :
    Je stocke la liste id/quantity dans un JSON qui est envoyé par l'input hidden checked_products lors de la validation du formulaire

    $('#products_table').on('click', function () {
    $('#products_table tr').each(function(index) {
    if(!index) return true;
    if($(this).hasClass( "selected" )){
    var id = $(this).attr('data-entry-id');
    var quantity = $(this).find('td:eq(3) input').val();
    checked_products[id] = { 'quantity': quantity };
    }
    });
    $('#checked_products').val(JSON.stringify(checked_products));
    });

    Ensuite le controller récupère le JSON et traite la table pivot avec un sync :

    public function update(Request $request, Prestation $prestation)
    {
    // Mise à jour des informations de la prestation
    $prestation->update($request->except(['products_table_length', 'checked_products']));

    // Mise à jour des produits contenus dans la prestation
    if($checked_products = json_decode($request->input('checked_products'), TRUE)){
    $prestation->linked_products()->sync($checked_products);
    }

    // Retour à la liste des prestations
    return redirect()->route('admin.prestations.index');
    }

    Je pense que ça correspond à la pratique Laravel non?

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    1998

    Salut,

    Disons que c'est la façon la plus optimisée ;)

  • Avatar de F.M.
    Membre depuis :
    10/07/2017
    Messages :
    97

    Pardon de faire du prosélytisme mais passez plutôt à doctrine. Laravel je l'aime quand il met une couche au-dessus de Symfony mais quand il fait de l'active record il m'agace. C'est facile de se moquer du principe "Single Responsibility", sauf qu'il s'appuie sur des années d'expériences de développeurs chevronnés, et que tous les vieux grognards finissent par en faire un mantra.

    En fait, vous avez raison d'expérimenter le mailstrom de l'active record, et ensuite, vous comprendrez enfin le SRP.

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    1998

    Salut,

    Tu vas finir par me convaincre de m'y mettre mais... c'est tellement facile avec Eloquent :)

Vous ne pouvez pas répondre à ce sujet.