วิธีการใช้สัญญาบริการสำหรับโมดูลที่กำหนดเองใน Magento 2


42

เท่าที่เห็นในโพสต์นี้: เลิกบันทึกและวิธีการโหลดนามธรรมรุ่นsaveและloadวิธีการเลิกใช้ในวีโอไอพี 2 พัฒนาสาขา

ดังนั้นแนวปฏิบัติที่ดีในขณะนี้คือการใช้สัญญาบริการเพื่อจัดการกับหน่วยงาน CRUD

กระบวนการทีละขั้นตอนที่ฉันต้องทำเพื่อใช้สัญญาบริการสำหรับหน่วยงานโมดูลที่กำหนดเองของฉันคืออะไร

หมายเหตุ: ฉันรู้ว่าอาจมีหลายพันวิธีในรุ่น CRUD ของฉันฉันแค่ขอวิธีการที่ชัดเจนตามที่ระบุไว้ที่นี่: http://devdocs.magento.com/guides/v2.0/extension-dev-guide /service-contracts/design-patterns.html :

  • get
  • save
  • getList
  • delete
  • deleteById

คำตอบ:


89

ฉันต้องการให้รายละเอียดเพิ่มเติมนอกเหนือจากคำตอบที่ยอดเยี่ยมของ @ryanF

ฉันต้องการสรุปเหตุผลในการเพิ่มที่เก็บสำหรับเอนทิตีที่กำหนดเองยกตัวอย่างวิธีการทำเช่นนั้นและอธิบายวิธีเปิดเผยวิธีการเก็บข้อมูลเหล่านั้นเป็นส่วนหนึ่งของ Web API

ข้อจำกัดความรับผิดชอบ: ฉันแค่อธิบายวิธีปฏิบัติอย่างจริงจังว่าจะทำอย่างไรสำหรับโมดูลบุคคลที่สาม - ทีมหลักมีมาตรฐานของตัวเองซึ่งพวกเขาปฏิบัติตาม (หรือไม่)

โดยทั่วไปวัตถุประสงค์ของที่เก็บคือการซ่อนตรรกะที่เกี่ยวข้องกับการจัดเก็บ
ไคลเอ็นต์ของที่เก็บไม่ควรสนใจว่าเอนทิตีที่ส่งคืนถูกเก็บไว้ในหน่วยความจำในอาร์เรย์หรือไม่ถูกดึงจากฐานข้อมูล MySQL เรียกจาก API ระยะไกลหรือจากไฟล์
ฉันคิดว่าทีมหลักของ Magento ทำสิ่งนี้เพื่อให้พวกเขาสามารถเปลี่ยนหรือเปลี่ยน ORM ในอนาคต ใน Magento ปัจจุบัน ORM ประกอบด้วยโมเดล, โมเดลทรัพยากรและคอลเล็กชัน
หากโมดูลของบุคคลที่สามใช้ที่เก็บข้อมูลเท่านั้นวีโอไอพีสามารถเปลี่ยนวิธีการจัดเก็บข้อมูลและสถานที่และโมดูลจะยังคงทำงานต่อไปแม้จะมีการเปลี่ยนแปลงในเชิงลึก

ที่เก็บโดยทั่วไปมีวิธีการเช่นfindById(), findByName(), หรือput() ในวีโอไอพีเหล่านี้โดยทั่วไปจะเรียกว่า, และไม่ได้แกล้งพวกเขากำลังทำอะไรอย่างอื่น แต่การดำเนินงาน CRUD DB remove()
getbyId()save()delete()

วิธีการเก็บข้อมูล Magento 2 สามารถเปิดเผยเป็นทรัพยากร API ได้อย่างง่ายดายทำให้มีคุณค่าสำหรับการผสานรวมกับระบบของบุคคลที่สามหรืออินสแตนซ์ Magento ที่ไม่มีหัว

"ฉันควรเพิ่มที่เก็บสำหรับเอนทิตีที่กำหนดเองของฉัน".

เช่นเคยคำตอบคือ

"มันขึ้นอยู่กับ".

หากต้องการสรุปสั้น ๆ หากโมดูลอื่น ๆ ของคุณจะถูกใช้โดยใช่คุณอาจต้องการเพิ่มพื้นที่เก็บข้อมูล

มีอีกปัจจัยหนึ่งที่มานับที่นี่: ใน Magento 2 ที่เก็บสามารถเปิดเผยได้อย่างง่ายดายเหมือน Web API - นั่นคือ REST และ SOAP - แหล่งข้อมูล

หากนั่นเป็นสิ่งที่น่าสนใจสำหรับคุณเนื่องจากการรวมระบบของบุคคลที่สามหรือการตั้งค่า Magento ที่ไม่มีส่วนหัวดังนั้นคุณอาจต้องการเพิ่มที่เก็บสำหรับเอนทิตีของคุณอีกครั้ง

ฉันจะเพิ่มที่เก็บสำหรับเอนทิตีที่กำหนดเองได้อย่างไร

สมมติว่าคุณต้องการเปิดเผยเอนทิตีของคุณเป็นส่วนหนึ่งของ REST API หากไม่เป็นเช่นนั้นคุณสามารถข้ามส่วนที่กำลังจะมาถึงในการสร้างอินเทอร์เฟซและตรงไปที่ "สร้างที่เก็บและการใช้โมเดลข้อมูล" ด้านล่าง

สร้างที่เก็บข้อมูลและอินเตอร์เฟสโมเดลข้อมูล

สร้างโฟลเดอร์Api/Data/ในโมดูลของคุณ นี่เป็นเพียงการประชุมคุณสามารถใช้สถานที่อื่นได้ แต่ไม่ควรทำ
พื้นที่เก็บข้อมูลจะเข้าไปในApi/โฟลเดอร์ Data/ไดเรกทอรีย่อยสำหรับในภายหลัง

ในApi/สร้างอินเทอร์เฟซ PHP ด้วยวิธีการที่คุณต้องการเปิดเผย ตามระเบียบของวีโอไอพี 2 ชื่ออินเตอร์เฟสทั้งหมดจะลงท้ายInterfaceด้วยคำต่อท้าย
ตัวอย่างเช่นสำหรับนิติบุคคลที่ผมจะสร้างอินเตอร์เฟซ HamburgerApi/HamburgerRepositoryInterface

สร้างอินเตอร์เฟสที่เก็บ

Magento 2 repositories เป็นส่วนหนึ่งของตรรกะโดเมนของโมดูล ซึ่งหมายความว่าไม่มีชุดวิธีการแก้ไขที่เก็บต้องดำเนินการ
ขึ้นอยู่กับวัตถุประสงค์ของโมดูล

อย่างไรก็ตามในทางปฏิบัติที่เก็บข้อมูลทั้งหมดนั้นค่อนข้างคล้ายกัน พวกเขาเป็นเครื่องห่อสำหรับฟังก์ชั่น CRUD
ส่วนใหญ่จะมีวิธีการgetById, save, และdelete อาจมีมากกว่านั้นตัวอย่างเช่นมีวิธีที่ดึงลูกค้าทางอีเมลโดยที่จะใช้เพื่อดึงข้อมูลลูกค้าตามรหัสเอนทิตี getList
CustomerRepositorygetgetById

นี่คืออินเทอร์เฟซที่เก็บตัวอย่างสำหรับเอนทิตีแฮมเบอร์เกอร์:

<?php

namespace VinaiKopp\Kitchen\Api;

use Magento\Framework\Api\SearchCriteriaInterface;
use VinaiKopp\Kitchen\Api\Data\HamburgerInterface;

