يعطيك هذا الدليل الخطوات كاملة من البداية لبناء واجهة برمجة تطبيقات (API) في Laravel. نفّذ الأمر php artisan install:api، وأنشئ نموذجًا (Model) مع ملف ترحيل (Migration)، ثم أضف سطرًا واحدًا هو Route::apiResource() لتحصل على خمس نقاط نهاية (Endpoints). بعد ذلك تحقّق من المدخلات باستخدام طلب نموذج (Form Request)، ونسّق مخرجات JSON باستخدام مورد API (API Resource)، ثم احمِ كل شيء بالبرمجية الوسيطة (Middleware) auth:sanctum من Sanctum. أرسل طلبًا، وستصلك استجابة JSON مرتبة.
شاهد الفيديو (9 دقائق):
سنبني فيما يلي واجهة API حقيقية لتذاكر الدعم الفني باسم helpdesk-api، خطوة بخطوة في Laravel 13، مع كل أمر وكل ملف. ثم نحل الأخطاء الأربعة التي يقع فيها الجميع تقريبًا.
ما تحتاج إليه
- PHP 8.3 أو أحدث. يتطلب Laravel 13 (صدر في 17 مارس 2026) الإصدار PHP 8.3 أو أحدث.
- Composer ومثبّت Laravel (
composer global require laravel/installer). على جهاز جديد، يثبّت موقع php.new كلًّا من PHP وComposer والمثبّت بأمر واحد. - عميل API مثل Postman أو Insomnia أو Bruno، لإرسال الطلبات.
- معرفة أساسية بلغة PHP. لست بحاجة إلى معرفة Laravel مسبقًا.
ما زلت تستخدم Laravel 12؟ كل الخطوات التالية تعمل بالطريقة نفسها. لكن Laravel 12 يتلقى إصلاحات أمنية فقط حتى 24 فبراير 2027، لذلك ابدأ مشاريعك الجديدة على الإصدار 13.
الخطوة 1: أنشئ المشروع وفعّل مسارات API
laravel new helpdesk-api
يطرح المثبّت بعض الأسئلة. لمشروع API خالص، اختر None لحزمة البداية (Starter Kit)، وأبقِ SQLite قاعدةً للبيانات. لا تحتاج SQLite إلى أي إعداد: ينشئ Laravel الملف database/database.sqlite وينفّذ أول عمليات الترحيل نيابةً عنك.
وهنا النقطة التي يغفل عنها المبتدئون: تطبيق Laravel الجديد لا يحتوي على الملف routes/api.php. فعّل توجيه API بأمر واحد:
cd helpdesk-api
php artisan install:api
يقوم الأمر install:api بثلاثة أشياء:
- يثبّت Laravel Sanctum، وهو حارس الرموز (Token Guard) الذي سنستخدمه في الخطوة 5.
- ينشئ الملف
routes/api.php. - ينشر ملف الترحيل الخاص بجدول
personal_access_tokensويعرض عليك تنفيذه (وافق على ذلك).
كل مسار في routes/api.php عديم الحالة (Stateless)، أي لا يعتمد على الجلسات، ويحصل تلقائيًا على البادئة /api. لذلك يكون مسار tickets على العنوان /api/tickets. شغّل خادم التطوير:
php artisan serve
# INFO Server running on [http://127.0.0.1:8000].
الخطوة 2: أنشئ نموذج Ticket وملف الترحيل
php artisan make:model Ticket -m
يُنشئ الخيار -m ملف الترحيل في الخطوة نفسها. افتح الملف database/migrations/xxxx_create_tickets_table.php وأضف الأعمدة التي تحتاجها التذكرة:
Schema::create('tickets', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description');
$table->string('priority')->default('medium');
$table->string('status')->default('open');
$table->timestamps();
});
نفّذه:
php artisan migrate
ثم افتح app/Models/Ticket.php وأضف المصفوفة $fillable:
class Ticket extends Model
{
protected $fillable = [
'title', 'description', 'priority', 'status',
];
}
المصفوفة $fillable قائمة أمان. فهي تخبر Laravel بالحقول التي يُسمح للدالتين Ticket::create() وupdate() بتعبئتها من الطلب، فلا يستطيع أحد تمرير عمود لم تقصد قبوله أصلًا.
الخطوة 3: أضف المسارات ومتحكم API
يعطيك أمر Artisan واحد المتحكم (Controller) وطلبَي نموذج:
php artisan make:controller TicketController --api --model=Ticket --requests
- الخيار
--apiيتجاوز الدالتينcreateوedit(فواجهة API لا تحتوي على نماذج HTML). - الخيار
--model=Ticketيحدد نوع المعاملTicket $ticketفي الدوال. - الخيار
--requestsينشئStoreTicketRequestوUpdateTicketRequest.
أضف الآن سطرًا واحدًا إلى routes/api.php:
use App\Http\Controllers\TicketController;
use Illuminate\Support\Facades\Route;
Route::apiResource('tickets', TicketController::class);
تحقّق مما حصلت عليه:
php artisan route:list --path=api
| الطريقة | العنوان (URI) | الإجراء |
|---|---|---|
| GET | /api/tickets | index (عرض القائمة) |
| POST | /api/tickets | store (إنشاء) |
| GET | /api/tickets/{ticket} | show (تذكرة واحدة) |
| PUT/PATCH | /api/tickets/{ticket} | update (تعديل) |
| DELETE | /api/tickets/{ticket} | destroy (حذف) |
خمس نقاط نهاية من سطر واحد. تحصل الدوال show وupdate وdestroy على التذكرة تلقائيًا عبر ربط النموذج بالمسار (Route Model Binding): يتحول الرقم 7 في /api/tickets/7 إلى التذكرة رقم 7، أو إلى استجابة 404 إذا لم تكن موجودة.
الخطوة 4: تحقّق من المدخلات ونسّق مخرجات JSON
طلبات النماذج (Form Requests)
افتح الملف app/Http/Requests/StoreTicketRequest.php. يولّد Laravel الدالة authorize() وهي تُرجع false، مما يمنع كل طلب ويعيد الخطأ 403. اجعلها تُرجع true (أو ضع فيها تحققًا حقيقيًا من الصلاحيات)، ثم أضف قواعد التحقق:
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'description' => ['required', 'string'],
'priority' => ['sometimes', 'in:low,medium,high'],
];
}
في UpdateTicketRequest، أضف القاعدة sometimes حتى يتمكن العميل من تعديل حقل واحد فقط:
public function rules(): array
{
return [
'title' => ['sometimes', 'required', 'string', 'max:255'],
'description' => ['sometimes', 'required', 'string'],
'priority' => ['sometimes', 'in:low,medium,high'],
'status' => ['sometimes', 'in:open,closed'],
];
}
أرسل طلب POST /api/tickets فارغًا (مع الترويسة Accept: application/json)، وسيرد Laravel بالرمز 422 مع رسالة message وقائمة أخطاء errors، دون أن تكتب أي جملة if في شيفرتك:
{
"message": "The title field is required. (and 1 more error)",
"errors": {
"title": ["The title field is required."],
"description": ["The description field is required."]
}
}
موارد API (API Resources)
لا ترسل النموذج مباشرةً إلى الخارج أبدًا (سنشرح السبب لاحقًا). أنشئ موردًا:
php artisan make:resource TicketResource
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'description' => $this->description,
'priority' => $this->priority,
'status' => $this->status,
'opened_at' => $this->created_at->toDateTimeString(),
];
}
المتحكم في صورته النهائية
use App\Http\Requests\StoreTicketRequest;
use App\Http\Requests\UpdateTicketRequest;
use App\Http\Resources\TicketResource;
use App\Models\Ticket;
class TicketController extends Controller
{
public function index()
{
return TicketResource::collection(Ticket::latest()->paginate(10));
}
public function store(StoreTicketRequest $request)
{
$ticket = Ticket::create($request->validated());
return (new TicketResource($ticket))->response()->setStatusCode(201);
}
public function show(Ticket $ticket)
{
return new TicketResource($ticket);
}
public function update(UpdateTicketRequest $request, Ticket $ticket)
{
$ticket->update($request->validated());
return new TicketResource($ticket);
}
public function destroy(Ticket $ticket)
{
$ticket->delete();
return response()->noContent(); // 204
}
}
أنشئ تذكرة:
POST /api/tickets
{
"title": "VPN not connecting",
"description": "Abd Almouhimen Shamout can't reach the office VPN from home.",
"priority": "high"
}
ستحصل على الاستجابة 201 Created، والتذكرة داخل المفتاح data. أما الطلب GET /api/tickets فيُرجع القائمة داخل data، بالإضافة إلى links وmeta القادمين من أداة الترقيم (Paginator)، فتعرف واجهتك الأمامية المكتوبة بـ React أو Next.js عدد الصفحات الموجودة.
الخطوة 5: احمِ واجهة API باستخدام Sanctum
في الوضع الحالي يستطيع أي شخص حذف تذاكرك. أضف السمة (Trait) HasApiTokens إلى app/Models/User.php (يذكّرك الأمر install:api بذلك في مخرجاته، ومن السهل أن يفوتك):
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}
ضع المسارات داخل البرمجية الوسيطة auth:sanctum:
Route::middleware('auth:sanctum')->group(function () {
Route::apiResource('tickets', TicketController::class);
});
الآن يحصل أي طلب بلا رمز على الرد 401 {"message": "Unauthenticated."}. لإنشاء رمز للاختبار، افتح Tinker:
php artisan tinker
$user = User::factory()->create(['name' => 'Abd Almouhimen Shamout', 'email' => 'abd@mouhimen.dev']);
$user->createToken('postman')->plainTextToken;
// = "1|hN3kQ8vX2pLm9..."
انسخ الرمز فورًا. يخزّن Sanctum تجزئة SHA-256 للرمز فقط، فلن تتمكن من رؤية الرمز الأصلي مرة أخرى. في عميل API، اختر Authorization > Bearer Token، والصق الرمز، ثم أرسل الطلب: ستحصل على 200 OK. في التطبيق الحقيقي، يوزّع مسار تسجيل الدخول هذا الرمز، وهذا موضوع المقال التالي في هذه السلسلة.
نصيحة من عملي مع العملاء
لا أُرجع أبدًا نموذج Eloquent مباشرةً من المتحكم. فالجداول تكبر مع الوقت: يضيف أحدهم يومًا ما عمود internal_notes أو رقم هاتف أحد الفنيين، فيرسله النموذج الخام إلى كل العملاء في اليوم نفسه دون أن يلاحظ أحد. أما المورد فيرسل الحقول التي تحددها فقط. النماذج تبقى في الداخل، والموارد تخرج إلى الخارج. وعندما تبدأ تطبيقات خارجية باستخدام واجهتك، ضع رقم الإصدار في العنوان (/api/v1/tickets) حتى تتمكن من تغيير الأمور لاحقًا دون أن تعطّل تلك التطبيقات.
الأخطاء وحلولها
1. Route [login] not defined.
استدعيت مسارًا محميًا دون رمز، ولأن الطلب لم يطلب استجابة JSON، حاول Laravel تحويلك إلى صفحة تسجيل دخول غير موجودة في واجهة API. أرسل الترويسة Accept: application/json مع كل طلب، وستحصل بدلًا من ذلك على خطأ 401 صحيح بصيغة JSON. في React أو Next.js، اضبطها مرة واحدة:
fetch('/api/tickets', {
headers: { Accept: 'application/json', Authorization: `Bearer ${token}` },
});
ولفرض JSON على كل مسارات API من جهة الخادم أيضًا، أضف برمجية وسيطة صغيرة:
// app/Http/Middleware/ForceJson.php
public function handle(Request $request, Closure $next): Response
{
$request->headers->set('Accept', 'application/json');
return $next($request);
}
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->api(prepend: [\App\Http\Middleware\ForceJson::class]);
})
2. الخطأ 403 This action is unauthorized.
ما زالت الدالة authorize() في طلب النموذج تُرجع false. اجعلها تُرجع true، أو ضع فيها تحققك الحقيقي من الصلاحيات.
3. Add [title] to fillable property to allow mass assignment on [App\Models\Ticket].
لا يحتوي النموذج على هذا الحقل في $fillable، وهو ما يمنع الإسناد الجماعي (Mass Assignment)، أي تعبئة عدة حقول دفعة واحدة من الطلب. أضف الحقل، وستعمل create() من جديد.
4. الخطأ 404 Not Found على /api/tickets.
تحقّق من ثلاثة أمور: هل نفّذت php artisan install:api؟ هل يبدأ عنوانك بـ /api؟ هل يظهر المسار عند تنفيذ php artisan route:list --path=api؟ وإذا كنت قد خزّنت المسارات مؤقتًا (Cache)، فنفّذ php artisan route:clear.
قائمة التحقق
- نُفّذ الأمر
php artisan install:api، والملفroutes/api.phpموجود - النموذج يحتوي على
$fillable - سُجّل
Route::apiResource()، ويعرضroute:listخمسة مسارات - طلبات النماذج تُرجع
trueمنauthorize()وتحتوي على قواعد تحقق - المتحكم يُرجع موارد API، ولا يُرجع النماذج الخام أبدًا
- السمة
HasApiTokensمضافة إلىUser، والمسارات داخلauth:sanctum - كل عميل يرسل الترويسة
Accept: application/json
الأسئلة الشائعة
هل Laravel مناسب لبناء REST API؟
نعم. التوجيه، والتحقق من المدخلات، وموارد JSON، والترقيم، والمصادقة بالرموز كلها مدمجة فيه. لذلك يستطيع المبتدئ إطلاق واجهة CRUD آمنة في فترة ما بعد الظهر، وهي قابلة للتوسع لتطبيقات كبيرة.
ما الفرق بين Route::resource وRoute::apiResource؟
يسجّل apiResource المسارات نفسها باستثناء create وedit، وهما مخصصان لعرض نماذج HTML فقط. لواجهة API تحتاج إلى apiResource.
هل أحتاج إلى Sanctum أو Passport لواجهة API في Laravel؟
يكفي Sanctum لمعظم التطبيقات: رموز شخصية لتطبيقات الجوال والعملاء الخارجيين، ومصادقة بملفات تعريف الارتباط (Cookies) لتطبيق الصفحة الواحدة (SPA) الخاص بك. استخدم Passport فقط عندما تحتاج إلى خادم OAuth2 كامل.
لماذا تُرجع واجهة API في Laravel صفحات HTML بدلًا من JSON؟
لأن الطلب لا يرسل الترويسة Accept: application/json، فيرد Laravel كما لو كان الطلب من متصفح (تحويلات وصفحات أخطاء HTML). أضف الترويسة، أو البرمجية الوسيطة ForceJson المذكورة أعلاه.
هل يعمل هذا الدليل في Laravel 12؟
نعم. تعمل install:api وapiResource وطلبات النماذج والموارد وSanctum بالطريقة نفسها في Laravel 12 و13.
تابع التعلّم
- حدّثت WordPress وما زال الموقع مخترقًا؟ رقّع PHP الآن (PHP2Shell)
- كم يجب أن تتقاضى؟ تسعير أول مشروع ويب لك
- منطقة DNS واحدة وخمسة نطاقات فرعية: DNS داخلي وكتل خادم Nginx
- التالي في هذه السلسلة: مصادقة API باستخدام Laravel Sanctum (الرموز + SPA) (الاثنين 23 نوفمبر 2026)
شاهد الفيديو كاملًا:
المصادر
- توثيق Laravel 13، التوجيه (مسارات API،
install:api،route:list): https://laravel.com/docs/13.x/routing - توثيق Laravel 13، المتحكمات (
--api،--model،--requests،apiResource): https://laravel.com/docs/13.x/controllers - توثيق Laravel 13، التحقق من المدخلات (طلبات النماذج، أخطاء JSON برمز 422،
validated()): https://laravel.com/docs/13.x/validation - توثيق Laravel 13، موارد Eloquent API (
make:resource، التغليف داخلdata، الترقيم): https://laravel.com/docs/13.x/eloquent-resources - توثيق Laravel 13، Sanctum (
HasApiTokens،createToken، رموز Bearer): https://laravel.com/docs/13.x/sanctum - توثيق Laravel 13، التثبيت (
laravel new، SQLite افتراضيًا): https://laravel.com/docs/13.x/installation - Laravel News، إصدار Laravel 13 (الحد الأدنى PHP 8.3، تواريخ الدعم): https://laravel-news.com/laravel-13-released
هذا المقال ترجمة عربية للمقال الأصلي باللغة الإنجليزية للكاتب عبد المهيمن شموط على mouhimen.dev.
