Quelle est la meilleure pratique dans Magento 2 pour créer des relations plusieurs à plusieurs?


15

J'ai regardé autour du cœur et vu quelques exemples de relations multiples entre modèles, mais je ne vois pas de réponse définitive à ce sujet.

Par exemple, disons que nous créons un nouveau modèle et que nous voulons avoir une relation plusieurs à plusieurs avec la table des produits existants.

Nous avons donc notre nouveau modèle - Stockist, et nous créons 2 tables en tant que telles, une pour stocker le nom de Stockist, l'autre pour stocker la relation plusieurs à plusieurs avec les produits.

Version tronquée des classes d'installation:

$table = $setup->getConnection()
        ->newTable($installer->getTable('stockist'))
        ->addColumn('stockist_id',
            \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
            null,
            ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
            'Stockist Id')
        ->addColumn('name',
            \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
            null,
            ['nullable' => false],
            'Stockist Name');

 $table = $installer->getConnection()
            ->newTable($installer->getTable('stockist_product'))
            ->addColumn(
                'entity_id',
                \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
                null,
                ['identity' => true, 'nullable' => false, 'primary' => true],
                'Entity ID'
            )
            ->addColumn(
                'stockist_id',
                \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
                null,
                ['unsigned' => true, 'nullable' => false, 'primary' => true, 'default' => '0'],
                'Stockist ID'
            )
            ->addColumn(
                'product_id',
                \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
                null,
                ['unsigned' => true, 'nullable' => false, 'primary' => true, 'default' => '0'],
                'Product ID'
            )
            ->addIndex(
                $installer->getIdxName('stockist_product', ['product_id']),
                ['product_id']
            )
            ->addIndex(
                $installer->getIdxName(
                    'stockist_product,
                    ['stockist_id', 'product_id'],
                    \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_UNIQUE
                ),
                ['stockist_id', 'product_id'],
                ['type' => \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_UNIQUE]
            )
            ->addForeignKey(
                $installer->getFkName('stockist_product', 'product_id', 'catalog_product_entity', 'entity_id'),
                'product_id',
                $installer->getTable('catalog_product_entity'),
                'entity_id',
                \Magento\Framework\DB\Ddl\Table::ACTION_CASCADE
            )
            ->addForeignKey(
                $installer->getFkName('stockist_product', 'stockist_id', 'stockist', 'stockist_id'),
                'stockist_id',
                $installer->getTable('stockist'),
                'stockist_id',
                \Magento\Framework\DB\Ddl\Table::ACTION_CASCADE
            )
            ->setComment('Stockist to Product Many to Many');

Ensuite, nous créons un modèle / ResourceModel / Collection standard pour Stockist comme suit:

namespace OurModule\Stockist\Model;

use Magento\Framework\Model\AbstractModel;

class Stockist extends AbstractModel
{

    protected function _construct()
    {
        $this->_init('OurModule\Stockist\Model\ResourceModel\Stockist');
    }

}

namespace OurModule\Stockist\Model\ResourceModel;

use Magento\Framework\Model\ResourceModel\Db\AbstractDb;

class Stockist extends AbstractDb
{

    protected function _construct()
    {
        $this->_init('stockist', 'stockist_id');
    }

}

namespace OurModule\Stockist\Model\ResourceModel\Stockist;

use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;

class Collection extends AbstractCollection
{

    public function _construct()
    {
        $this->_init('OurModule\Stockist\Model\Stockist', 'OurModule\Stockist\Model\ResourceModel\Stockist');
    }

}

C'est là que nous arrivons à la façon de gérer la table avec la relation plusieurs à plusieurs. Jusqu'à présent, j'ai trouvé quelque chose dans le sens de cela.

Créer un modèle pour représenter StockistProduct

namespace OurModule\Stockist\Model;

use Magento\Framework\Model\AbstractModel;

class StockistProduct extends AbstractModel
{

protected function _construct()
{
    $this->_init('OurModule\Stockist\Model\ResourceModel\StockistProduct');
}

/**
 * @param array $productIds
 */
public function getStockists($productIds)
{
    return $this->_getResource()->getStockists($productIds);
}

/**
 * @param array $stockistIds
 */
public function getProducts($stockistIds)
{
    return $this->_getResource()->getProducts($stockistIds);
}
}

Voici la définition de 2 méthodes qui prendront soit un tableau d'ID de revendeur, retournant un tableau d'ID de produit correspondants et vice-versa.

Cela utilise un modèle de ressource pour la table stockist_product contenant la relation plusieurs à plusieurs:

/**
 * Class StockistProduct
 */