interface HamburgerRepositoryInterface
{
    /**
     * @param int $id
     * @return \VinaiKopp\Kitchen\Api\Data\HamburgerInterface
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    public function getById($id);

    /**
     * @param \VinaiKopp\Kitchen\Api\Data\HamburgerInterface $hamburger
     * @return \VinaiKopp\Kitchen\Api\Data\HamburgerInterface
     */
    public function save(HamburgerInterface $hamburger);

    /**
     * @param \VinaiKopp\Kitchen\Api\Data\HamburgerInterface $hamburger
     * @return void
     */
    public function delete(HamburgerInterface $hamburger);

    /**
     * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
     * @return \VinaiKopp\Kitchen\Api\Data\HamburgerSearchResultInterface
     */
    public function getList(SearchCriteriaInterface $searchCriteria);

}

สิ่งสำคัญ! ที่นี่เป็น timesinks!
มี gotchas อยู่สองสามที่นี่ซึ่งยากที่จะทำการดีบั๊กถ้าคุณทำผิด:

  1. อย่าใช้ประเภทอาร์กิวเมนต์สเกลาร์PHP7หรือประเภทส่งคืนหากคุณต้องการเชื่อมต่อกับ REST API!
  2. เพิ่มคำอธิบายประกอบ PHPDoc สำหรับข้อโต้แย้งทั้งหมดและประเภทกลับไปที่วิธีการทั้งหมด!
  3. ใช้ชื่อคลาสที่ผ่านการรับรองอย่างสมบูรณ์ในบล็อก PHPDoc!

หมายเหตุประกอบจะถูกวิเคราะห์โดย Magento Framework เพื่อกำหนดวิธีการแปลงข้อมูลไปและกลับจาก JSON หรือ XML การอิมพอร์ตคลาส (นั่นคือuseข้อความสั่ง) จะไม่ถูกนำไปใช้!

ทุกวิธีจะต้องมีคำอธิบายประกอบที่มีประเภทอาร์กิวเมนต์ใด ๆ และประเภทส่งคืน แม้ว่าเมธอดจะไม่มีอาร์กิวเมนต์และไม่ส่งคืนสิ่งใดมันก็จะต้องมีหมายเหตุประกอบ:

/**
 * @return void
 */

ประเภทเกลา ( string, int, floatและbool) นอกจากนี้ยังจะต้องมีการระบุทั้งข้อโต้แย้งและเป็นค่าตอบแทน

โปรดทราบว่าในตัวอย่างข้างต้นคำอธิบายประกอบสำหรับวิธีการที่ส่งคืนวัตถุจะถูกระบุเป็นอินเตอร์เฟสเช่นกัน
อินเตอร์เฟสชนิดคืนค่าทั้งหมดในApi\Dataเนมสเปซ / ไดเร็กทอรี
นี่เป็นการระบุว่าพวกเขาไม่มีตรรกะทางธุรกิจใด ๆ มันเป็นเพียงข้อมูล
เราต้องสร้างอินเตอร์เฟสเหล่านี้ต่อไป

สร้างอินเทอร์เฟซ DTO

ฉันคิดว่า Magento เรียกอินเทอร์เฟซ "data models" ซึ่งเป็นชื่อที่ฉันไม่ชอบเลย
ประเภทของชั้นนี้เป็นที่รู้จักกันทั่วไปว่าเป็นวัตถุการถ่ายโอนข้อมูลหรือDTO
คลาส DTO เหล่านี้มี getters และ setters สำหรับคุณสมบัติทั้งหมดเท่านั้น

เหตุผลที่ฉันชอบใช้ DTO มากกว่า data model เพราะมันสับสนน้อยกว่ากับโมเดลข้อมูล ORM, model resource หรือดู model ... หลาย ๆ สิ่งนั้นเป็น model ใน Magento อยู่แล้ว

ข้อ จำกัด เดียวกันกับการพิมพ์ PHP7 ที่ใช้กับที่เก็บยังนำไปใช้กับ DTO ด้วย
นอกจากนี้ทุกวิธีจะต้องมีคำอธิบายประกอบที่มีประเภทอาร์กิวเมนต์ทั้งหมดและประเภทส่งคืน

<?php

namespace VinaiKopp\Kitchen\Api\Data;

use Magento\Framework\Api\ExtensibleDataInterface;

interface HamburgerInterface extends ExtensibleDataInterface
{
    /**
     * @return int
     */
    public function getId();

    /**
     * @param int $id
     * @return void
     */
    public function setId($id);

    /**
     * @return string
     */
    public function getName();

    /**
     * @param string $name
     * @return void
     */
    public function setName($name);

    /**
     * @return \VinaiKopp\Kitchen\Api\Data\IngredientInterface[]
     */
    public function getIngredients();

    /**
     * @param \VinaiKopp\Kitchen\Api\Data\IngredientInterface[] $ingredients
     * @return void
     */
    public function setIngredients(array $ingredients);

    /**
     * @return string[]
     */
    public function getImageUrls();

    /**
     * @param string[] $urls
     * @return void
     */
    public function setImageUrls(array $urls);

    /**
     * @return \VinaiKopp\Kitchen\Api\Data\HamburgerExtensionInterface|null
     */
    public function getExtensionAttributes();

    /**
     * @param \VinaiKopp\Kitchen\Api\Data\HamburgerExtensionInterface $extensionAttributes
     * @return void
     */
    public function setExtensionAttributes(HamburgerExtensionInterface $extensionAttributes);
}

หากวิธีการดึงหรือผลตอบแทนอาร์เรย์ประเภทของรายการในอาร์เรย์จะต้องมีการระบุไว้ในคำอธิบายประกอบ PHPDoc []ตามด้วยการเปิดและปิดวงเล็บเหลี่ยม
สิ่งนี้เป็นจริงสำหรับทั้งค่าสเกลาร์ (เช่นint[]) และวัตถุ (เช่นIngredientInterface[])

โปรดทราบว่าฉันใช้Api\Data\IngredientInterfaceเป็นตัวอย่างสำหรับวิธีการคืนค่าอาร์เรย์ของวัตถุฉันจะไม่เพิ่มรหัสของส่วนผสมลงในบทความนี้

ExtensibleDataInterface?

ในตัวอย่างข้างต้นขยายHamburgerInterface ในทางเทคนิคสิ่งนี้จำเป็นเฉพาะถ้าคุณต้องการให้โมดูลอื่นสามารถเพิ่มคุณสมบัติให้กับเอนทิตีของคุณ ถ้าเป็นเช่นนั้นคุณยังต้องเพิ่มคู่ getter / หมาอีกโดยการประชุมที่เรียกว่าและ ExtensibleDataInterface

getExtensionAttributes()setExtensionAttributes()

การตั้งชื่อประเภทการคืนสินค้าของวิธีนี้มีความสำคัญมาก!

เฟรมเวิร์ก Magento 2 จะสร้างส่วนต่อประสานการนำไปปฏิบัติและโรงงานสำหรับการนำไปใช้หากคุณตั้งชื่อให้ถูกต้อง รายละเอียดของกลไกเหล่านี้อยู่นอกขอบเขตของบทความนี้
เพียงแค่รู้ว่าถ้าอินเตอร์เฟซของวัตถุที่คุณต้องการที่จะทำให้การขยายที่เรียกว่าแล้วขยายแอตทริบิวต์ประเภทจะต้องมี\VinaiKopp\Kitchen\Api\Data\HamburgerInterface \VinaiKopp\Kitchen\Api\Data\HamburgerExtensionInterfaceดังนั้นคำว่าExtensionจะต้องถูกแทรกหลังชื่อเอนทิตีก่อนหน้าInterfaceคำต่อท้าย

