تطوير Laravel
تطبيقات Laravel، نبنيها ونُبقيها تعمل.
من أول نموذج إلى عمّال الطوابير، ومن النشر إلى الترقية بعد سنوات — يكتبها ويستضيفها ويرعاها فريق واحد.
إصدار، كما يبدو على الخادم
كل تطبيق Laravel نشغّله يخرج بالطريقة نفسها: يُختبر ويُبنى ويُخزَّن مؤقتًا ويُفعَّل دون ثانية واحدة من التوقف.
01ci ~/app $ php artisan test PASS Tests\Feature\CheckoutTest ✓ a paid order queues the invoice ✓ a declined card never marks the order paid PASS Tests\Unit\VatTest ✓ vat is added once, on the total 02app ~/releases/1400 $ composer install --no-dev --optimize-autoloaderInstalling dependencies from lock fileGenerating optimized autoload files 03app ~/releases/1400 $ php artisan migrate --force INFO Running migrations. 2026_09_27_090000_add_due_date_to_invoices ....... DONE 04app ~/releases/1400 $ php artisan optimize INFO Caching framework bootstrap, configuration, and metadata. config ...................................... DONE events ...................................... DONE routes ...................................... DONE views ....................................... DONE 05app ~/releases/1400 $ ln -sfn ~/releases/1400 ~/current06app ~/releases/1400 $ php artisan horizon:terminate && php artisan octane:reload 07app ~/releases/1400 $ sudo supervisorctl statushorizon RUNNING pid 48211, uptime 0:00:09octane RUNNING pid 31877, uptime 6 days, 2:14:51 08app ~/releases/1400 $ php artisan schedule:list */5 * * * * php artisan horizon:snapshot ... Next Due: 3 minutes from now 0 2 * * * php artisan model:prune ........ Next Due: 9 hours from now 0 9 * * * php artisan invoices:remind .... Next Due: 16 hours from now
الاختبارات أولًا
تعمل الاختبارات قبل أن يصل أي شيء إلى الخادم. واختبار واحد فاشل يوقف الإصدار.
الاعتماديات
تُثبَّت من ملف القفل فقط، دون حزم التطوير، ومع محمِّل تلقائي مُحسَّن للأصناف.
الترحيلات
تُكتب بحيث تظل الشيفرة العاملة صالحة مع المخطط الجديد خلال انتقال الإصدار.
التخزين المؤقت
تُخزَّن الإعدادات والأحداث والمسارات وقوالب العرض مؤقتًا. ومن هنا فصاعدًا لا يُقرأ ملف البيئة إطلاقًا، ولهذا يقتصر مكان env() على ملفات الإعدادات.
التبديل
يُحوَّل رابط واحد فيدخل الإصدار الجديد الخدمة. ويبقى الإصدار السابق على القرص للرجوع إليه.
العمّال
يُنهي Horizon المهام التي بين يديه ثم يُعاد تشغيله على الشيفرة الجديدة، ويعيد Octane تحميل عمّاله بالطريقة نفسها.
تحت الإشراف
يُبقي Supervisor كلًّا من Horizon وOctane قيد التشغيل، ويعيد أيًّا منهما إذا توقف.
المُجدوِل
سطر cron واحد يشغّل كل مهمة مجدولة. وهذا ما سيشغّله تاليًا.
من نقرة واحدة إلى مهمة مكتملة
يخرج العمل البطيء من الطلب. يحصل العميل على ردّ فوري، ويتولى عامل الباقي في الخلفية.
-
01 · الطلب
POST /checkoutالعميل يدفع
يتحقق المتحكّم من الطلب ويحفظه ويردّ فورًا. لا يجري هنا أي شيء بطيء.
-
02 · المهمة
PayOrder::dispatch()الجزء البطيء يصبح مهمة
تُرسَل بعد أن تُثبِّت قاعدة البيانات المعاملة، فلا يلتقط أي عامل طلبًا جرى التراجع عنه.
-
03 · الطابور
redis · paymentsتنتظر في طابورها الخاص
للمدفوعات طابورها الخاص، فلا يؤخرها تصدير طويل أبدًا.
-
04 · العامل
horizon · supervisorيتسلّمها عامل
مع مهلة زمنية، وإعادة محاولات يطول انتظارها في كل مرة، وحدّ لعدد ما يعمل منها في وقت واحد.
-
05 · الإشعار
OrderPaid → mail, databaseيُبلَّغ الجميع
يتلقى العميل الفاتورة بالبريد الإلكتروني، ويتحول الطلب إلى «مدفوع» في لوحة التحكم لديك.
failed_jobs
المهمة التي تستنفد محاولاتها تُحفظ مع خطئها، ويصلنا تنبيه. ويُعاد تشغيلها بعد إصلاح السبب — ولا تضيع في صمت أبدًا.
التطبيق كله، لا الخادم وحده
يحمل فريقنا خبرة خمسة وعشرين عامًا في PHP إلى كل مشروع Laravel، من أول نموذج إلى الترقية بعد سنوات.
نبني
التطبيق نفسه.
- البنية
- منطق الأعمال في أصناف الإجراءات والخدمات، لا في المتحكّمات. طلبات نماذج مخصّصة للتحقق من المدخلات، وسياسات تحدد من يحق له فعل ماذا.
- Eloquent
- تُحمَّل العلاقات مسبقًا حيث تعرضها الصفحة في قائمة، فتكلّف الصفحة بضعة استعلامات لا المئات. وفي بيئة التطوير يُطلق التحميل الكسول خطأً، فتُكتشف المشكلة مبكرًا.
- Livewire · Inertia
- Livewire حين يكتب الفريق بـ PHP وBlade؛ وInertia مع Vue أو React حين تكون الواجهة الأمامية مشروعًا قائمًا بذاته.
- واجهات API مع Sanctum
- رموز وصول لتطبيقات الجوال والشركاء، وجلسات عبر ملفات تعريف الارتباط لواجهتك الأمامية، وحدود لمعدّل الطلبات لكل عميل.
- Pest · PHPUnit
- اختبارات لكل مسار يتقاضى مالًا أو يرسل بريدًا، تعمل مع كل رفع للشيفرة قبل بناء أي إصدار.
نشغّل
ما يُبقيه مستجيبًا.
- الطوابير وHorizon
- طوابير منفصلة للأعمال العاجلة والأعمال المجمّعة، لكلٍّ منها عمّاله ومُهَله وإعادات محاولته، تحت المراقبة في لوحة Horizon.
- المُجدوِل
- سطر cron واحد يشغّل كل المهام. والمهام التي يجب ألا تتداخل، أو يجب أن تعمل على خادم واحد فقط، تُعلَّم بذلك.
- Octane
- يُبقي Swoole أو RoadRunner أو FrankenPHP التطبيق في الذاكرة بين الطلبات. وقبل أن نفعّله، نفحص الشيفرة بحثًا عن أي حالة تتسرّب من طلب إلى الطلب الذي يليه.
- Redis
- التخزين المؤقت والجلسات والطوابير والأقفال، على Redis لا يمكن الوصول إليه من الإنترنت.
- Supervisor
- يعمل عمّال الطوابير وHorizon وOctane كعمليات تحت الإشراف، يُعاد تشغيلها إذا توقفت وبعد كل إصدار.
نصون
ما يُبقيه محدَّثًا.
- نشر دون توقف
- يُبنى كل إصدار في مجلده الخاص ويُفعَّل بتحويل رابط واحد. والعودة هي الخطوة نفسها بالعكس.
- السجلات والاستثناءات
- ملفات سجلات يومية تُحفظ لمدة محددة، وكل استثناء يُبلَّغ إلينا مع الطلب الذي تسبب فيه.
- الترقيات
- يُصدر Laravel إصدارًا رئيسيًا كل عام. وننقل التطبيق إصدارًا واحدًا في كل مرة، مع نجاح الاختبارات في كل خطوة.
ما نُعِدّه من اليوم الأول
قبل أن يصل أول عميل، يكون كل هذا جاهزًا على كل خادم Laravel نشغّله.
- وضع التصحيح مُعطَّل في بيئة الإنتاج، ولا يُتاح للويب إلا المجلد العام
APP_DEBUG=false - عمّال الطوابير تحت Supervisor، ويُعاد تشغيلهم مع كل إصدار
supervisord - سطر cron واحد للمُجدوِل
* * * * * php artisan schedule:run - Redis للتخزين المؤقت والجلسات والطوابير
CACHE_STORE=redis - الإعدادات والأحداث والمسارات وقوالب العرض مخزّنة مؤقتًا مع كل إصدار
php artisan optimize - OPcache مُفعَّل، ويُعاد ضبطه مع كل إصدار
opcache.validate_timestamps=0 - مجموعة PHP-FPM بحجم يناسب ذاكرة الخادم
pm.max_children - الإصدارات في مجلداتها الخاصة، على بُعد خطوة واحدة من الرجوع
current → releases/… - المهام الفاشلة تُبلَّغ إلينا، ولا تُترك في جدول
failed_jobs - تدوير السجلات وتنبيهات الاستثناءات
LOG_CHANNEL=daily - نسخة احتياطية ليلية لقاعدة البيانات، تُنسخ خارج الخادم
backup · offsite - شهادة TLS تتجدد تلقائيًا
Let's Encrypt
إصدار على القرص
/var/www/example-app
├── current → releases/1400
├── releases/
│ ├── 1400
│ └── 0930
└── shared/
├── .env
└── storage/

أخبرنا عن تطبيقك
تطبيق جديد، أو تطبيق يحتاج إلى استضافة، أو تطبيق عالق على إصدار قديم. يُسعَّر كل مشروع بعد محادثة قصيرة.