Najlepszy sposób na załadowanie niestandardowego modelu w Magento 2

15

Ponieważ trudno mi było znaleźć właściwą drogę, poniżej możesz znaleźć najlepszą praktykę, którą zastosowałem. Ciesz się, popraw mój angielski w razie potrzeby i powiedz mi, że się mylę, jeśli tak jest. :)

Edycja: ... i odkryłem, że się myliłem w niektórych aspektach. Zaktualizowałem więc oryginalny post, gdy odpowiedzi Raphaela pomogły mi zrozumieć więcej. Dzięki mu!

Zastosowana poniżej koncepcja :

Łatwiej będzie ci zrozumieć poniższe kody i objaśnienia, jeśli nie masz nic przeciwko tym pojęciom:

  • Zależność wtrysku (ponieważ $this->variablewstrzykiwane są wszystkie zmienne w kodach)
  • Umowa serwisowa i repozytorium
  • Fabryka

Kontekst :

Aby mieć więcej kontekstu, wyobraź sobie, że mamy poprawnie skonstruowany moduł z:

  • klasa blokowa CustomBlock zawierająca metodę getCustomModel($id),
  • ta metoda zwraca obiekt CustomModel na podstawie identyfikatora przekazanego w param,
  • Typ CustomModel odpowiada modelowi w \Vendor\Module\Model\CustomModel
  • Ten model jest dostarczany z modelem zasobów (w \Vendor\Module\Model\ResourceModel\CustomModel)
  • oraz z jego repozytorium (in \Vendor\Module\Model\CustomModelRepository).

Pytanie :

  • Jaka jest najlepsza praktyka pozwalająca wszystkim ładować obiekt CustomModel?

Nie możesz użyć load()obiektu CustomModel, ponieważ ta metoda jest przestarzała.

Dobra praktyka mówi, że musisz skorzystać z umowy serwisowej CustomModel. Kontrakty serwisowe to interfejsy danych (np. CustomModelInterface) i interfejsy serwisowe (np. CustomModelRepositoryInterface). Mój blok wygląda tak:

/ ** @var SlideRepositoryInterface * /
chroniony $ slideRepository;

/ **
 * Konstruktor CustomBlock
 * ...
 * @param CustomModelRepositoryInterface $ customModelRepository
 * ...
 * /
funkcja publiczna __construct (
...
CustomModelRepositoryInterface $ customModelRepository
...
) {
    $ this-> customModelRepository = $ customModelRepository;
}

funkcja publiczna getCustomModel ($ id) {
    return $ this-> customModelRepository-> get ($ id);
}

Przede wszystkim wstrzykiwamy CustomModelRepositoryInterfaceobiekt do konstruktora i używamy go w naszej getCustomModel()metodzie.

W klasie Api\CustomModelRepositoryInterfacenie ma dużo. Generalnie (ale nic nie przeszkodzi, aby zrobić inaczej) można zadeklarować podstawowe metody: get, getList, save, delete, deleteById. Na potrzeby tego tematu poniżej znajduje się tylko getdeklaracja metody:

/**
 * Get info by id
 *
 * @param int $id
 * @return Data\CustomModelInterface
 * @throws \Magento\Framework\Exception\NoSuchEntityException
 */
public function get($id);

Ok, ale jeśli mój interfejs CustomModel jest wywoływany przez wstrzyknięcie zależności w moim konstruktorze bloku, to gdzie jest kod? Aby odpowiedzieć na to pytanie, musisz wyjaśnić Magento, gdzie znajduje się klasa implementująca ten interfejs. W pliku etc / di.xml modułu musisz dodać:

<preference for="Vendor\Module\Api\CustomModelRepositoryInterface" type="Vendor\Module\Model\CustomModelRepository" />

Zatem CustomModelRepositoryInterfaceklasa jest interfejsem usługi. Wdrażając go, będziesz musiał zaimplementować również interfejsy danych (przynajmniej Vendor\Module\Api\Data\CustomModelInterfacei Vendor\Module\Api\Data\CustomModelSearchResultsInterface). Twój model będzie musiał zaimplementować Vendor\Module\Api\Data\CustomModelInterfacei dodać <preference ... />linie dla każdego interfejsu. Wreszcie, w każdej chwili, gdy korzystasz z umowy serwisowej, nie myśl mySomethingInterfacejuż więcej mySomething: pozwól magento skorzystać z di.xmlmechanizmu preferencji.

Ok, co będzie dalej? Gdy wprowadzamy CustomModelRepositoryInterfacekonstruktor bloku, otrzymujemy CustomModelRepositoryobiekt. CustomModelRepositorymusi zaimplementować metodę deklarowania w CustomModelRepositoryInterface. Mamy to w Vendor\Module\Model\CustomModelRepository:

funkcja publiczna get ($ id) {
    $ customModel = $ this-> customModelFactory-> create ();
    $ customModel-> load ($ id);
    if (! $ customModel-> getId ()) {
      wyrzuć nowy wyjątek NoSuchEntityException (__ („CustomModel o identyfikatorze„% 1 ”nie istnieje.”, $ id));
    }
    return $ customModel;
}

Co robimy ? Tworzymy pusty CustomModelobiekt dzięki fabryce. Następnie ładujemy dane za CustomModelpomocą metody modelu obciążenia. Następnie zwracamy a, NoSuchEntityExceptionjeśli nie udało się załadować CustomModelidentyfikatora w params. Ale jeśli wszystko jest w porządku, zwracamy obiekt modelu i życie trwa.

Ale wow ...! W tym przykładzie co to jest?

$customModel->load($id);

