¿Cuál es la mejor práctica en Magento 2 para crear relaciones Muchos a Muchos?

15

He mirado alrededor del núcleo y he visto algunos ejemplos de relaciones de muchos a muchos entre modelos, pero no puedo ver una respuesta definitiva sobre esto.

Como ejemplo, supongamos que creamos un nuevo modelo y queremos tener una relación de muchos a muchos con la tabla de productos existentes.

Entonces tenemos nuestro nuevo Modelo - Stockist, y creamos 2 tablas como tales, una para almacenar el nombre de Stockist, la otra para almacenar la relación de muchos a muchos con los productos.

Versión truncada de las clases de configuración:

$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');

Luego creamos un Modelo / Modelo de recurso / Colección estándar para Stockist de la siguiente manera:

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

}

Aquí es donde llegamos a cómo manejar la tabla con la relación de muchos a muchos. Hasta ahora se me ocurrió algo similar a esto.

Crear un modelo para representar 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);
}
}

Aquí se definen 2 métodos que tomarán una matriz de ID de stock, devolviendo una matriz de ID de producto coincidentes y viceversa.

Esto utiliza un modelo de recursos para la tabla stockist_product que contiene la relación de muchos a muchos:

/**
 * 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;
    }
}

Luego, use este modelo de StockistProduct cuando necesite recuperar un conjunto de cualquiera de los modelos, suponiendo que tengamos un Modelo de producto en $ product y $ stockistProduct sea una instancia de \ OurModule \ Stockist \ Model \ StockistProduct

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

Luego podemos crear cada modelo por turno haciendo un bucle en la lista de ID devueltos, donde $ stockistFactory es una instancia de \ OurModule \ Stockist \ Model \ StockistFactory

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

Todo esto funciona bien y se basa en un código similar dentro del núcleo de Magento 2, pero no puedo evitar preguntarme si hay una mejor manera.

Chris
fuente
Tengo que hacer algo muy similar ... y esta es la única idea que tengo, si no hay respuestas :(
slayerbleast

Respuestas:

1

Implementé una solución similar a esta. Para cada SKU, había información de "ajuste": año, marca, modelo de un automóvil al que se podía aplicar el producto (accesorio para el automóvil). A primera vista, esto sería más fácil con los atributos nativos de Magento. Solo use tres campos de texto, uno para el año, uno para la marca, uno para el modelo. Esto permite toda la funcionalidad integrada de Magento, como buscar y filtrar con estos atributos, junto con una fácil actualización en el futuro.

El problema, como usted describe, es que necesitamos "muchas" de estas relaciones. Podríamos hacer 30 atributos de texto: año1, marca1, modelo1, año2, marca2, modelo2, ... año10, marca10, modelo10. Esto a) probablemente dejaría muchos atributos vacíos, yb) crearía un límite artificial en la cantidad de automóviles que admite un producto.

Lo que podría funcionar es algo como esto:

Year: ____
Make: ____
Model: ____

Add new YearMakeModel relationship (+)

Y luego, después de hacer clic en el signo más (+), verá:

Year: ____
Make: ____
Model: ____

Year: ____
Make: ____
Model: ____

Add new YearMakeModel relationship (+)

Dicha interfaz de usuario podría implementarse con javascript dentro de una plantilla de tema respaldada. Al enviar el formulario, deberá proporcionar estos datos a Magento como atributos del producto. No creo que haya actualmente un tipo de atributo que admita una longitud dinámica. Estaría implementando un tipo de atributo personalizado. Nuevamente, esto proporciona soporte de la funcionalidad Magento incorporada: búsqueda de atributos ingresados, actualización fácil para estos atributos en el futuro.

Al final, nuestro cliente tomó la decisión de ahorrar dinero al no implementar esta "edición fácil", y en su lugar bloqueamos los datos en una tabla personalizada, tal como usted describe. Tengo un script de importación personalizado que toma entradas y salidas CSV a la tabla. Más tarde, la página del producto (bueno, su bloque) realiza consultas a esta tabla, extrae la información sobre su SKU y se muestra al usuario como una tabla. Esta tabla de la página del producto era el comportamiento deseado del cliente, por lo que para nosotros no tenía sentido profundizar en hacerlo "The Magento Way" e implementar un atributo de miembro variable.

nshiff
fuente