Co i dlaczego jest właściwym sposobem na załadowanie modelu

9

Mam dość duże doświadczenie z Magento, ale zdałem sobie sprawę, że nie rozumiem, który sposób załadowania modelu jest właściwy i dlaczego. Przeczytałem wszystko, co mogłem, na ten temat, ale ludzie tłumaczący takie rzeczy nigdy tak naprawdę nie sięgają wystarczająco głęboko, aby wyjaśnić, dlaczego używają tej konkretnej metody zamiast innej. Załóżmy, że nie ma repozytorium dla modelu, który chciałbym załadować.

Do tej pory zawsze używałem modelu w konstruktorze, a następnie po prostu go ładowałem.

public function __construct(
    \Vendor\Module\Model\Something $somethingModel
) {
    $this->somethingModel = $somethingModel;
}

public function getTestById($id) {
    return $this->somethingModel->load($id);
}

I zawsze działało zgodnie z przeznaczeniem, jestem też całkiem pewien, że było to, a przynajmniej było powszechnie używane w rdzeniu.

Ale potem zobaczyłem, jak jeden z moich kolegów używa

modelFactory->create()->load($id)

O ile rozumiem, fabryki są używane do utworzenia nowego podmiotu, na przykład, jeśli chciałbym stworzyć nowy produkt, mogę utworzyć fabrykę, zapełnić ją danymi, a następnie zapisać. Ale znowu zacząłem badać ten temat i zobaczyłem przykład od Fabiana Schmenglera ( Kiedy powinniśmy korzystać z repozytorium i fabryki w Magento 2? ), Który ładował model w ten sposób, a także zniechęcał innych po prostu do ładowania modeli, nie zrobił tego Wyjaśnij, dlaczego oprócz tego, że „nie jest częścią umowy o świadczenie usług”. O ile rozumiem, repozytoria są częścią umów serwisowych, więc nie widzę tutaj żadnego związku, jeśli chodzi o ładowanie modeli, które nie są dostępne przez repozytorium.

Aby dodać jeszcze więcej zamieszania, znalazłem również sposób na wczytanie modelu poprzez pobranie sourceModel z utworzonego modeluFactory, został przedstawiony przez Vinai Kopp ( Jak wdrożyć umowę serwisową dla niestandardowego modułu w Magento 2? ), A teraz jestem całkowicie zagubiony, ponieważ zawsze czytałem, że nie powinienem używać modeli zasobów bezpośrednio.

Tak, czy ktoś mógłby mi powiedzieć, który jest właściwy sposób i dlaczego powinienem go używać zamiast wszystkich innych metod?

czs
źródło
Dosłownie łączę ten wątek z mylącym przykładem, czy w ogóle przeczytałeś mój post?
czs
1
Dobre pytanie, postaram się później znaleźć szczegółową odpowiedź. Mogę ci już tyle powiedzieć: to inny przypadek, jeśli załadujesz własne modele (na przykład Vinai) lub modele rdzenia lub modułów innych firm (moja odpowiedź). Również wstrzyknięcie modelu za pomocą konstruktora da ci tę samą instancję za każdym razem, co może prowadzić do niepożądanych efektów ubocznych.
Fabian Schmengler,

Odpowiedzi:

12

Pierwszym krokiem, który powinieneś sprawdzić dla tego modelu, jest: Czy istnieje umowa serwisowa repozytorium? Jeśli tak, użyj tego, ponieważ umowy serwisowe są powiązane z wersją semantyczną i będą się zachowywać tak, jak powinny, aż do momentu wydania Magento 3.x. Nie trzeba dodawać, że tworząc własne moduły z modelami wymagającymi wytrwałości, należy również napisać repozytorium.

public function __construct(
    \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
) {
    $this->productRepository = $productRepository;
    /** @var \Magento\Catalog\Api\Data\ProductInterface $product */
    $this->productRepository->save($product);
}

Jeśli nie ma repozytorium, użyj modelu zasobów . Zauważ, że modele zasobów nie zawierają stanu: używają trwałości dla swoich „zwykłych” modeli. Dlatego nie jest wymagane dołączanie ich przy użyciu fabryki:

public function __construct(
    \Magento\Catalog\Model\ResourceModel\Product $productResource,
    \Magento\Catalog\Model\ProductFactory $productFactory
) {
    $this->productResource = $productResource;
    $this->productFactory = $productFactory;
    ...
    /** @var \Magento\Catalog\Api\Data\ProductInterface $product */
    $product = $this->productFactory->create();
    $this->productResource->save($product);
}