หากคุณไม่ต้องการที่นิติบุคคลของคุณจะขยายแล้วอินเตอร์เฟซ DTO ไม่ได้มีการขยายการอินเตอร์เฟซที่อื่น ๆ และgetExtensionAttributes()และsetExtensionAttributes()วิธีการสามารถละเว้น

พอเกี่ยวกับอินเทอร์เฟซ DTO ในขณะนี้ถึงเวลาที่จะกลับไปที่อินเทอร์เฟซที่เก็บข้อมูล

getList () ประเภทส่งคืน SearchResults

วิธีการเก็บข้อมูลgetListกลับมาอีกประเภทหนึ่งนั่นคือSearchResultsInterfaceอินสแตนซ์

getListแน่นอนว่าวิธีการนั้นอาจส่งคืนอาร์เรย์ของวัตถุที่ตรงกับที่ระบุSearchCriteriaแต่การส่งคืนSearchResultsอินสแตนซ์ช่วยให้สามารถเพิ่มข้อมูลเมตาที่มีประโยชน์บางอย่างให้กับค่าที่ส่งคืน

คุณสามารถดูวิธีการทำงานด้านล่างในgetList()การใช้วิธีการเก็บข้อมูล

นี่คืออินเทอร์เฟซผลการค้นหาแฮมเบอร์เกอร์ตัวอย่าง:

<?php

namespace VinaiKopp\Kitchen\Api\Data;

use Magento\Framework\Api\SearchResultsInterface;

interface HamburgerSearchResultInterface extends SearchResultsInterface
{
    /**
     * @return \VinaiKopp\Kitchen\Api\Data\HamburgerInterface[]
     */
    public function getItems();

    /**
     * @param \VinaiKopp\Kitchen\Api\Data\HamburgerInterface[] $items
     * @return void
     */
    public function setItems(array $items);
}

อินเทอร์เฟซทั้งหมดนี้จะมีการแทนที่ชนิดของวิธีการสองวิธีgetItems()และsetItems()ของอินเทอร์เฟซหลัก

สรุปอินเทอร์เฟซ

ตอนนี้เรามีอินเทอร์เฟซต่อไปนี้:

  • \VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface
  • \VinaiKopp\Kitchen\Api\Data\HamburgerInterface
  • \VinaiKopp\Kitchen\Api\Data\HamburgerSearchResultInterface

พื้นที่เก็บข้อมูลขยายอะไรขยาย, และขยาย
HamburgerInterface\Magento\Framework\Api\ExtensibleDataInterface
HamburgerSearchResultInterface\Magento\Framework\Api\SearchResultsInterface

สร้างการนำไปใช้ที่เก็บและโมเดลข้อมูล

ขั้นตอนต่อไปคือการสร้างการใช้งานของสามอินเตอร์เฟส

พื้นที่เก็บข้อมูล

ในสาระสำคัญที่เก็บใช้ ORM ที่จะทำงานมัน

getById(), save() และdelete()วิธีการที่ค่อนข้างตรงไปตรงมา ถูกฉีดเข้าไปในพื้นที่เก็บข้อมูลเป็นอาร์กิวเมนต์นวกรรมิกที่สามารถมองเห็นบิตดังต่อไปนี้
HamburgerFactory

public function getById($id)
{
    $hamburger = $this->hamburgerFactory->create();
    $hamburger->getResource()->load($hamburger, $id);
    if (! $hamburger->getId()) {
        throw new NoSuchEntityException(__('Unable to find hamburger with ID "%1"', $id));
    }
    return $hamburger;
}

public function save(HamburgerInterface $hamburger)
{
    $hamburger->getResource()->save($hamburger);
    return $hamburger;
}

public function delete(HamburgerInterface $hamburger)
{
    $hamburger->getResource()->delete($hamburger);
}

ตอนนี้เป็นส่วนที่น่าสนใจที่สุดของพื้นที่เก็บข้อมูลgetList()วิธีการ วิธีการที่มีการแปลสภาพลงในโทรวิธีการในการเก็บรวบรวม
getList()SerachCriteria

ส่วนที่ยุ่งยากของการรับANDและORเงื่อนไขสำหรับตัวกรองถูกต้องโดยเฉพาะอย่างยิ่งเนื่องจากไวยากรณ์สำหรับการตั้งค่าเงื่อนไขในคอลเลกชันจะแตกต่างกันขึ้นอยู่กับว่ามันเป็น EAV หรือเอนทิตีตารางแบน

ในกรณีส่วนใหญ่getList()สามารถใช้งานได้ตามตัวอย่างด้านล่าง

<?php

namespace VinaiKopp\Kitchen\Model;

use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SortOrder;
use Magento\Framework\Exception\NoSuchEntityException;
use VinaiKopp\Kitchen\Api\Data\HamburgerInterface;
use VinaiKopp\Kitchen\Api\Data\HamburgerSearchResultInterface;
use VinaiKopp\Kitchen\Api\Data\HamburgerSearchResultInterfaceFactory;
use VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface;
use VinaiKopp\Kitchen\Model\ResourceModel\Hamburger\CollectionFactory as HamburgerCollectionFactory;
use VinaiKopp\Kitchen\Model\ResourceModel\Hamburger\Collection;

class HamburgerRepository implements HamburgerRepositoryInterface
{
    /**
     * @var HamburgerFactory
     */
    private $hamburgerFactory;

    /**
     * @var HamburgerCollectionFactory
     */
    private $hamburgerCollectionFactory;

    /**
     * @var HamburgerSearchResultInterfaceFactory
     */
    private $searchResultFactory;

    public function __construct(
        HamburgerFactory $hamburgerFactory,
        HamburgerCollectionFactory $hamburgerCollectionFactory,
        HamburgerSearchResultInterfaceFactory $hamburgerSearchResultInterfaceFactory
    ) {
        $this->hamburgerFactory = $hamburgerFactory;
        $this->hamburgerCollectionFactory = $hamburgerCollectionFactory;
        $this->searchResultFactory = $hamburgerSearchResultInterfaceFactory;
    }

    // ... getById, save and delete methods listed above ...

    public function getList(SearchCriteriaInterface $searchCriteria)
    {
        $collection = $this->collectionFactory->create();

        $this->addFiltersToCollection($searchCriteria, $collection);
        $this->addSortOrdersToCollection($searchCriteria, $collection);
        $this->addPagingToCollection($searchCriteria, $collection);

        $collection->load();

        return $this->buildSearchResult($searchCriteria, $collection);
    }

    private function addFiltersToCollection(SearchCriteriaInterface $searchCriteria, Collection $collection)
    {
        foreach ($searchCriteria->getFilterGroups() as $filterGroup) {
            $fields = $conditions = [];
            foreach ($filterGroup->getFilters() as $filter) {
                $fields[] = $filter->getField();
                $conditions[] = [$filter->getConditionType() => $filter->getValue()];
            }
            $collection->addFieldToFilter($fields, $conditions);
        }
    }

    private function addSortOrdersToCollection(SearchCriteriaInterface $searchCriteria, Collection $collection)
    {
        foreach ((array) $searchCriteria->getSortOrders() as $sortOrder) {
            $direction = $sortOrder->getDirection() == SortOrder::SORT_ASC ? 'asc' : 'desc';
            $collection->addOrder($sortOrder->getField(), $direction);
        }
    }

    private function addPagingToCollection(SearchCriteriaInterface $searchCriteria, Collection $collection)
    {
        $collection->setPageSize($searchCriteria->getPageSize());
        $collection->setCurPage($searchCriteria->getCurrentPage());
    }