class StockistProduct extends AbstractDb
{
    /**
     * Model initialization
     *
     * @return void
     */
    protected function _construct()
    {
        $this->_init('stockist_product', 'entity_id');
    }

    /**
     * Retrieve product stockist Ids
     *
     * @param array $productIds
     * @return array
     */
    public function getStockists(array $productIds)
    {
        $select = $this->getConnection()->select()->from(
            $this->getMainTable(),
            ['product_id', 'stockist_id']
        )->where(
            'product_id IN (?)',
            $productIds
        );
        $rowset = $this->getConnection()->fetchAll($select);

        $result = [];
        foreach ($rowset as $row) {
            $result[$row['product_id']][] = $row['stockist_id'];
        }

        return $result;
    }


    /**
     * Retrieve stockist product Ids
     *
     * @param array $stockistIds
     * @return array
     */
    public function getProducts(array $stockistIds)
    {
        $select = $this->getConnection()->select()->from(
            $this->getMainTable(),
            ['product_id', 'stockist_id']
        )->where(
            'stockist_id IN (?)',
            $stockistIds
        );
        $rowset = $this->getConnection()->fetchAll($select);

        $result = [];
        foreach ($rowset as $row) {
            $result[$row['product_id']][] = $row['stockist_id'];
        }

        return $result;
    }
}

Ensuite, en utilisant ce modèle StockistProduct lorsque vous devez récupérer un ensemble de l'un ou l'autre modèle comme tel, en supposant que nous avons un modèle de produit dans $ product et $ stockistProduct est une instance de \ OurModule \ Stockist \ Model \ StockistProduct

$stockists = $stockistProduct->getStockists([$product->getId()]);

Nous pouvons ensuite créer chaque modèle tour à tour en bouclant la liste des identifiants renvoyés comme tels, où $ stockistFactory est une instance de \ OurModule \ Stockist \ Model \ StockistFactory

$stockist = $this->stockistFactory->create();
$stockist->load($stockistId);

Tout cela fonctionne bien et est basé sur un code similaire dans le noyau de Magento 2, mais je ne peux m'empêcher de me demander s'il existe une meilleure façon?


Je dois faire quelque chose de très similaire ... et c'est la seule idée que j'ai, s'il n'y a pas de réponses :(
slayerbleast

Réponses:


1

J'ai implémenté une solution similaire à celle-ci. Pour chaque SKU, il y avait des informations de "montage": année, marque, modèle de voiture auquel le produit (accessoire de voiture) pouvait être appliqué. À première vue, ce serait plus facile avec des attributs natifs de Magento. Utilisez simplement trois champs de texte, un pour l'année, un pour la marque, un pour le modèle. Cela permet toutes les fonctionnalités intégrées de Magento, comme la recherche et le filtrage avec ces attributs, ainsi qu'une mise à jour facile à l'avenir.

Le problème, comme vous le décrivez, est que nous avons besoin de «beaucoup» de ces relations. Nous pourrions créer 30 attributs de texte: year1, make1, model1, year2, make2, model2, ... year10, make10, model10. Cela a) laisserait probablement de nombreux attributs vides et b) créerait une limite artificielle sur le nombre de voitures prises en charge par un produit.

Ce qui pourrait fonctionner est quelque chose comme ceci:

Year: ____
Make: ____
Model: ____

Add new YearMakeModel relationship (+)

Et puis après avoir cliqué sur le plus (+), vous verrez:

Year: ____
Make: ____
Model: ____

Year: ____
Make: ____
Model: ____

Add new YearMakeModel relationship (+)

Une telle interface utilisateur pourrait être implémentée avec javascript dans un modèle de thème soutenu. Lors de l'envoi du formulaire, vous devrez fournir ces données à Magento en tant qu'attributs de produit. Je ne pense pas qu'il existe actuellement un type d'attribut qui prend en charge une longueur dynamique. Vous implémenteriez un type d'attribut personnalisé. Encore une fois, cela prend en charge la fonctionnalité Magento intégrée: recherche sur les attributs saisis, mise à jour facile pour ces attributs à l'avenir.

En fin de compte, notre client a pris la décision d'économiser de l'argent en ne mettant pas en œuvre cette «modification facile», et au lieu de cela, nous avons verrouillé les données dans un tableau personnalisé, comme vous le décrivez. J'ai un script d'importation personnalisé qui prend les entrées et sorties CSV dans la table. Plus tard, la page du produit (enfin, son bloc) interroge ce tableau, extrait les informations sur son SKU et s'affiche pour l'utilisateur sous forme de tableau. Ce tableau de page de produit était le comportement souhaité du client, donc pour nous, il n'était pas logique de creuser en le faisant "The Magento Way" et en implémentant un attribut de membre variable.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.