Как лучше всего использовать Magento 2 для создания отношений «многие ко многим»?

15

Я осмотрел ядро ​​и увидел несколько примеров множества отношений между моделями, но я не вижу однозначного ответа на этот вопрос.

В качестве примера, скажем, мы создаем новую модель, и мы хотим иметь связь «многие ко многим» с существующей таблицей продуктов.

Итак, у нас есть наша новая Модель - Stockist, и мы создаем 2 таблицы как таковые: одну для хранения имени Stockist, другую для хранения связи многих со многими продуктами.

Усеченная версия классов настройки:

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

Затем мы создаем стандартную модель / ResourceModel / Collection для Stockist следующим образом:

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

}

Здесь мы подходим к тому, как обращаться со столом с отношениями «многие ко многим». До сих пор я придумал что-то подобное.

Создать модель для представления 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);
}
}

Здесь определены 2 метода, которые будут принимать либо массив идентификаторов запаса, возвращая массив совпадающих идентификаторов продукта, и наоборот.

При этом используется модель ресурсов для таблицы stockist_product, содержащей отношение многие ко многим:

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

Затем используйте эту модель StockistProduct, когда вам нужно получить набор любой из этих моделей, предположив, что у нас есть Product Model в $ product, а $ stockistProduct является экземпляром \ OurModule \ Stockist \ Model \ StockistProduct

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

Затем мы можем создать каждую модель по очереди, зациклив список возвращенных идентификаторов, как показано ниже, где $ stockistFactory является экземпляром \ OurModule \ Stockist \ Model \ StockistFactory

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

Все это прекрасно работает и основано на некотором подобном коде в ядре Magento 2, но я не могу не задаться вопросом, есть ли лучший способ?

Крис
источник
Я должен сделать что-то очень похожее ... и это единственная идея, которая у меня есть, если нет ответов :(
slayerbleast

Ответы:

1

Я реализовал решение, подобное этому. Для каждой SKU была указана информация о соответствии: год, марка, модель автомобиля, к которому можно применить продукт (автомобильный аксессуар). На первый взгляд, это будет проще всего с нативными атрибутами Magento. Просто используйте три текстовых поля, одно для года, одно для марки, одно для модели. Это позволяет использовать все встроенные функции Magento, такие как поиск и фильтрация по этим атрибутам, а также легко обновлять их в будущем.

Проблема, как вы описываете, заключается в том, что нам нужно «много» из этих отношений. Мы могли бы сделать 30 текстовых атрибутов: год1, год1, модель1, год2, год2, модель2, ... год10, год10, модель10. Это а) скорее всего оставит много пустых атрибутов, и б) создаст искусственное ограничение на количество автомобилей, которые поддерживает продукт.

Что может сработать, примерно так:

Year: ____
Make: ____
Model: ____

Add new YearMakeModel relationship (+)

И тогда после нажатия на плюс (+) вы увидите:

Year: ____
Make: ____
Model: ____

Year: ____
Make: ____
Model: ____

Add new YearMakeModel relationship (+)

Такой пользовательский интерфейс может быть реализован с помощью javascript внутри шаблона темы с поддержкой. После отправки формы вам необходимо будет предоставить эти данные в Magento в качестве атрибутов продукта. Я не думаю, что в настоящее время существует тип атрибута, который поддерживает динамическую длину. Вы будете реализовывать пользовательский тип атрибута. Опять же, это обеспечивает поддержку встроенной функциональности Magento: поиск по введенным атрибутам, простое обновление этих атрибутов в будущем.

В итоге наш клиент принял решение сэкономить деньги, не реализовав это «простое редактирование», и вместо этого мы заблокировали данные в пользовательской таблице, как вы описали. У меня есть собственный скрипт импорта, который принимает входные и выходные данные CSV в таблицу. Позже страница продукта (ну, его блок) делает запросы к этой таблице, извлекает информацию о его SKU и отображает для пользователя в виде таблицы. Эта таблица страниц продукта была желательным поведением клиента, поэтому для нас не было смысла копаться в том, чтобы сделать это «Путь Magento» и реализовать атрибут переменной-члена.

nshiff
источник