    private function buildSearchResult(SearchCriteriaInterface $searchCriteria, Collection $collection)
    {
        $searchResults = $this->searchResultFactory->create();

        $searchResults->setSearchCriteria($searchCriteria);
        $searchResults->setItems($collection->getItems());
        $searchResults->setTotalCount($collection->getSize());

        return $searchResults;
    }
}

ตัวกรองภายในFilterGroupต้องรวมกันโดยใช้ตัวดำเนินการOR
กลุ่มกรองเฉพาะกิจการจะรวมกันโดยใช้ตรรกะและผู้ประกอบการ

ว้า
นี่เป็นงานที่ใหญ่ที่สุด การใช้อินเตอร์เฟสอื่น ๆ นั้นง่ายกว่า

DTO

Magento เดิมทีตั้งใจให้นักพัฒนาใช้ DTO เป็นคลาสแยกต่างหากซึ่งแตกต่างจากเอนทิตีโมเดล

ทีมหลักทำสิ่งนี้เฉพาะกับโมดูลของลูกค้าเท่านั้น ( \Magento\Customer\Api\Data\CustomerInterfaceดำเนินการโดย\Magento\Customer\Model\Data\Customerไม่ใช่\Magento\Customer\Model\Customer)
ในกรณีอื่นทั้งหมดโมเดลเอนทิตีใช้อินเทอร์เฟซ DTO (ตัวอย่างเช่น\Magento\Catalog\Api\Data\ProductInterfaceนำมาใช้โดย\Magento\Catalog\Model\Product)

ฉันถามสมาชิกของทีมหลักเกี่ยวกับเรื่องนี้ในการประชุม แต่ฉันไม่ได้รับการตอบสนองที่ชัดเจนว่าอะไรคือสิ่งที่ควรพิจารณาในการฝึกฝนที่ดี
ความประทับใจของฉันคือการแนะนำนี้ถูกทอดทิ้ง มันคงจะดีถ้าได้รับแถลงการณ์อย่างเป็นทางการในเรื่องนี้

สำหรับตอนนี้ฉันได้ทำการตัดสินใจอย่างจริงจังเพื่อใช้โมเดลเป็นการนำอินเตอร์เฟส DTO ไปใช้งาน หากคุณรู้สึกว่าการใช้โมเดลข้อมูลแยกต่างหากนั้นสะอาดกว่า วิธีการทั้งสองทำงานได้ดีในทางปฏิบัติ

หาก inteface DTO จะขยายรูปแบบที่มีการขยายMagento\Framework\Api\ExtensibleDataInterface หากคุณไม่สนใจเกี่ยวกับความสามารถในการขยายโมเดลก็สามารถขยายคลาสพื้นฐานของโมเดล ORM ต่อไปได้ Magento\Framework\Model\AbstractExtensibleModel
Magento\Framework\Model\AbstractModel

เนื่องจากตัวอย่างHamburgerInterfaceขยายExtensibleDataInterfaceรูปแบบแฮมเบอร์เกอร์ที่ขยายAbstractExtensibleModelดังที่สามารถเห็นได้ที่นี่:

<?php

namespace VinaiKopp\Kitchen\Model;

use Magento\Framework\Model\AbstractExtensibleModel;
use VinaiKopp\Kitchen\Api\Data\HamburgerExtensionInterface;
use VinaiKopp\Kitchen\Api\Data\HamburgerInterface;

class Hamburger extends AbstractExtensibleModel implements HamburgerInterface
{
    const NAME = 'name';
    const INGREDIENTS = 'ingredients';
    const IMAGE_URLS = 'image_urls';

    protected function _construct()
    {
        $this->_init(ResourceModel\Hamburger::class);
    }

    public function getName()
    {
        return $this->_getData(self::NAME);
    }

    public function setName($name)
    {
        $this->setData(self::NAME, $name);
    }

    public function getIngredients()
    {
        return $this->_getData(self::INGREDIENTS);
    }

    public function setIngredients(array $ingredients)
    {
        $this->setData(self::INGREDIENTS, $ingredients);
    }

    public function getImageUrls()
    {
        $this->_getData(self::IMAGE_URLS);
    }

    public function setImageUrls(array $urls)
    {
        $this->setData(self::IMAGE_URLS, $urls);
    }

    public function getExtensionAttributes()
    {
        return $this->_getExtensionAttributes();
    }

    public function setExtensionAttributes(HamburgerExtensionInterface $extensionAttributes)
    {
        $this->_setExtensionAttributes($extensionAttributes);
    }
}

การแยกชื่อคุณสมบัติเป็นค่าคงที่อนุญาตให้เก็บไว้ในที่เดียว สามารถใช้ได้โดยคู่ getter / setter และโดยสคริปต์การตั้งค่าที่สร้างตารางฐานข้อมูล มิฉะนั้นจะไม่มีประโยชน์ในการแยกมันออกเป็นค่าคงที่

SearchResult

นี่SearchResultsInterfaceเป็นอินเทอร์เฟซสามตัวที่ง่ายที่สุดที่จะใช้เนื่องจากมันสามารถสืบทอดฟังก์ชันการทำงานทั้งหมดได้จากคลาสเฟรมเวิร์ก

<?php

namespace VinaiKopp\Kitchen\Model;

use Magento\Framework\Api\SearchResults;
use VinaiKopp\Kitchen\Api\Data\HamburgerSearchResultInterface;

class HamburgerSearchResult extends SearchResults implements HamburgerSearchResultInterface
{

}

กำหนดค่าการกำหนดค่าตามความชอบ ObjectManager

แม้ว่าการใช้งานจะเสร็จสมบูรณ์เรายังคงไม่สามารถใช้อินเทอร์เฟซเป็นการพึ่งพาของคลาสอื่น ๆ ได้เนื่องจากตัวจัดการวัตถุ Magento Framework ไม่ทราบว่าการประยุกต์ใช้งานแบบใด เราจำเป็นต้องเพิ่มการetc/di.xmlกำหนดค่าสำหรับด้วยการตั้งค่า

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface" type="VinaiKopp\Kitchen\Model\HamburgerRepository"/>
    <preference for="VinaiKopp\Kitchen\Api\Data\HamburgerInterface" type="VinaiKopp\Kitchen\Model\Hamburger"/>
    <preference for="VinaiKopp\Kitchen\Api\Data\HamburgerSearchResultInterface" type="VinaiKopp\Kitchen\Model\HamburgerSearchResult"/>
</config>

ที่เก็บสามารถแสดงเป็นทรัพยากร API ได้อย่างไร

ส่วนนี้ง่ายมากมันเป็นรางวัลสำหรับการทำงานทั้งหมดที่สร้างอินเทอร์เฟซการใช้งานและการเชื่อมต่อเข้าด้วยกัน

สิ่งที่เราต้องทำคือสร้างetc/webapi.xmlไฟล์

<?xml version="1.0"?>
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">
    <route method="GET" url="/V1/vinaikopp_hamburgers/:id">
        <service class="VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface" method="getById"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
    <route method="GET" url="/V1/vinaikopp_hamburgers">
        <service class="VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface" method="getList"/>
        <resources>
            <resource ref="anonymouns"/>
        </resources>
    </route>
    <route method="POST" url="/V1/vinaikopp_hamburgers">
        <service class="VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface" method="save"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
    <route method="PUT" url="/V1/vinaikopp_hamburgers">
        <service class="VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface" method="save"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
    <route method="DELETE" url="/V1/vinaikopp_hamburgers">
        <service class="VinaiKopp\Kitchen\Api\HamburgerRepositoryInterface" method="delete"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
</routes>

โปรดทราบว่าการกำหนดค่านี้ไม่เพียง แต่ช่วยให้การใช้พื้นที่เก็บข้อมูลเป็นปลายทาง REST แต่ยังเปิดเผยวิธีการที่เป็นส่วนหนึ่งของ SOAP API

