REST APIFull CRUD.
هنبني REST API كامل لنفس الـ entity ("Ticket") — Create, Read, Update, Delete — خطوة بخطوة بالشرح. الجميل إن Magento بيبني الـ REST أوتوماتيك فوق الـ service contracts، فالشغل أقل مما تتخيّل.
Magento 2 بيبني الـ REST API فوق الـ service contracts: بتعرّف Data Interface و Repository Interface، وبتربط كل URL و HTTP method بـ method في الـ repository جوّه etc/webapi.xml، مع صلاحية معرّفة في acl.xml. وMagento بيتولّى الـ routing وتحويل الـ JSON للـ objects والعكس، وبتختبر الـ endpoints بـ access token.
نظرة عامة والملفات
الصورة الكاملة قبل الكود: إزاي REST بيشتغل في Magento وأنهي ملفات محتاجينها.
REST في Magento مبني على مبدأ: "عرّف العقد، والباقي أوتوماتيك". بتكتب interface للـ repository، وبتقول في webapi.xml "الـ URL ده بينادي الـ method دي" — و Magento يعمل كل حاجة.
في GraphQL بتكتب resolver لكل عملية. في REST، لو الـ repository جاهز، بتكتب XML بس — مفيش كود resolver. Magento بيعمل الـ serialization من والـ Data interface أوتوماتيك.
Data Interface
بيوصّف شكل بيانات الـ Ticket. منه Magento بيعرف يعمل serialization للـ JSON.
يعرّف الـ getters والـ setters لكل حقل. Magento بيقرا الـ return types عشان يبني الـ JSON response بالأنواع الصح.
interface TicketInterface { const ENTITY_ID = 'entity_id'; const TITLE = 'title'; const STATUS = 'status'; public function getId(); public function setId($id); public function getTitle(): ?string; public function setTitle(string $title): TicketInterface; public function getStatus(): ?string; public function setStatus(string $status): TicketInterface; }
Repository Interface
قلب الـ REST API. كل عملية REST بتربط بـ method هنا. لازم تكون typed بدقة.
يعرّف العمليات: save (بتخدم create + update)، getById، getList، وdelete. كل واحدة هتتربط بـ endpoint.
interface TicketRepositoryInterface { public function save( TicketInterface $ticket ): TicketInterface; public function getById( int $id ): TicketInterface; public function getList( SearchCriteriaInterface $criteria ): TicketSearchResultsInterface; public function delete( TicketInterface $ticket ): bool; public function deleteById( int $id ): bool; }
webapi.xml — الـ routes
الملف السحري. هنا بتربط كل URL + HTTP verb بـ method في الـ repository. ده أهم ملف.
كل <route> فيه: الـ url والـ method (الـ HTTP verb)، الـ <service> (الـ interface و method)، والـ <resources> (صلاحية الوصول).
<routes> <!-- READ one --> <route url="/V1/tickets/:id" method="GET"> <service class="...\TicketRepositoryInterface" method="getById"/> <resources> <resource ref="Vendor_Module::view"/> </resources> </route> <!-- READ list --> <route url="/V1/tickets" method="GET"> <service class="...\TicketRepositoryInterface" method="getList"/> <resources> <resource ref="Vendor_Module::view"/> </resources> </route> <!-- CREATE --> <route url="/V1/tickets" method="POST"> <service class="...\TicketRepositoryInterface" method="save"/> <resources> <resource ref="Vendor_Module::manage"/> </resources> </route> <!-- UPDATE --> <route url="/V1/tickets/:id" method="PUT"> <service class="...\TicketRepositoryInterface" method="save"/> <resources> <resource ref="Vendor_Module::manage"/> </resources> </route> <!-- DELETE --> <route url="/V1/tickets/:id" method="DELETE"> <service class="...\TicketRepositoryInterface" method="deleteById"/> <resources> <resource ref="Vendor_Module::manage"/> </resources> </route> </routes>
الـ resources اللي استخدمناها فوق (view وmanage) لازم تكون معرّفة في الـ acl.xml.
<resources> <resource id="Magento_Backend::admin"> <resource id="Vendor_Module::main" title="Tickets"> <resource id="Vendor_Module::view" title="View Tickets"/> <resource id="Vendor_Module::manage" title="Manage Tickets"/> </resource> </resource> </resources>
Read — GET
القراءة جاهزة بمجرد ما عرّفنا الـ routes. مفيش كود إضافي — الـ repository موجود.
الـ route بتاع الـ GET مربوط بـ getById وgetList اللي في الـ repository. Magento بيستدعيهم ويحوّل النتيجة لـ JSON. أنت بس بتنادي الـ endpoint.
{
"entity_id": 5,
"title": "Login issue",
"status": "open"
}
الـ getList بيدعم فلاتر متقدمة عن طريق الـ searchCriteria في الـ query string — Magento بيبنيها أوتوماتيك.
Create — POST
إنشاء ticket. بتبعت الـ data في الـ body، وMagento بيحوّلها لـ object ويناديها بالـ save.
بتبعت JSON فيه مفتاح ticket (باسم الـ parameter في method الـ save). Magento بيحوّله لـ TicketInterface object ويمرّره للـ save أوتوماتيك.
{
"ticket": {
"title": "Payment failed",
"status": "open"
}
}
{
"entity_id": 6,
"title": "Payment failed",
"status": "open"
}
Update — PUT
التعديل بيستخدم نفس method الـ save، بس مع id في الـ URL.
الـ save بتعمل الاتنين: لو الـ object مالوش id بتعمل create، ولو ليه id بتعمل update. في الـ PUT، الـ id بييجي من الـ URL فبتعرف تحدّث الموجود.
{
"ticket": {
"status": "closed"
}
}
Delete — DELETE
الأبسط. الـ id في الـ URL، ومربوط بـ deleteById.
الـ route مربوط بـ deleteById اللي بياخد int $id. الـ id بييجي من الـ URL مباشرة. بيرجّع true لو نجح.
true
الاختبار والأخطاء
التفعيل، الـ authentication، وأشهر المشاكل.
أغلب الـ endpoints محتاجة token. أول حاجة بتجيب token، وبعدين بتبعته في كل request.
POST /rest/V1/integration/admin/token # body: { "username": "admin", "password": "..." } # returns a token string, then send it: Authorization: Bearer <token>
php bin/magento setup:upgrade php bin/magento cache:clean php bin/magento setup:di:compile
- جرّب بـ Postman أو curl أو Insomnia.
- Magento بيولّد Swagger docs أوتوماتيك على /swagger — بتلاقي فيه كل الـ endpoints.
- لأي عملية كتابة، متنساش الـ Authorization header.
- 404 على الـ endpoint: نسيت setup:upgrade أو الـ URL في webapi.xml غلط.
- 401 Unauthorized: مبعتش token، أو الـ token انتهى.
- 403 Forbidden: الـ token مالوش الصلاحية المطلوبة في الـ <resources>.
- الـ POST بيرجّع null أو error: اسم المفتاح في الـ JSON مش مطابق لاسم الـ parameter في الـ method.
- الـ serialization غلط: الـ Data interface types ناقصة أو غلط.
REST API CRUD كامل ✓
دلوقتي عندك REST API كامل: Read, Create, Update, Delete — كله مبني على service contracts، وأغلبه مجرد config في webapi.xml بدون كود resolver.
الخلاصة الذهبية: repository قوي = REST + GraphQL شبه مجانيين. اصرف مجهودك في الـ service contract، والـ APIs بتتبني فوقه بسهولة.
خلّصت الدرس؟علّمه عشان تتابع تقدّمك في الكورس.
أسئلة شائعة
إزاي أعمل REST API لـ custom entity في Magento 2؟
بتعرّف service contract الأول: Data interface لشكل البيانات وRepository interface فيه save وgetById وgetList وdeleteById. بعدها بتربط كل URL وHTTP verb بـ method في الـ repository جوّه etc/webapi.xml، وبتعرّف الصلاحيات في acl.xml. Magento بيتولّى الـ routing وتحويل الـ JSON والـ authorization، فمش محتاج تكتب controllers.
إيه وظيفة ملف webapi.xml في Magento 2؟
ده الملف اللي بيربط كل endpoint بالكود: كل route فيه الـ url والـ HTTP method، وعنصر service بيحدد الـ interface والـ method اللي هتتنادى، وعنصر resources بيحدد الصلاحية المطلوبة. ولو الـ repository جاهز، الـ REST API كله تقريبًا بيبقى config في الملف ده من غير كود resolver.
إزاي أعمل Create و Update لنفس الـ entity في Magento 2 REST API؟
الاتنين بيتربطوا بنفس method الـ save: الـ POST على /V1/tickets بيعمل create، والـ PUT على /V1/tickets/:id بيعمل update لأن الـ id جاي من الـ URL. البيانات بتتبعت في الـ body تحت مفتاح اسمه نفس اسم الـ parameter، زي ticket لـ save(TicketInterface $ticket). وفي الـ update الأحسن الـ repository يجيب الـ record الموجود بالـ id ويعدّل عليه قبل الحفظ عشان متمسحش الحقول اللي مبعتتهاش.
ليه أعمل deleteById في الـ repository مع إن فيه delete؟
الـ delete بياخد object كامل وده مفيد جوّه الكود، لكن الـ DELETE endpoint بيبعت الـ id في الـ URL بس. عشان كده بتربط الـ route بـ deleteById(int $id)، والـ :id في الـ URL بيتطابق مع الـ parameter اللي اسمه $id، وبيرجّع true لو الحذف نجح.
يعني إيه resource ref="anonymous" في webapi.xml؟
لما تحط ref="anonymous" بدل ACL resource، الـ endpoint بيبقى public وأي حد يقدر يوصله من غير token. استخدمه بحذر شديد وللبيانات العامة بس، وأي عملية كتابة اربطها بصلاحية معرّفة في acl.xml.
إزاي أعمل filter و pagination لـ getList في Magento 2 REST API؟
الـ GET list endpoint بيقبل searchCriteria في الـ query string، زي searchCriteria[filter_groups][0][filters][0][field]=status. شكله معقّد شوية بس بيدعم الـ filters والـ sorting والـ pagination كلها من الـ URL، وMagento بيبني الـ SearchCriteria أوتوماتيك من غير أي كود إضافي منك.
ما الفرق بين REST و GraphQL في Magento 2؟
الـ REST بيتعرّف بالكامل في webapi.xml من غير كود resolver، وكل endpoint بيرجّع structure ثابت. الـ GraphQL محتاج resolver لكل عملية، بس بيدّي الـ client حرية يطلب الحقول اللي عايزها بالظبط في request واحد. والاتنين بيعتمدوا على نفس الـ repository، فالـ service contract القوي بيخلّي الاتنين سهلين.