Czy to nie jest ta sama przestarzała loadmetoda niż na początku? Tak to jest. Myślę, że to wstyd, ale musisz go użyć, ponieważ w metodzie load () wywoływane są pewne zdarzenia i programista może ich słuchać (patrz odpowiedź Raphaela poniżej).

W przyszłości będziemy oszczędzać przez Entity Manager. To kolejna historia jako nowa koncepcja Magento 2, ale jeśli chcesz rzucić okiem, Entity Manager jest już zaimplementowany w modelu zasobów strony CMS (v2.1):

public function load(AbstractModel $object, $value, $field = null)
{
    $pageId = $this->getPageId($object, $value, $field);
    if ($pageId) {
        $this->entityManager->load($object, $pageId);
    }
    return $this;
}
Nicolas PERNOT
źródło

Odpowiedzi:

16

Najlepsza praktyka: poprzez umowę serwisową

Najlepszą praktyką jest zawsze korzystanie z umowy serwisowej, gdy tylko jest to możliwe. Listę powodów znajdziesz tutaj: Magento 2: jakie są korzyści z korzystania z umów serwisowych?

Aby uzyskać szczegółowe informacje na temat wdrażania umowy serwisowej, sugeruję sprawdzić ten temat: Jak wdrożyć umowę serwisową dla niestandardowego modułu w Magento 2?

Jeśli nie jest dostępna żadna umowa serwisowa

Jeśli nie jest dostępna żadna umowa serwisowa, należy użyć getmetody repozytorium modeli . Korzystając z tej metody, korzystasz z systemu buforowania magento, na przykład dla CategoryRepositoryklasy:

public function get($categoryId, $storeId = null)
{
    $cacheKey = null !== $storeId ? $storeId : 'all';
    if (!isset($this->instances[$categoryId][$cacheKey])) {
        /** @var Category $category */
        $category = $this->categoryFactory->create();
        if (null !== $storeId) {
            $category->setStoreId($storeId);
        }
        $category->load($categoryId);
        if (!$category->getId()) {
            throw NoSuchEntityException::singleField('id', $categoryId);
        }
        $this->instances[$categoryId][$cacheKey] = $category;
    }
    return $this->instances[$categoryId][$cacheKey];
}

Przestarzała load()metoda

Magento 2 powoli odchodzi od standardowego systemu CRUD, porzucając system dziedziczenia i wdrażając go poprzez kompozycję za pomocą nowego 2.1 EntityManager 2.1. Szczegóły można znaleźć tutaj: Magento 2.1: za pomocą menedżera encji

Sugeruję również przeczytanie tego interesującego tematu na temat przestarzałych metod CRUD: Przestarzałe metody zapisywania i ładowania w Modelu abstrakcyjnym

Dlaczego nie użyć obciążenia modelu zasobów

Głównym powodem jest to, że jeśli użyjesz loadmetody modelu zasobów , pominiesz ważną część systemu ładowania, które są zaimplementowane w loadmetodzie modelu , patrz Magento\Framework\Model\AbstractModel:

public function load($modelId, $field = null)
{
    $this->_beforeLoad($modelId, $field);
    $this->_getResource()->load($this, $modelId, $field);
    $this->_afterLoad();
    $this->setOrigData();
    $this->_hasDataChanges = false;
    $this->updateStoredData();
    return $this;
}

loadBezpośrednie wywołanie metody modelu zasobów będzie miało następujący wpływ:

  • _beforeLoad nie jest wywoływany: dlatego ładowanie modelu przed zdarzeniami nie jest wywoływane
  • _afterLoad nie jest wywoływany: dlatego ładowanie modelu po zdarzeniach nie jest wywoływane
  • przechowywane dane nie są aktualizowane, co może powodować różne problemy (na przykład, jeśli dzwonisz prepareDataForUpdatez Magento\Framework\Model\ResourceModel\Db\AbstractDb)
Raphael at Digital Pianism
źródło
Dzięki Raphael, wszystko, co mówisz, ma sens i uzupełnia moją wiedzę. Ale nie rozumiem, dlaczego KAndy komentuje (pod swoją odpowiedzią), że Marius może użyć metody load () swojego niestandardowego modelu zasobów modułu? Jest w [ magento.stackexchange.com/questions/114929 /... metod zapisu i wczytywania w Modelu abstrakcyjnym). Jakieś pomysły ?
Nicolas PERNOT
@NicolasPERNOT w zasadzie KAndy wyjaśnia, że ​​celem jest posiadanie SL (Service Layer) dla każdego modułu i że tego należy używać za każdym razem, gdy trzeba załadować jednostkę. Sugeruję, abyś skomentował go, być może będzie w stanie cię oświecić, ponieważ, jak sądzę, jest pracownikiem Magento Inc
Raphael z Digital Pianism,
Cóż, w końcu zaktualizowałem swój oryginalny post. Dziękuję Raphael za pomoc.
Nicolas PERNOT
Widzę, że przynajmniej w Magento 2.2 to ważne jest uwzględnione w ładowaniu ResourceModel, więc używanie metod ResourceModel nie jest w porządku, prawda?
Jānis Elmeris
Obecnie możemy bezpiecznie załadować Model przy użyciu load()metody Model zasobów . Model zasobów wywołuje metody modelu z metody własnej load(): $model->beforeLoad() { $this->_beforeLoad() }i$model->afterLoad() { $this->_afterLoad() }
sergei.sss
-2

Myślę, że poniższe oświadczenie nie jest teraz aktualne.

Why not using the resource model load

możemy znaleźć Magento\Framework\EntityManager\Observerfolder wszystkich wydarzeń.

Siva Kumar Koduru
źródło