ในเส้นทางตัวอย่างแรก<route method="GET" url="/V1/vinaikopp_hamburgers/:id">ตัวยึดมีให้ตรงกับชื่อของการโต้แย้งเป็นวิธีแมป:id สองชื่อต้องตรงกับตัวอย่างเช่นจะไม่ทำงานเนื่องจากชื่อวิธีตัวแปรอาร์กิวเมนต์เป็น public function getById($id)
/V1/vinaikopp_hamburgers/:hamburgerId$id

สำหรับตัวอย่างนี้ผมได้ตั้ง accessability <resource ref="anonymous"/>ไป ซึ่งหมายความว่าทรัพยากรถูกเปิดเผยสู่สาธารณะโดยไม่มีข้อ จำกัด ! เพื่อให้เป็นแหล่งข้อมูลที่สามารถใช้ได้เฉพาะกับลูกค้าเข้าสู่ระบบในการใช้งาน
<resource ref="self"/>ในกรณีนี้คำพิเศษmeใน URL ปลายทางของทรัพยากรจะถูกใช้เพื่อเติมตัวแปรอาร์กิวเมนต์$idด้วย ID ของลูกค้าที่เข้าสู่ระบบในปัจจุบัน
ลองดูที่ Magento Customer etc/webapi.xmlและCustomerRepositoryInterfaceหากคุณต้องการสิ่งนั้น

ในที่สุด<resources>สามารถใช้เพื่อ จำกัด การเข้าถึงทรัพยากรไปยังบัญชีผู้ใช้ของผู้ดูแลระบบ เมื่อต้องการทำสิ่งนี้ให้ตั้งค่าการ<resource>อ้างอิงเป็นตัวระบุที่กำหนดไว้ในetc/acl.xmlไฟล์
ตัวอย่างเช่น<resource ref="Magento_Customer::manage"/>จะ จำกัด การเข้าถึงบัญชีผู้ดูแลระบบที่มีสิทธิ์จัดการลูกค้า

ตัวอย่างแบบสอบถาม API ที่ใช้ curl อาจมีลักษณะเช่นนี้:

$ curl -X GET http://example.com/rest/V1/vinaikopp_hamburgers/123

หมายเหตุ: การเขียนสิ่งนี้เริ่มต้นเป็นคำตอบไปที่https://github.com/astorm/pestle/issues/195
ตรวจสอบสาก , ซื้อCommercebugและกลายเป็นpatreonของ @alanstorm


1
ขอบคุณสำหรับคำตอบที่ยอดเยี่ยมนี้ ขออภัยบางทีฉันหายไปบางอย่าง แต่จุดที่มีอินเทอร์เฟซใหม่ทั้งหมดสำหรับเอนทิตีเมื่อสิ้นสุดจะต้องขยายจาก AbstractModel ที่มีเมธอด setData ซึ่งหมายความว่าคุณสามารถเพิ่มอะไรลงในวัตถุโดยไม่คำนึงถึงอินเทอร์เฟซ
LDusan

คลาสสามารถใช้อินเทอร์เฟซจำนวนเท่าใดก็ได้และเพิ่มวิธีการเพิ่มเติมเช่นกัน สิ่งสำคัญคือคลาสอื่น ๆ นั้นขึ้นอยู่กับวิธีการเชื่อมต่อเท่านั้นดังนั้นจึงไม่รู้เกี่ยวกับสิ่งอื่นใด ซึ่งทำให้รายละเอียดการใช้งานที่ไม่ใช่อินเตอร์เฟสสามารถเปลี่ยนแปลงได้ตลอดเวลาโดยไม่ต้องทำลายคลาสภายนอก นั่นคือแนวคิดที่อยู่เบื้องหลังการผกผันของการพึ่งพา ทั้งคลาสและไคลเอนต์ใด ๆ ขึ้นอยู่กับอินเทอร์เฟซและไม่รู้เกี่ยวกับรายละเอียดการใช้งาน ชัดเจนหรือไม่
Vinai

ขอบคุณสำหรับคำตอบฉันเห็นสิ่งที่คุณหมายถึง สิ่งที่เป็นที่ setData เป็นวิธีการสาธารณะดังนั้นฉันไม่แน่ใจว่ามันจะถือเป็นรายละเอียดการใช้งาน ถ้ามันถูกใช้งานเหมือนวิธีสาธารณะเราจะแน่ใจได้อย่างไรว่ามันจะไม่แตกอะไรภายนอกเมื่อมีการเปลี่ยนแปลง?
LDusan

3
ฉันขอโทษ. สิ่งที่คุณกำลังอธิบายเป็นมุมมองทั่วไป กลไกการพึ่งพาไม่ง่ายและเนื่องจาก PHP อนุญาตให้เรียกวิธีการที่ไม่ได้เป็นส่วนหนึ่งของการพึ่งพาส่วนต่อประสานและเนื่องจากไม่จำเป็นต้องคอมไพล์จึงทำให้วิธีการพึ่งพาทำงานมากขึ้นพร่ามัวและยากที่จะเห็นชัดเจน . ซึ่งสามารถสังเกตได้ในแกนกลางวีโอไอพี 2 ซึ่งมีหลายสถานที่ซึ่งเรียกวิธีการติดตั้งซึ่งไม่ได้ขึ้นอยู่กับส่วนต่อประสาน สิ่งเหล่านี้เป็นตัวอย่างที่ไม่ดีและทำให้การเข้าใจที่ชัดเจนและชัดเจนยิ่งขึ้น
Vinai

1
ขอให้เรายังคงอภิปรายนี้ในการแชท
Vinai

35

@ ราฟาเอลที่ Pianism ดิจิตอล:

โปรดอ้างอิงโครงสร้างตัวอย่างโมดูลต่อไปนี้:

app/
   code/
  |    Namespace/
  |   |    Custom/
  |   |   |    Api/
  |   |   |   |    CustomRepositoryInterface.php
  |   |   |   |    Data/
  |   |   |   |   |    CustomInterface.php
  |   |   |   |   |    CustomSearchResultsInterface.php
  |   |   |    etc/
  |   |   |   |    di.xml
  |   |   |   |    module.xml
  |   |   |    Model/
  |   |   |   |    Custom.php
  |   |   |   |    CustomRepository.php
  |   |   |   |    ResourceModel/
  |   |   |   |   |    Custom.php
  1. สร้างส่วนต่อประสานที่เก็บข้อมูล (สัญญาบริการ)
    Namespace/Custom/Api/CustomRepositoryInterface.php: http://codepad.org/WognSKnH

  2. สร้าง SearchResultsInterface
    Namespace/Custom/Api/Data/CustomSearchResultsInterface.php: http://codepad.org/zcbi8X4Z

  3. สร้าง CustomInterface (Data Container)
    Namespace/Custom/Api/Data/CustomInterface.php: http://codepad.org/Ze53eT4o

  4. สร้าง CustomRepository (Concrete Repository)
    Namespace/Custom/Model/CustomRepository.php: http://codepad.org/KNt5QAGZ ที่
    นี่คือ "เวทมนต์" ที่เกิดขึ้น ผ่าน Constructor DI คุณผ่านโมเดลทรัพยากร / คลังเก็บสำหรับโมดูลที่กำหนดเองของคุณ เกี่ยวกับวิธีบันทึก CRUD ในที่เก็บนี้เนื่องจาก CustomRepositoryInterface ของคุณคุณต้องผ่านพารามิเตอร์ CustomInterface โมดูล di.xml ของคุณมีการตั้งค่าเพื่อแทนที่อินเทอร์เฟซประเภทนี้ด้วยโมเดลเอนทิตี โมเดลเอนทิตีได้รับการส่งผ่านไปยังโมเดลรีซอร์สและถูกบันทึก

  5. ตั้งค่าเป็น
    Namespace/Custom/etc/di.xml: http://codepad.org/KmcoOUeV

  6. รุ่น Entity ใช้อินเตอร์เฟซที่กำหนดเอง (ข้อมูลคอนเทนเนอร์)
    Namespace/Custom/Model/Custom.php: http://codepad.org/xQiBU7p7

  7. โมเดลทรัพยากร
    Namespace/Custom/Model/ResourceModel/Custom.php: http://codepad.org/IOsxm9qW

