Qual é a melhor maneira de praticar no Magento 2 para criar relacionamentos Muitos para Muitos?

15

Eu olhei em volta do núcleo e vi alguns exemplos de muitos para muitos relacionamentos entre modelos, mas não consigo ver uma resposta definitiva sobre isso.

Como exemplo, digamos que criamos um novo modelo e queremos ter um relacionamento de muitos para muitos com a tabela de produtos existentes.

Portanto, temos o nosso novo Model - Stockist, e criamos 2 tabelas como tal, uma para armazenar o nome da Stockist, a outra para armazenar o relacionamento de muitos para muitos com os produtos.

Versão truncada das classes de instalação:

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

Em seguida, criamos um Model / ResourceModel / Collection padrão para o Stockist da seguinte forma:

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

}

É aqui que chegamos a como lidar com a mesa com o relacionamento muitos para muitos. Até agora, eu criei algo nesse sentido.

Crie um modelo para representar o 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);
}
}

Aqui, definimos 2 métodos que receberão uma matriz de IDs armazenistas, retornando uma matriz de IDs de produtos correspondentes e vice-versa.

Isso usa um Modelo de Recurso para a tabela stockist_product que contém o relacionamento muitos para muitos:

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

Em seguida, use esse modelo StockistProduct quando precisar recuperar um conjunto de qualquer um dos modelos, assumindo que temos um Modelo de Produto em $ product e $ stockistProduct é uma instância de \ OurModule \ Stockist \ Model \ StockistProduct

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

Em seguida, podemos criar cada modelo alternando a lista de IDs retornados, onde $ stockistFactory é uma instância de \ OurModule \ Stockist \ Model \ StockistFactory

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

Isso tudo funciona bem e é baseado em algum código semelhante dentro do Core of Magento 2, mas não posso deixar de me perguntar se existe uma maneira melhor?

Chris
fonte
Eu tenho que fazer algo muito semelhante ... e esta é a única idéia que eu tenho, se não houver respostas :(
slayerbleast

Respostas:

1

Eu implementei uma solução semelhante a esta. Para cada SKU, havia informações de "montagem": ano, marca, modelo de um carro ao qual o produto (acessório do carro) podia ser aplicado. Em face disso, isso seria mais fácil com atributos nativos do Magento. Basta usar três campos de texto, um para o ano, um para marca, um para modelo. Isso permite todas as funcionalidades internas do Magento, como pesquisar e filtrar com esses atributos, além de facilitar a atualização no futuro.

O problema, como você descreve, é que precisamos de "muitos" desses relacionamentos. Poderíamos criar 30 atributos de texto: ano1, marca1, modelo1, ano2, marca2, modelo2, ... ano10, marca10, modelo10. Isso provavelmente deixaria muitos atributos vazios eb) criaria um limite artificial para o número de carros que um produto suporta.

O que poderia funcionar é algo como isto:

Year: ____
Make: ____
Model: ____

Add new YearMakeModel relationship (+)

E depois de clicar no sinal de mais (+), você verá:

Year: ____
Make: ____
Model: ____

Year: ____
Make: ____
Model: ____

Add new YearMakeModel relationship (+)

Essa interface do usuário pode ser implementada com javascript dentro de um modelo de tema com backup. Ao enviar o formulário, você precisará fornecer esses dados ao Magento como atributos do produto. Não acho que exista atualmente um tipo de atributo que suporte um comprimento dinâmico. Você estaria implementando um tipo de atributo personalizado. Novamente, isso fornece suporte da funcionalidade Magento integrada: pesquisando nos atributos inseridos, atualização fácil para esses atributos no futuro.

No final, nosso cliente tomou a decisão de economizar dinheiro ao não implementar essa "edição fácil" e, em vez disso, bloqueamos os dados em uma tabela personalizada, exatamente como você descreve. Eu tenho um script de importação personalizado que leva as entradas e saídas CSV para a tabela. Posteriormente, a página do produto (bem, seu bloco) faz consultas a esta tabela, obtém as informações sobre seu SKU e é exibida para o usuário como uma tabela. Essa tabela de páginas do produto era o comportamento desejado do cliente, portanto, para nós, não fazia sentido cavar "The Magento Way" e implementar um atributo de membro variável.

nshiff
fonte