Jaka jest najlepsza praktyka w Magento 2 do tworzenia relacji wiele do wielu?

15

Rozejrzałem się wokół rdzenia i zobaczyłem kilka przykładów relacji między modelami od wielu do wielu, ale nie widzę na to ostatecznej odpowiedzi.

Jako przykład powiedzmy, że tworzymy nowy model i chcemy mieć relację wiele do wielu z istniejącą tabelą produktów.

Mamy więc nasz nowy Model - Stockist i tworzymy 2 tabele jako takie, jedną do przechowywania nazwy dystrybutora, drugą do przechowywania relacji wielu do wielu produktów.

Skrócona wersja klas konfiguracji:

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

Następnie tworzymy standardowy Model / ResourceModel / Collection for Stockist, tak aby:

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

}

To tutaj dochodzimy do tego, jak poradzić sobie ze stołem z relacją wielu do wielu. Do tej pory wymyśliłem coś podobnego do tego.

Utwórz model do reprezentowania produktu 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);
}
}

W tym miejscu zdefiniowano 2 metody, które przyjmą tablicę identyfikatorów produktów, zwracając tablicę pasujących identyfikatorów produktów i odwrotnie.

Używa to modelu zasobów dla tabeli stockist_product zawierającej relację wiele do wielu:

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

Następnie za pomocą tego modelu StockistProduct, gdy trzeba pobrać zestaw obu modeli, zakładając, że mamy model produktu w $ product, a $ stockistProduct jest instancją \ OurModule \ Stockist \ Model \ StockistProduct

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

Następnie możemy kolejno stworzyć każdy model, zapętlając listę identyfikatorów zwróconych w ten sposób, gdzie $ stockistFactory jest instancją \ OurModule \ Stockist \ Model \ StockistFactory

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

To wszystko działa dobrze i opiera się na jakimś podobnym kodzie w rdzeniu Magento 2, ale nie mogę przestać się zastanawiać, czy istnieje lepszy sposób?

Chris
źródło
Muszę zrobić coś bardzo podobnego ... i to jest jedyny pomysł, jaki mam, jeśli nie ma odpowiedzi :(
slayerbleast

Odpowiedzi:

1

Wdrożyłem podobne rozwiązanie. Dla każdego SKU znajdowały się informacje o „wyposażeniu”: rok, marka, model samochodu, do którego można zastosować produkt (akcesorium samochodowe). Na pierwszy rzut oka byłoby to najłatwiejsze z natywnymi atrybutami Magento. Wystarczy użyć trzech pól tekstowych, jednego dla roku, jednego dla marki, jednego dla modelu. Pozwala to na wszystkie wbudowane funkcje Magento, takie jak wyszukiwanie i filtrowanie za pomocą tych atrybutów, a także łatwą aktualizację w przyszłości.

Problem, jak pan opisuje, polega na tym, że potrzebujemy „wielu” tych relacji. Możemy stworzyć 30 atrybutów tekstowych: rok1, marka1, model1, rok2, marka2, model2, ... rok10, marka10, model10. To a) prawdopodobnie pozostawiłoby wiele pustych atrybutów, i b) stworzyłoby sztuczne ograniczenie liczby samochodów obsługiwanych przez produkt.

To, co może działać, jest mniej więcej takie:

Year: ____
Make: ____
Model: ____

Add new YearMakeModel relationship (+)

A następnie po kliknięciu znaku plus (+) zobaczysz:

Year: ____
Make: ____
Model: ____

Year: ____
Make: ____
Model: ____

Add new YearMakeModel relationship (+)

Taki interfejs użytkownika można zaimplementować za pomocą javascript w szablonie kompozycji. Po przesłaniu formularza musisz podać te dane Magento jako atrybuty produktu. Nie sądzę, że obecnie istnieje typ atrybutu, który obsługuje dynamiczną długość. Wdrożysz niestandardowy typ atrybutu. Ponownie zapewnia to obsługę wbudowanej funkcji Magento: wyszukiwanie wprowadzonych atrybutów, łatwa aktualizacja tych atrybutów w przyszłości.

Ostatecznie nasz klient podjął decyzję o zaoszczędzeniu pieniędzy, nie wdrażając tej „łatwej edycji”, a zamiast tego zamknęliśmy dane w niestandardowej tabeli, tak jak to opisujesz. Mam niestandardowy skrypt importu, który pobiera dane wejściowe i wyjściowe CSV do tabeli. Później strona produktu (cóż, jego blok) tworzy zapytania do tej tabeli, wyciąga informacje o jej SKU i wyświetla użytkownikowi jako tabelę. Ta tabela strony produktu była pożądanym zachowaniem klienta, więc dla nas nie było sensu kopać się w „Magento Way” i implementować atrybut zmiennego elementu.

nshiff
źródło