สิ่งที่ควรทราบ:

  • การปฏิเสธความรับผิดชอบ !!! ฉันใช้ "Namespace" แทนชื่อผู้จำหน่ายที่กำหนดเองของคุณชื่อเอเจนซี่ ฯลฯ ... ชื่ออะไรก็ตามที่คุณใช้เพื่อจัดกลุ่มโมดูลของคุณเข้าด้วยกัน ... การใช้ "Namespace" ที่แท้จริงนั้นไม่ถูกต้องใน Php ... ดังนั้นโปรดทราบ ที่ฉันทำสิ่งนี้เพราะเห็นแก่ความสะดวกสบายและฉันไม่คิดว่ามันจะใช้งานได้หรือฉันไม่แนะนำให้ทำ แต่อย่างใด

  • @Ryan Street สอนฉันนี้ ... ดังนั้นฉันไม่ต้องการรับเครดิตทั้งหมด

  • เปลี่ยนการใช้ Repository อย่างชัดเจนเพื่อให้เหมาะกับความต้องการของคุณ

  • คุณใช้การโต้ตอบกับเอนทิตีแบบกำหนดเองของคุณ / โมเดลทรัพยากร / คอลเลกชันในพื้นที่เก็บข้อมูลคอนกรีต ...

  • ฉันรู้ว่าฉันไม่ได้กล่าวถึงวิธีการทั้งหมดที่คุณระบุไว้ในคำถามของคุณ แต่นี่เป็นการเริ่มต้นที่ดีและควรเชื่อมช่องว่างระหว่างเอกสารและการใช้งานจริง


ไรอันมีวิธีการที่กล่าวถึงในสัญญาบริการที่บังคับใช้สำหรับสบู่ที่กำหนดเองใด ๆ ที่เราสร้างเช่น save (), delete () ฯลฯ ?
Sushivam

คุณช่วยบอกฉันเกี่ยวกับวิธีสร้างสบู่ api แบบกำหนดเองใน magento 2 ได้ไหม
Sushivam

@SachinS น่าเสียดายที่ฉันไม่มีข้อมูลเชิงลึกที่จะเสนอเกี่ยวกับสบู่ ฉันยังไม่ได้ตรวจสอบและยังไม่ได้ใช้งาน สิ่งที่ดีที่สุดที่ฉันสามารถแนะนำได้ก็คือเปิดคำถามใหม่เกี่ยวกับเรื่องนี้ ฉันจะบอกว่าตรวจสอบเอกสารด้วย แต่นั่นก็ไม่ใช่วิธีที่ดีที่สุดในการทำทุกอย่างที่น่าเศร้า คุณสามารถดูที่รหัสฐานหลักหรือส่วนขยายของบุคคลที่สามและดูว่ามีข้อมูลเชิงลึกใด ๆ หรือไม่ โชคดี! หากคุณพบคำตอบของคุณอาจเป็นการเพิ่มลิงค์ที่นี่ ขอบคุณ
ryanF

ขอบคุณสำหรับการตอบกลับ @ryan, anyways ฉันใช้โมดูลของฉันโดยใช้ REST เนื่องจากมีน้ำหนักเบาเมื่อเทียบกับ SOAP ... ถ้าฉันใช้ SOAP เดียวกันใน SOAP, ไม่โพสต์มัน
Sushivam

3
@ryanF ขอบคุณสำหรับคำตอบที่มีประโยชน์มาก ฉันรู้ว่ามันไม่ควรที่จะคัดลอก / วางรหัสการทำงาน แต่นี่เป็นข้อผิดพลาดบางประการเพื่อประโยชน์ของผู้อื่นต่อไปนี้ ในที่เก็บ CustomSearchResultsInterfaceFactory ควรเป็น CustomSearchResultsFactory $ searchResults-> setCriteria ควรเป็น $ searchResults-> setSearchCriteria $ Customs [] ใน foreach ควรเป็น $ customs [] ฉันคิดว่ามันเกี่ยวกับมัน
tetranz

3

ไฟล์ทั้งหมดของการใช้สัญญาบริการ

ที่กำหนดเอง / โมดูล / registration.php

<?php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Custom_Module',
    __DIR__
);

../etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Custom_Module" setup_version="1.0.0" />
</config>

../Setup/InstallSchema.php

<?php
namespace Custom\Module\Setup;
use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\DB\Ddl\Table;
class InstallSchema implements InstallSchemaInterface {
    public function install( SchemaSetupInterface $setup, ModuleContextInterface $context ) {
        $installer = $setup;
        $installer->startSetup();
        $table = $installer->getConnection()->newTable(
            $installer->getTable( 'ad_shipping_quote' )
        )->addColumn(
            'entity_id',
            Table::TYPE_SMALLINT,
            null,
            [ 'identity' => true, 'nullable' => false, 'primary' => true ],
            'Post ID'
        )->addColumn(
            'product_id',
            Table::TYPE_SMALLINT,
            255,
            [ ],
            'Post ID'
        )
            ->addColumn(
            'customer_name',
            Table::TYPE_TEXT,
            255,
            [ 'nullable' => false ],
            'Post Title'
        )

            ->addColumn(
            'customer_email',
            Table::TYPE_TEXT,
            '2M',
            [ ],
            'Post Content'
        ) ->addColumn(
                'customer_comments',
                Table::TYPE_TEXT,
                255,
                [ 'nullable' => false ],
                'Post Title'
            )->addColumn(
                'date_added',
                Table::TYPE_TEXT,
                255,
                [ 'nullable' => false ],
                'Post Title'
            )->addColumn(
                'date_updated',
                Table::TYPE_TEXT,
                255,
                [ 'nullable' => false ],
                'Post Title'
            )
            ->setComment(
            'Ad Shipping Quote Table'
        );
        $installer->getConnection()->createTable( $table );
        $installer->endSetup();
    }
}

../etc/di.xml

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Custom\Module\Api\ModelRepositoryInterface"
                type="Custom\Module\Model\ModelRepository" />
    <preference for="Custom\Module\Api\Data\ModelInterface"
                type="Custom\Module\Model\Model" />
    <preference for="Custom\Module\Api\Data\ModelSearchResultsInterface"
                type="Custom\Module\Model\ModelSearchResults" />
</config>

../etc/webapi.xml

  <?xml version="1.0"?>
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">

    <route method="GET" url="/V1/model/:id">
        <service class="Custom\Module\Api\ModelRepositoryInterface" method="getById"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>


    <route method="GET" url="/V1/model">
        <service class="Custom\Module\Api\ModelRepositoryInterface" method="getList"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
</routes>

../Api/ModelRepositoryInterface.php

  <?php
namespace Custom\Module\Api;

use \Custom\Module\Api\Data\ModelInterface;
use \Magento\Framework\Api\SearchCriteriaInterface;

interface ModelRepositoryInterface
{
    /**
     * @api
     * @param \Custom\Module\Api\Data\ModelInterface $model
     * @return \Custom\Module\Api\Data\ModelInterface
     */
    public function save(ModelInterface $model);