„Więc jaką korzyść przynosi umowa o świadczenie usług / repozytorium nad modelem zasobów?” możesz zapytać. Cóż, teoretycznie model zasobów powinien być odpowiedzialny tylko za trwałość modelu danych , podczas gdy repozytorium bierze również pod uwagę dodatkowe zadania związane z zapisywaniem obiektu. Pomyśl o aktualizacji indeksów, tworzeniu relacji z innymi bytami itp. Jest to teoria, chociaż w rzeczywistości linie te często się zacierają. Ale dobrze jest mieć to na uwadze.

Państwo nie powinno używać modele bezpośredni save(), load()itp -methods. Są przestarzałe, ponieważ jest niepoprawne semantycznie. Pomyśl o tym w SOLIDNY ​​sposób:

  • (Dane) Modele powinny być odpowiedzialne wyłącznie za przechowywanie danych.
  • Modele zasobów powinny być odpowiedzialne za trwałość takich danych.
  • Repozytoria powinny być odpowiedzialne za komunikację wewnątrz i na zewnątrz modułu za działania związane z trwałością.

I to ostatni punkt, który robi różnicę: komunikując się z innymi modułami, w idealnym świecie nigdy nie należy polegać na wewnętrznej logice tych modułów (ani żadnej z jego publicznych metod, ale to kolejna dyskusja), ale korzystaj tylko z tej funkcji, którą zapewniają umowy serwisowe modułów .

Podsumowując

Aby odpowiedzieć na twoje pytanie: w kolejności preferencji. Prawidłowy sposób załadowania modelu to:

  • Jeśli istnieje repozytorium, załaduj je za pomocą repozytorium.
  • Tylko jeśli nie ma repozytorium, użyj modelu zasobów (w połączeniu z fabryką).
Giel Berkers
źródło
1
Ok, więc jeśli wykonam poprawnie - kiedy chcę zmodyfikować / dodać nowe dane i zapisać je w bazie danych, powinienem użyć Modelu Zasobów, a jeśli chcę załadować dane do pamięci, to powinienem użyć Fabryki? Czy jest więc jakaś sytuacja, w której powinienem używać bezpośrednio Modelu (tak jak przy użyciu klasy Model w konstruktorze)?
czs
@cz Masz rację Dodałem bardziej opisowy przykład ładowania modelu dla tego samego.
Milind Singh
2
  • ModelsInterfejsy danych służą do przechowywania danych tylko w obiektach, tj. do seti getdanych dla wiersza.
  • ResourceModelssą mechanizmem odpowiedzialnym za trwałość takich danych, tj. wykonują zapytanie SQL w rzeczywistości savelub loaddanych w Modelobiekcie.

Prawidłowym sposobem loadi savepowinno być utworzenie repozytorium lub ładowanie z zasobu w następujący sposób:

namespace MyVendor\MyModule\Model;

class QueueRepository impliments \MyVendor\MyModule\Api\QueueRepositoryInterface
{

    /** @var \MyVendor\MyModule\Model\ResourceModel\Queue  */
    public $resource;

    /** @var \MyVendor\MyModule\Model\QueueFactory  */
    public $modelFactory;

    public function __construct(
        \MyVendor\MyModule\Model\ResourceModel\Queue $resource,
        \MyVendor\MyModule\Model\QueueFactory $modelFactory
    ) {
        $this->resource = $resource;
        $this->modelFactory = $modelFactory;
    }

    /**
     * Save
     * @param \MyVendor\MyModule\Api\Data\QueueInterface $queue
     * @return $queue
     * @throws \Exception
     */
    public function save(\MyVendor\Integrator\Api\Data\QueueInterface $queue)
    {
        $this->resource->save($queue);
        return $queue;
    }

    /**
     * Save
     * @param \MyVendor\MyModule\Api\Data\QueueInterface $queue
     * @param int $id
     * @return $queue
     * @throws \Exception
     */
    public function load(\MyVendor\MyModule\Api\Data\QueueInterface $queue, $id)
    {
        $this->resource->load($queue, $id);
        return $queue;
    }

    public function getById($id)
    {
        $queue = $this->modelFactory->create();
        $this->resource->load($queue, $id);
        return $queue;
    }
}

Tutaj \MyVendor\MyModule\Api\Data\QueueInterfacejest wszczepiony przez QueueModel.

Tak więc, za kulisami, jesteśmy rzeczywiście stworzenie Modelobiektu następnie loadinggo przez ResourceModelobiekt. To jest właściwy sposób ładowania lub zapisywania.

        $queue = $this->modelFactory->create();
        $this->resource->load($queue, $id);
        return $queue;
Milind Singh
źródło