    /**
     * @api
     * @param \Custom\Module\Api\Data\ModelInterface $model
     * @return \Custom\Module\Api\Data\ModelInterface
     */
    public function delete(ModelInterface $model);

    /**
     * @api
     * @param \Custom\Module\Api\Data\ModelInterface $id
     * @return void
     */
    public function deleteById($id);

    /**
     * @api
     * @param int $id
     * @return \Custom\Module\Api\Data\ModelInterface
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    public function getById($id);

    /**
     * @api
     * @param \Magento\Framework\Api\SearchCriteriaInterface $criteria
     * @return \Custom\Module\Api\Data\ModelSearchResultsInterface
     */
    public function getList(SearchCriteriaInterface $criteria);
}

../Api/Data/ModelInterface.php

<?php
namespace Custom\Module\Api\Data;

interface ModelInterface
{
    /**
     * Return the Entity ID
     *
     * @return int
     */
    public function getEntityId();

    /**
     * Set Entity ID
     *
     * @param int $id
     * @return $this
     */
    public function setEntityId($id);

    /**
     * Return the Product ID associated with Quote
     *
     * @return int
     */
    public function getProductId();

    /**
     * Set the Product ID associated with Quote
     *
     * @param int $productId
     * @return $this
     */
    public function setProductId($productId);

    /**
     * Return the Customer Name
     *
     * @return string
     */
    public function getCustomerName();

    /**
     * Set the Customer Name
     *
     * @param string $customerName
     * @return $this
     */
    public function setCustomerName($customerName);

    /**
     * Return the Customer Email
     *
     * @return string
     */
    public function getCustomerEmail();

    /**
     * Set the Customer Email
     *
     * @param string $customerEmail
     * @return $this
     */
    public function setCustomerEmail($customerEmail);

    /**
     * Return the Customer Comments
     *
     * @return string
     */
    public function getCustomerComments();

    /**
     * Set the Customer Comments
     *
     * @param string $customerComments
     * @return $this
     */
    public function setCustomerComments($customerComments);

    /**
     * Return the Date and Time of record added
     *
     * @return string
     */
    public function getDateAdded();

    /**
     * Set the Date and Time of record added
     *
     * @param string $date
     * @return $this
     */
    public function setDateAdded($date);

    /**
     * Return the Date and Time of record updated
     *
     * @return string
     */
    public function getDateUpdated();

    /**
     * Set the Date and Time of record updated
     *
     * @param string $date
     * @return $this
     */
    public function setDateUpdated($date);
}

..Api / ข้อมูล / ModelSearchResultsInterface.php

<?php

namespace Custom\Module\Api\Data;

use Magento\Framework\Api\SearchResultsInterface;

interface ModelSearchResultsInterface extends SearchResultsInterface
{
    /**
     * @return \Custom\Module\Api\Data\ModelInterface[]
     */
    public function getItems();

    /**
     * @param \Custom\Module\Api\Data\ModelInterface[] $items
     * @return $this
     */
    public function setItems(array $items);
}

../Model/Model.php

    <?php

namespace Custom\Module\Model;

use Custom\Module\Api\Data\ModelInterface;

class Model extends \Magento\Framework\Model\AbstractModel implements
    \Custom\Module\Api\Data\ModelInterface
{
    protected function _construct()
    {
        $this->_init('Custom\Module\Model\ResourceModel\Model');
    }

    /**
     * @inheritdoc
     */
    public function getEntityId()
    {
        return $this->_getData('entity_id');
    }

    /**
     * @inheritdoc
     */
    public function setEntityId($id)
    {
        $this->setData('entity_id', $id);
    }

    /**
     * @inheritdoc
     */
    public function getProductId()
    {
        return $this->_getData('product_id');
    }

    /**
     * @inheritdoc
     */
    public function setProductId($productId)
    {
        $this->setData('product_id', $productId);
    }

    /**
     * @inheritdoc
     */
    public function getCustomerName()
    {
        return $this->_getData('customer_name');
    }

    /**
     * @inheritdoc
     */
    public function setCustomerName($customerName)
    {
        $this->setData('customer_name', $customerName);
    }

    /**
     * @inheritdoc
     */
    public function getCustomerEmail()
    {
        return $this->_getData('customer_email');
    }

    /**
     * @inheritdoc
     */
    public function setCustomerEmail($customerEmail)
    {
        $this->setData('customer_email', $customerEmail);
    }

    /**
     * @inheritdoc
     */
    public function getCustomerComments()
    {
        return $this->_getData('customer_comments');
    }

    /**
     * @inheritdoc
     */
    public function setCustomerComments($customerComments)
    {
        $this->setData('customer_comments', $customerComments);
    }

    /**
     * @inheritdoc
     */
    public function getDateAdded()
    {
        return $this->_getData('date_added');
    }

    /**
     * @inheritdoc
     */
    public function setDateAdded($date)
    {
        $this->setData('date_added', $date);
    }

    /**
     * @inheritdoc
     */
    public function getDateUpdated()
    {
        return $this->_getData('date_updated');
    }

    /**
     * @inheritdoc
     */
    public function setDateUpdated($date)
    {
        $this->setData('date_updated', $date);
    }
}

../Model/ResourceModel/Model.php

<?php

namespace Custom\Module\Model\ResourceModel;

class Model extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
    protected $_idFieldName = 'entity_id';

    protected function _construct()
    {
        $this->_init('ad_shipping_quote','entity_id');
    }
}

../Model/ResourceModel/Model/Collection.php

<?php

namespace Custom\Module\Model\ResourceModel\Model;

class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
    protected $_idFieldName = 'entity_id';
    protected $_eventPrefix = 'ad_shipping_quote_collection';
    protected $_eventObject = 'quote_collection';

    protected function _construct()
    {
        $this->_init('Custom\Module\Model\Model', 'Custom\Module\Model\ResourceModel\Model');
    }
}

../Model/ModelRepository.php

 <?php
    namespace Custom\Module\Model;

    use \Custom\Module\Api\Data\ModelInterface;
    use \Custom\Module\Model\ResourceModel\Model as ObjectResourceModel;
    use \Magento\Framework\Api\SearchCriteriaInterface;
    use \Magento\Framework\Exception\CouldNotSaveException;
    use \Magento\Framework\Exception\NoSuchEntityException;
    use \Magento\Framework\Exception\CouldNotDeleteException;

    class ModelRepository implements \Custom\Module\Api\ModelRepositoryInterface
    {
        protected $objectFactory;

        protected $objectResourceModel;

        protected $collectionFactory;

        protected $searchResultsFactory;

        public function __construct(
            \Custom\Module\Model\ModelFactory $objectFactory,
            ObjectResourceModel $objectResourceModel,
            \Custom\Module\Model\ResourceModel\Model\CollectionFactory $collectionFactory,
            \Magento\Framework\Api\SearchResultsInterfaceFactory $searchResultsFactory
        ) {
            $this->objectFactory        = $objectFactory;
            $this->objectResourceModel  = $objectResourceModel;
            $this->collectionFactory    = $collectionFactory;
            $this->searchResultsFactory = $searchResultsFactory;
        }

        public function save(ModelInterface $object)
        {
            $name = $object->getCustomerName();
            $hasSpouse = $object->getSpouse();
            if ($hasSpouse == true) {
                $name = "Mrs. " . $name;
            } else {
                $name = "Miss. " . $name;
            }
            $object->setCustomerName($name);
            try {
                $this->objectResourceModel->save($object);
            } catch (\Exception $e) {
                throw new CouldNotSaveException(__($e->getMessage()));
            }
            return $object;
        }

        /**
         * @inheritdoc
         */
        public function getById($id)
        {
            $object = $this->objectFactory->create();
            $this->objectResourceModel->load($object, $id);
            if (!$object->getId()) {
                throw new NoSuchEntityException(__('Object with id "%1" does not exist.', $id));
            }
            return $object;
        }

        public function delete(ModelInterface $object)
        {
            try {
                $this->objectResourceModel->delete($object);
            } catch (\Exception $exception) {
                throw new CouldNotDeleteException(__($exception->getMessage()));
            }
            return true;
        }

        public function deleteById($id)
        {
            return $this->delete($this->getById($id));
        }

        /**
         * @inheritdoc
         */
        public function getList(SearchCriteriaInterface $criteria)
        {
            $searchResults = $this->searchResultsFactory->create();
            $searchResults->setSearchCriteria($criteria);
            $collection = $this->collectionFactory->create();
            foreach ($criteria->getFilterGroups() as $filterGroup) {
                $fields = [];
                $conditions = [];
                foreach ($filterGroup->getFilters() as $filter) {
                    $condition = $filter->getConditionType() ? $filter->getConditionType() : 'eq';
                    $fields[] = $filter->getField();
                    $conditions[] = [$condition => $filter->getValue()];
                }
                if ($fields) {
                    $collection->addFieldToFilter($fields, $conditions);
                }
            }
            $searchResults->setTotalCount($collection->getSize());
            $sortOrders = $criteria->getSortOrders();
            if ($sortOrders) {
                /** @var SortOrder $sortOrder */
                foreach ($sortOrders as $sortOrder) {
                    $collection->addOrder(
                        $sortOrder->getField(),
                        ($sortOrder->getDirection() == SortOrder::SORT_ASC) ? 'ASC' : 'DESC'
                    );
                }
            }
            $collection->setCurPage($criteria->getCurrentPage());
            $collection->setPageSize($criteria->getPageSize());
            $objects = [];
            foreach ($collection as $objectModel) {
                $objects[] = $objectModel;
            }
            $searchResults->setItems($objects);
            return $searchResults;
        }
    }

../Model/ModelSearchResults.php

namespace Custom\Module\Model;

use \Magento\Framework\Api\SearchResults;
use \Custom\Module\Api\Data\ModelSearchResultsInterface;


class ModelSearchResults extends SearchResults implements ModelSearchResultsInterface
{

}

../Controller/Index/Save.php

<?php

namespace Custom\Module\Controller\Index;

use \Magento\Framework\Controller\Result\RawFactory;

class Save extends \Magento\Framework\App\Action\Action
{

    /**
     * Index resultPageFactory
     * @var PageFactory
     */
    private $resultPageFactory;
    /**
     * @var
     */
    private $modelFactory;
    /**
     * @var
     */
    private $modelRepository;


    /**
     * Index constructor.
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     * @param \Custom\Module\Model\ModelFactory $modelFactory
     * @param \Custom\Module\Model\ModelRepository $modelRepository
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory,
        \Custom\Module\Model\ModelFactory $modelFactory,
        \Custom\Module\Model\ModelRepository $modelRepository
) {
        $this->resultPageFactory = $resultPageFactory;
        $this->modelFactory = $modelFactory;
        $this->modelRepository = $modelRepository;
        return parent::__construct($context);


    }

    /**
     * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $data = [

            "product_id" => 201,
            "customer_name" => "Katrina",
            "customer_email" => "karina@kapoor.com",
            "spouse" => 1
        ];

        $obj = $this->modelFactory->create();
        $this->modelRepository->save($obj->addData($data)); // Service Contract


        //$obj->addData($data)->save(); // Model / Resource Model

        $this->resultFactory->create("raw");
    }
}

../Controller/Index/Getlist.php

<?php

namespace Custom\Module\Controller\Index;

use \Magento\Framework\Controller\Result\RawFactory;

class Getlist extends \Magento\Framework\App\Action\Action
{

    /**
     * Index resultPageFactory
     * @var PageFactory
     */
    private $resultPageFactory;
    /**
     * @var
     */
    private $modelFactory;
    /**
     * @var
     */
    private $modelRepository;
    /**
     * @var
     */
    private $searchCriteriaBuilder;


    /**
     * Index constructor.
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     * @param \Custom\Module\Model\ModelRepository $modelRepository
     * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory,
        \Custom\Module\Model\ModelRepository $modelRepository,
        \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder
) {
        $this->resultPageFactory = $resultPageFactory;
        $this->modelRepository = $modelRepository;
        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
        return parent::__construct($context);
    }

    /**
     * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $_filter = $this->searchCriteriaBuilder
            ->addFilter("customer_name", "%na%", "like")->create();
        $list = $this->modelRepository->getList($_filter);
        $results = $list->getItems();
        foreach ($results as $result) {
            echo $result->getCustomerName() . "<br>";
        }




        $this->resultFactory->create("raw");
    }
}

../Controller/Index/Getbyid.php

<?php

namespace Custom\Module\Controller\Index;

use \Magento\Framework\Controller\Result\RawFactory;

class Getbyid extends \Magento\Framework\App\Action\Action
{

    /**
     * Index resultPageFactory
     * @var PageFactory
     */
    private $resultPageFactory;
    /**
     * @var
     */
    private $modelRepository;

    /**
     * Index constructor.
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     * @param \Custom\Module\Model\ModelRepository $modelRepository
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory,
        \Custom\Module\Model\ModelRepository $modelRepository

) {
        $this->resultPageFactory = $resultPageFactory;
        $this->modelRepository = $modelRepository;
        return parent::__construct($context);
    }

    public function execute()
    {

        $search = $this->modelRepository->getById(1);
        print_r($search->getData());

        $this->resultFactory->create("raw");
    }
}

../Controller/Index/Deletebyid.php

<?php

namespace Custom\Module\Controller\Index;

use \Magento\Framework\Controller\Result\RawFactory;

class Deletbyid extends \Magento\Framework\App\Action\Action
{

    /**
     * Index resultPageFactory
     * @var PageFactory
     */
    private $resultPageFactory;
    /**
     * @var
     */
    private $modelRepository;

    /**
     * Index constructor.
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     * @param \Custom\Module\Model\ModelRepository $modelRepository
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory,
        \Custom\Module\Model\ModelRepository $modelRepository

) {
        $this->resultPageFactory = $resultPageFactory;
        $this->modelRepository = $modelRepository;
        return parent::__construct($context);
    }

    public function execute()
    {

        $this->modelRepository->deleteById(1);

        $this->resultFactory->create("raw");
    }
}

../Controller/Index/Del.php

<?php

namespace Custom\Module\Controller\Index;

use \Magento\Framework\Controller\Result\RawFactory;

class Del extends \Magento\Framework\App\Action\Action
{

    /**
     * Index resultPageFactory
     * @var PageFactory
     */
    private $resultPageFactory;
    /**
     * @var
     */
    private $modelRepository;

    /**
     * Index constructor.
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     * @param \Custom\Module\Model\ModelFactory $modelFactory
     * @param \Custom\Module\Model\ModelRepository $modelRepository
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory,
        \Custom\Module\Model\ModelFactory $modelFactory,
        \Custom\Module\Model\ModelRepository $modelRepository

) {
        $this->resultPageFactory = $resultPageFactory;
        $this->modelFactory = $modelFactory;
        $this->modelRepository = $modelRepository;
        return parent::__construct($context);
    }

    public function execute()
    {
        $obj = $this->modelFactory->create()->load(2);
         $this->modelRepository->delete($obj);

        $this->resultFactory->create("raw");
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.