أطر عمل PHP: الاختيار بين Symfony و Laravel

نشرت: 2022-03-11

اليوم ، عند بدء مشروع جديد ، فإن أحد القرارات الرئيسية هو اختيار الإطار الصحيح. أصبح من الصعب تخيل إنشاء تطبيق ويب معقد من البداية بدونه في الوقت الحاضر.

العديد من اللغات الشائعة لتطوير الويب لها إطار عمل "افتراضي" ، مثل Ruby on Rails for Ruby أو Django لـ Python. ومع ذلك ، لا تحتوي PHP على مثل هذا الإعداد الافتراضي الفردي ولديها العديد من الخيارات الشائعة للاختيار من بينها.

وفقًا لاتجاهات Google و GitHub ، فإن أكثر أطر عمل PHP شيوعًا هي Symfony مع 13.7 ألف نجمة و Laravel بـ 29 ألف نجمة (في وقت كتابة هذا المقال).

في هذه المقالة ، سأقارن هذين الإطارين وأوضح لك كيفية تنفيذ ميزات يومية بسيطة مع كل منهما. بهذه الطريقة ، يمكنك مقارنة رمز أمثلة الحياة الواقعية جنبًا إلى جنب.

تفترض هذه المقالة مهارات PHP قوية وفهمًا للنموذج المعماري MVC ، ولكن لا يلزم وجود خبرة سابقة مع Symfony أو Laravel.

المتنافسون

Laravel

عندما نتحدث عن Laravel ، فإننا نشير إلى الإصدار 4 من Laravel وما بعده. تم إصدار Laravel 4 في عام 2013 وكان يمثل إعادة كتابة كاملة للإطار. تم فصل وظيفة إطار العمل إلى مكونات منفصلة ، والتي تمت إدارتها باستخدام Composer ، بدلاً من أن يكون كل شيء في مستودع واحد ضخم للرمز.

يعلن Laravel عن نفسه كإطار عمل للتطور السريع ببنية بسيطة وجميلة يسهل تعلمها وقراءتها وصيانتها. إنه الإطار الأكثر شيوعًا في عام 2016. وفقًا لاتجاهات Google ، فهو أكثر شعبية بثلاث مرات من الأطر الأخرى ، وعلى GitHub ، لديه ضعف نجوم أكثر من المنافسين.

سيمفوني

تم إصدار Symfony 2 في عام 2011 ، ولكن يجب عدم الخلط بينه وبين Symfony 1 ، والذي كان إطارًا مختلفًا تمامًا مع مبادئ أساسية مختلفة. أنشأ Fabien Potencier Symfony 2 ، والإصدار الحالي هو 3.2 ، وهو إصدار تزايدي من Symfony 2. لذلك ، غالبًا ما يطلق عليهم ببساطة Symfony2 / 3.

مثل Laravel 4 ، تم تصميم Symfony 2 كمجموعة من المكونات المنفصلة. هناك فائدتان هنا: يمكننا استبدال أي مكون في مشروع Symfony ، ويمكننا أخذ واستخدام أي مكون Symfony في مشروع غير Symfony. يمكن أن تكون مكونات Symfony أمثلة رائعة على التعليمات البرمجية ويتم استخدامها في الكثير من المشاريع مفتوحة المصدر مثل Drupal و phpBB و Codeception. في الواقع ، يستخدم Laravel ما لا يقل عن 14 مكونًا من مكونات Symfony. يمنحك فهم Symfony العديد من الفوائد عند العمل مع مشاريع أخرى.

التركيبات الهيكلية

يأتي كلا الإطارين مع أدوات تثبيت وأغلفة متاحة عبر خادم الويب المدمج في PHP.

تثبيت Symfony

تثبيت Symfony بسيط كالتالي:

 # Downloading Symfony installer sudo curl -LsS https://symfony.com/installer -o /usr/local/bin/symfony # Granting permissions to execute installer sudo chmod a+x /usr/local/bin/symfony # Creating new Symfony project symfony new symfony_project # Launching built-in server cd symfony_project/ && php bin/console server:start

هذا هو! تثبيت Symfony الخاص بك متاح على URL http://localhost:8000 .

تثبيت Laravel

عملية تثبيت Laravel هي نفسها تقريبًا وبسيطة عملية تثبيت Symfony ؛ الاختلاف الوحيد هو أنك تثبِّت مُثبِّت Laravel من خلال Composer:

 # Downloading Laravel installer using Composer composer global require "laravel/installer" # Creating new Laravel project laravel new laravel_project # Launching built-in server cd laravel_project/ && php artisan serve

يمكنك الآن زيارة http://localhost:8000 والتحقق من تثبيت Laravel الخاص بك.

ملاحظة: يعمل كل من Laravel و Symfony على نفس منفذ المضيف المحلي (8000) افتراضيًا ، لذلك لا يمكنك تشغيل هذه المثيلات الافتراضية بشكل متزامن. لا تنس إيقاف خادم Symfony عن طريق تشغيل php bin/console server:stop قبل تشغيل خادم Laravel.

حول تثبيت الإطار

هذه أمثلة على التثبيت الأساسي. للحصول على أمثلة استخدام أكثر تقدمًا ، مثل القدرة على تكوين المشاريع مع المجالات المحلية أو تشغيل مشاريع متعددة في وقت واحد ، يوفر كلا الإطارين صناديق Vagrant:

  • منزل Laravel ،
  • سيمفوني هومستيد.

تكوينات الإطار الأساسية

تكوين Symfony الأساسي

يستخدم Symfony YAML باعتباره بناء الجملة لتحديد التكوين الخاص به. يوجد التكوين الافتراضي في ملف app/config/config.yml ، ويبدو مثل المثال التالي:

 imports: - { resource: parameters.yml } - { resource: security.yml } - { resource: services.yml } framework: secret: '%secret%' router: { resource: '%kernel.root_dir%/config/routing.yml' } # ... # Twig Configuration twig: debug: '%kernel.debug%' strict_variables: '%kernel.debug%' # ...

لإنشاء تهيئة خاصة بالبيئة ، قم بإنشاء ملف app/config/config_ENV.yml يحتوي على معلمات التكوين الأساسية. فيما يلي مثال على ملف config_dev.yml لبيئة التطوير:

 imports: - { resource: config.yml } # ... web_profiler: toolbar: true # ...

يقوم هذا المثال بتشغيل أداة web_profiler Symfony لبيئة التطوير فقط. تساعدك هذه الأداة في تصحيح أخطاء التطبيق الخاص بك وتوصيفه مباشرة في نافذة المتصفح.

في ملفات التكوين ، يمكنك أيضًا ملاحظة إنشاءات %secret% . إنها تسمح لنا بوضع المتغيرات الخاصة بالبيئة في ملف parameters.yml المنفصل. يمكن أن يكون هذا الملف فريدًا على كل جهاز ولا يتم تخزينه تحت التحكم في الإصدار. للتحكم في الإصدار ، لدينا ملف parameters.yml.dist خاص وهو قالب لملف parameters.yml .

فيما يلي مثال على ملف parameters.yml :

 parameters: database_host: 127.0.0.1 database_port: null database_name: symfony database_user: root database_password: null secret: f6b16aea89dc8e4bec811dea7c22d9f0e55593af

تكوين Laravel الأساسي

يبدو تكوين Laravel مختلفًا تمامًا عن إعدادات Symfony. الشيء الوحيد المشترك بينهما هو أنهما يستخدمان ملفات غير مخزنة تحت التحكم في الإصدار ( .env في حالة Laravel) وقالب لإنشاء هذا الملف ( .env.example ). يحتوي هذا الملف على قائمة بالمفاتيح والقيم ، مثل المثال التالي:

 APP_ENV=local APP_KEY=base64:Qm8mIaur5AygPDoOrU+IKecMLWgmcfOjKJItb7Im3Jk= APP_DEBUG=true APP_LOG_LEVEL=debug APP_URL=http://localhost

مثل ملف Symfony YAML ، فإن ملف Laravel هذا قابل للقراءة أيضًا ويبدو نظيفًا. يمكنك أيضًا إنشاء ملف .env.testing الذي سيتم استخدامه عند تشغيل اختبارات PHPUnit.

يتم تخزين تكوين التطبيق في ملفات .php في دليل config . يتم تخزين التكوين الأساسي في ملف app.php ويتم تخزين التكوين الخاص بالمكون في ملفات <component>.php (على سبيل المثال ، cache.php أو mail.php ). فيما يلي مثال على ملف config/app.php :

 <?php return [ 'name' => 'Laravel', 'env' => env('APP_ENV', 'production'), 'debug' => env('APP_DEBUG', false), 'url' => env('APP_URL', 'http://localhost'), 'timezone' => 'UTC', 'locale' => 'en', // ... ];

تكوين الإطار: Symfony مقابل Laravel

تتيح لك آليات تكوين تطبيقات Symfony إنشاء ملفات مختلفة لبيئات مختلفة. بالإضافة إلى ذلك ، يمنعك من إدخال منطق PHP معقد في تكوين YAML.

ومع ذلك ، قد تشعر براحة أكبر مع صيغة تكوين PHP الافتراضية التي يستخدمها Laravel وليس عليك تعلم بناء جملة YAML.

التوجيه والتحكم

بشكل عام ، يتحمل تطبيق الويب الخلفي مسؤولية أساسية واحدة: قراءة كل طلب وإنشاء استجابة اعتمادًا على محتوى الطلب. وحدة التحكم هي فئة مسؤولة عن تحويل الطلب إلى الاستجابة عن طريق استدعاء طرق التطبيق ، في حين أن جهاز التوجيه هو آلية تساعدك على اكتشاف فئة وحدة التحكم والطريقة التي يجب تنفيذها لطلب معين.

لنقم بإنشاء وحدة تحكم تعرض صفحة منشور مدونة مطلوبة من المسار /posts/{id} .

التوجيه والتحكم في Laravel

مراقب

 <?php namespace App\Http\Controllers; use App\Post; class BlogController extends Controller { /** * Show the blog post * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { return view('post', ['post' => Post::findOrFail($id)]); } }

جهاز التوجيه

 Route::get('/posts/{id}', 'BlogController@show');

لقد حددنا المسار لطلبات GET . ستعمل جميع الطلبات التي تحتوي على URI المطابق لـ /posts/{id} على تنفيذ show الطريقة لوحدة تحكم BlogController ، وستقوم بتمرير id المعلمة إلى هذه الطريقة. في وحدة التحكم ، نحاول العثور على كائن النموذج POST id الذي تم تمريره ، واستدعاء Laravel helper view() لتصيير الصفحة.

التوجيه والتحكم في Symfony

في Symfony ، يكون exampleController أكبر قليلاً:

 <?php namespace BlogBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; class PostController extends Controller { /** * @Route("/posts/{id}") * @param int $id * @return \Symfony\Component\HttpFoundation\Response */ public function indexAction($id) { $repository = $this->getDoctrine()->getRepository('BlogBundle:Post'); $post = $repository->find($id); if ($post === null) { throw $this->createNotFoundException(); } return $this->render('BlogBundle:Post:show.html.twig', ['post'=>$post]); } }

يمكنك أن ترى أننا قمنا بالفعل بتضمين @Route("/posts/{id}”) في التعليق التوضيحي ، لذلك نحتاج فقط إلى تضمين وحدة التحكم في ملف التكوين routing.yml :

 blog: resource: "@BlogBundle/Controller/" type: annotation prefix: /

المنطق التدريجي هو نفسه في حالة Laravel.

التوجيه والتحكم: Symfony مقابل Laravel

في هذه المرحلة ، قد تعتقد أن Laravel أجمل بكثير من Symfony. هذا صحيح في البداية. يبدو الطريق أفضل وأسهل للبدء. ومع ذلك ، في تطبيقات الحياة الواقعية ، لا يجب عليك استدعاء Doctrine من وحدة التحكم. بدلاً من ذلك ، يجب عليك الاتصال بخدمة تحاول العثور على المنشور أو طرح استثناء HTTP 404 .

القوالب

يأتي Laravel بمحرك نموذجي يسمى Blade ويشحن Symfony مع Twig. ينفذ كلا محركي القالب ميزتين رئيسيتين:

  1. وراثة القالب
  2. كتل أو أقسام

تتيح لك كلتا الميزتين تحديد قالب أساسي بأقسام قابلة للتجاوز وقوالب فرعية تملأ قيم هذه الأقسام.

دعنا نفكر في مثال صفحة منشور مدونة مرة أخرى.

محرك قالب Laravel Blade

 // base.blade.php <html> <head> <style></style> <title>@section('page-title') Welcome to blog! @show </title> </head> <body> <div class="container"> <h1>@yield('title')</h1> <div class="row"> @yield('content') </div> </div> </body> </html> // post.blade.php @extends('base') @section('page-title')Post {{ $post->title }} - read this and more in our blog.@endsection @section('title'){{ $post->title }}@endsection @section('content') {{ $post->content }} @endsection

الآن يمكنك إخبار Laravel في وحدة التحكم الخاصة بك لتقديم قالب post.blade.php . هل تتذكر وجهة نظرنا view('post', …) في مثال وحدة التحكم السابقة؟ لا تحتاج إلى أن تعرف في التعليمات البرمجية الخاصة بك أنها موروثة من قالب آخر. يتم تحديد كل ذلك فقط في القوالب الخاصة بك ، على مستوى العرض.

محرك قالب Symfony Twig

 // base.html.twig <html> <head> <style></style> <title>{% block page_title %} Welcome to blog! {% endblock %} </title> </head> <body> <div class="container"> <h1>{% block title %}{% endblock %}</h1> <div class="row"> {% block content %}{% endblock %} </div> </div> </body> </html> // show.html.twig {% extends '@Blog/base.html.twig' %} {% block page_title %}Post {{ post.title }} - read this and more in our blog.{% endblock %} {% block title %}{{ post.title }}{% endblock %} {% block content %} {{ post.content }} {% endblock %}

القوالب: Symfony مقابل Laravel

من الناحية الهيكلية ، تتشابه قوالب Blade و Twig تمامًا. يقوم كلاهما بإنشاء قوالب في كود PHP ويعملان بسرعة ، وكلاهما ينفذ هياكل التحكم ، مثل عبارات if والحلقات. الميزة الأكثر أهمية لكلا المحركين هي أنهما يهربان من الإخراج افتراضيًا ، مما يساعد على منع هجمات XSS.

بصرف النظر عن التركيب اللغوي ، فإن الاختلاف الرئيسي هو أن Blade يسمح لك بحقن كود PHP مباشرة في القوالب الخاصة بك ولا يفعل Twig. بدلاً من ذلك ، يتيح لك Twig استخدام المرشحات.

على سبيل المثال ، إذا كنت ترغب في كتابة سلسلة بأحرف كبيرة ، فستحدد في Blade ما يلي:

 {{ ucfirst('welcome friend') }}

في Twig ، من ناحية أخرى ، يمكنك القيام بما يلي:

 {{ 'welcome friend'|capitalize }}

في Blade ، من الأسهل توسيع بعض الوظائف ، لكن Twig لا يسمح بأي كود PHP مباشر في القوالب.

حقن التبعية

تحتوي التطبيقات على الكثير من الخدمات والمكونات المختلفة ، مع ترابطات مختلفة. تحتاج إلى تخزين جميع المعلومات حول الكائنات التي تم إنشاؤها وتبعياتها بطريقة أو بأخرى.

هنا يأتي المكون التالي - حاوية الخدمة. إنه كائن PHP يقوم بإنشاء الخدمات المطلوبة ويخزن المعلومات حول الكائنات التي تم إنشاؤها وتبعياتها.

لنفكر في المثال التالي: أنت تقوم بإنشاء فئة PostService لتنفيذ طريقة تكون مسؤولة عن إنشاء منشور مدونة جديد. تعتمد هذه الفئة على خدمتين أخريين: PostRepository ، المسؤولة عن تخزين المعلومات في قاعدة البيانات ، و SubscriberNotifier ، المسؤول عن إخطار المستخدمين المشتركين بالنشرة الجديدة. لجعلها تعمل ، تحتاج إلى تمرير هاتين الخدمتين كوسائط منشئ لـ PostService أو ، بعبارة أخرى ، تحتاج إلى حقن هذه التبعيات.

مثال حقن تبعية Symfony

أولاً ، دعنا نحدد خدماتنا النموذجية:

 <?php // src/BlogBundle/Repository/PostRepository.php namespace BlogBundle\Repository; use BlogBundle\Entity\Post; use Doctrine\ORM\EntityRepository; class PostRepository extends EntityRepository { public function persist(Post $post) { // Perform save to db } }
 <?php // src/BlogBundle/Service/SubscriberNotifier.php namespace BlogBundle\Service; use BlogBundle\Entity\Post; class SubscriberNotifier { public function notifyCreate(Post $post) { // Notify subscribers } }
 <?php // src/BlogBundle/Service/PostService namespace BlogBundle\Service; use BlogBundle\Entity\Post; use BlogBundle\Repository\PostRepository; class PostService { /** @var PostRepository */ private $repository; /** @var SubscriberNotifier */ private $notifier; function __construct(PostRepository $repository, SubscriberNotifier $notifier) { $this->repository = $repository; $this->notifier = $notifier; } public function create(Post $post) { $this->repository->persist($post); $this->notifier->notifyCreate($post); } }

التالي هو تكوين حقن التبعية:

 # src/BlogBundle/Resources/config/services.yml services: # Our main service blog.post_service: class: BlogBundle\Service\PostService arguments: ['@blog.post_repository', '@blog.subscriber_notifier'] # SubscriberNotifier service. It could also have its own dependencies, for example, mailer class. blog.subscriber_notifier: class: BlogBundle\Service\SubscriberNotifier # Repository. Don't dive deep into it's configuration, it is not a subject now blog.post_repository: class: BlogBundle\Repository\PostRepository factory: 'doctrine.orm.default_entity_manager:getRepository' arguments: - BlogBundle\Entity\Post

يمكنك الآن طلب خدمة البريد الخاصة بك في أي مكان في الرمز من كائن Service Container الخاص بك. على سبيل المثال ، في وحدة التحكم يمكن أن يكون شيء من هذا القبيل:

 // Controller file. $post variable defined below $this->get('blog.post_service')->create($post);

تعتبر Service Container مكونًا رائعًا ، وتساعد في بناء تطبيقك وفقًا لمبادئ تصميم SOLID.

الموضوعات ذات الصلة: حقن التبعية الحقيقي مع مكونات Symfony

مثال حقن تبعية Laravel

من الأسهل بكثير إدارة التبعيات في Laravel. لنفكر في نفس المثال:

 <?php // app/Repository/PostRepository.php namespace App\Repository; use App\Post; class PostRepository { public function persist(Post $post) { // Perform save to db } }
 <?php // app/Service/SubscriberNotifier.php namespace App\Service; use App\Post; class SubscriberNotifier { public function notifyCreate(Post $post) { // Notify subscribers } }
 <?php // app/Service/PostService.php namespace App\Service; use App\Post; use App\Repository\PostRepository; class PostService { /** @var PostRepository */ private $repository; /** @var SubscriberNotifier */ private $notifier; public function __construct(PostRepository $repository, SubscriberNotifier $notifier) { $this->repository = $repository; $this->notifier = $notifier; } public function create(Post $post) { $this->repository->persist($post); $this->notifier->notifyCreate($post); } }

هنا يأتي جمال Laravel - لست بحاجة إلى إنشاء تكوينات تبعية . يفحص Laravel تلقائيًا التبعيات لـ PostService في أنواع وسيطات الباني الخاصة به ويحلها تلقائيًا.

يمكنك أيضًا استخدام الحقن في تابع وحدة التحكم الخاصة بك لاستخدام PostService من خلال "نوع تلميح" في وسيطات الطريقة:

 <?php namespace App\Http\Controllers; use App\Post; use App\Service\PostService; class BlogController extends Controller { public function create(PostService $service) { $post = new Post(['title' => 'Title', 'content' => 'Content']); $service->create($post); return redirect('/posts/'.$post->id); } }

حقن التبعية: Symfony مقابل Laravel

يعمل الاكتشاف التلقائي في Laravel بشكل رائع. يحتوي Symfony على إمكانية مماثلة تسمى "autowire" والتي يتم إيقاف تشغيلها افتراضيًا ويمكن تشغيلها عن طريق إضافة autowire: true لتكوين التبعية الخاص بك ، لكنه يتطلب بعض التكوين. طريقة Laravel أبسط.

رسم الخرائط العلائقية للكائن (ORM)

للعمل مع قواعد البيانات ، يأتي كلا الإطارين مع ميزات رسم الخرائط الارتباطية (ORM). يقوم ORM بتعيين السجلات من قاعدة البيانات إلى الكائنات الموجودة في الكود. للقيام بذلك ، يجب عليك إنشاء نماذج لكل نوع سجل (أو كل جدول) في قاعدة البيانات الخاصة بك.

يستخدم Symfony مبدأ مشروع طرف ثالث للتفاعل مع قاعدة البيانات ، بينما يستخدم Laravel مكتبته الخاصة Eloquent.

تنفذ Eloquent ORM نمط ActiveRecord للعمل مع قاعدة البيانات. في هذا النمط ، يكون كل نموذج على دراية بالاتصال بقاعدة البيانات ويمكنه التفاعل معها. على سبيل المثال ، يمكنه حفظ البيانات في قاعدة البيانات أو تحديث سجل أو حذفه.

تطبق العقيدة نمط مخطط البيانات ، حيث لا تعرف النماذج شيئًا عن قاعدة البيانات ؛ هم فقط على دراية بالبيانات نفسها. طبقة خاصة منفصلة ، EntityManager ، تخزن جميع المعلومات حول التفاعل بين النماذج وقواعد البيانات ، وتتولى جميع العمليات.

لنأخذ مثالاً لفهم الاختلاف. لنفترض أن نموذجك يحتوي على مفتاح id أساسي وعنوان ومحتوى ومؤلف. يخزن جدول المنشورات id المؤلف فقط ، لذلك تحتاج إلى إنشاء علاقة بجدول المستخدمين .

عقيدة

لنبدأ بتحديد النماذج:

 <?php // src/BlogBundle/Entity/User.php namespace BlogBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * User * * @ORM\Table(name="user") * @ORM\Entity */ class User { /** * @var int * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @var string * * @ORM\Column(name="name", type="string", length=255) */ private $name; }
 <?php // src/BlogBundle/Entity/Post.php namespace BlogBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * Post * * @ORM\Table(name="post") * @ORM\Entity(repositoryClass="BlogBundle\Repository\PostRepository") */ class Post { /** * @var int * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @var string * * @ORM\Column(name="title", type="string", length=255) */ protected $title; /** * @var string * * @ORM\Column(name="content", type="text") */ protected $content; /** * @var User * * @ORM\ManyToOne(targetEntity="BlogBundle\Entity\User") * @ORM\JoinColumn(name="author_id", referencedColumnName="id") */ protected $author;

هنا ، أنشأنا معلومات تعيين النموذج ويمكننا الآن استخدام المساعد لإنشاء أبتر الطريقة:

 php bin/console doctrine:generate:entities BlogBundle

بعد ذلك ، نحدد طرق ما بعد المستودع:

 <?php // src/BlobBundle/Repository/PostRepository.php namespace BlogBundle\Repository; use BlogBundle\Entity\Post; use Doctrine\ORM\EntityRepository; class PostRepository extends EntityRepository { /** * Store post to database * * @param Post $post */ public function persist(Post $post) { $this->getEntityManager()->persist($post); $this->getEntityManager()->flush(); } /** * Search posts with given author's name * * @param string $name * @return array */ public function findByAuthorName($name) { return $this->createQueryBuilder('posts') ->select('posts') ->join('posts.author', 'author') ->where('author.name = :name') ->setParameter('name', $name) ->getQuery() ->getResult(); } }

يمكنك الآن استدعاء هذه الطرق من الخدمة أو ، على سبيل المثال ، من PostController :

 // To search for posts $posts = $this->getDoctrine()->getRepository('BlogBundle:Post')->findByAuthorName('Karim'); // To save new post in database $this->getDoctrine()->getRepository('BlogBundle:Post')->persist($post);

بليغ

يأتي نموذج المستخدم مع Laravel ويتم تعريفه افتراضيًا ، لذلك تحتاج فقط إلى تحديد نموذج واحد للمنشور .

 <?php // app/Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { public function author() { return $this->belongsTo('App\User', 'author_id'); } }

هذا كل شيء للنماذج. في Eloquent ، لا يتعين عليك تحديد خصائص النموذج ، لأنها تبنيها ديناميكيًا بناءً على هيكل جدول قاعدة البيانات. لتخزين منشور جديد $post في قاعدة البيانات ، تحتاج إلى إجراء هذه المكالمة (من وحدة التحكم ، على سبيل المثال):

 $post->save();

للعثور على جميع منشورات مؤلف يحمل اسمًا معينًا ، فإن أفضل طريقة هي العثور على مستخدم باسمه وطلب منشورات جميع المستخدمين:

 $posts = Post::whereHas('author', function ($q) { $q->where('name', 'Karim'); })->get();

ORM: Symfony مقابل Laravel

فيما يتعلق بـ ORM ، يبدو Eloquent أكثر ملاءمة لمطوري PHP وأسهل في التعلم من Doctrine.

مرسل الأحداث مقابل البرامج الوسيطة

Symfony مقابل دورة حياة Laravel

من أهم الأشياء التي يجب فهمها حول إطار العمل دورة حياته.

Symfony و Event Dispatcher

لتحويل طلب إلى استجابة ، يستخدم Symfony EventDispatcher. وبالتالي فإنه يطلق أحداث دورة حياة مختلفة ومستمعين للأحداث الخاصة للتعامل مع هذه الأحداث. في البداية ، يرسل الحدث kernel.request الذي يتضمن معلومات الطلب. المستمع الافتراضي الرئيسي لهذا الحدث هو RouterListener ، والذي يستدعي مكون جهاز التوجيه للعثور على قاعدة توجيه مناسبة للطلب الحالي. بعد ذلك ، يتم تنفيذ الأحداث الأخرى خطوة بخطوة. برامج الإنصات للأحداث النموذجية هي فحص الأمان والتحقق من رمز CSRF وعملية التسجيل. إذا كنت ترغب في إضافة بعض الوظائف في دورة حياة الطلب ، فأنت بحاجة إلى إنشاء EventListener مخصص والاشتراك فيه في الحدث الضروري.

Laravel والبرمجيات الوسيطة

يستخدم Laravel حلاً مختلفًا: الوسيطة. أحب مقارنة البرمجيات الوسيطة بالبصل: يحتوي التطبيق الخاص بك على طبقات معينة ويمر الطلب عبر هذه الطبقات في طريقه إلى وحدة التحكم والعودة. لذلك ، إذا كنت ترغب في تمديد منطق التطبيق الخاص بك وإضافة بعض الوظائف في دورة حياة الطلب ، فأنت بحاجة إلى إضافة طبقة إضافية إلى قائمة البرامج الوسيطة ، وسيقوم Laravel بتنفيذها.

REST API

دعنا نحاول إنشاء مثال CRUD أساسي لإدارة منشور مدونة:

  • إنشاء - POST /posts/
  • قراءة - GET /posts/{id}
  • تحديث - PATCH /posts/{id}
  • حذف - DELETE /posts/{id}

واجهة برمجة تطبيقات REST في Symfony

لا تمتلك Symfony حلًا سهلًا لإنشاء واجهة برمجة تطبيقات REST بسرعة ، ولكنها تحتوي على حزم رائعة من جهات خارجية FOSRestBundle و JMSSerializerBundle .

لنفكر في الحد الأدنى من مثال العمل مع FOSRestBundle و JMSSerializerBundle . بعد أن قمت بتثبيتها وتشغيلها في AppKernel ، يمكنك تعيين تكوين الحزمة الذي ستستخدمه بتنسيق JSON وأنه لا يجب تضمينه في طلبات URL:

 #app/config/config.yml fos_rest: routing_loader: default_format: json include_format: false

في تكوين التوجيه ، يجب أن تحدد أن وحدة التحكم هذه ستنفذ مورد REST:

 #app/config/routing.yml blog: resource: BlogBundle\Controller\PostController type: rest

قمت بتطبيق طريقة مستمرة في المستودع في المثال السابق ؛ أنت الآن بحاجة إلى إضافة طريقة حذف:

 // src/BlogBundle/Repository/PostRepository.php public function delete(Post $post) { $this->getEntityManager()->remove($post); $this->getEntityManager()->flush(); }

بعد ذلك ، تحتاج إلى إنشاء فئة نموذج لقبول طلبات الإدخال وتعيينها إلى النموذج. يمكنك القيام بذلك باستخدام مساعد CLI:

 php bin/console doctrine:generate:form BlogBundle:Post

سوف تتلقى نوع النموذج الذي تم إنشاؤه بالرمز التالي:

 <?php // src/BlogBundle/Form/PostType.php namespace BlogBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class PostType extends AbstractType { /** * {@inheritdoc} */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('title')->add('content'); } /** * {@inheritdoc} */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => 'BlogBundle\Entity\Post', 'csrf_protection' => false ]); } /** * {@inheritdoc} */ public function getBlockPrefix() { return 'post'; } }

الآن دعونا ننفذ وحدة التحكم الخاصة بنا.

ملاحظة: الكود الذي سأعرضه عليك ليس مثاليًا. إنه ينتهك بعض مبادئ التصميم ولكن يمكن إعادة بنائه بسهولة. الغرض الرئيسي هو توضيح كيفية تنفيذ كل طريقة خطوة بخطوة.

 <?php // src/BlogBundle/Controller/PostController.php namespace BlogBundle\Controller; use BlogBundle\Entity\Post; use BlogBundle\Form\PostType; use FOS\RestBundle\Controller\FOSRestController; use FOS\RestBundle\View\View; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; class PostController extends FOSRestController { /** * @param $id * @return Response */ public function getPostAction($id) { $view = new View(); $post = $this->getDoctrine()->getRepository('BlogBundle:Post')->find($id); if ($post === null) { $view->setStatusCode(Response::HTTP_NOT_FOUND); } else { $view->setData(['post' => $post]); } return $this->handleView($view); } /** * @param Request $request * @return Response */ public function postPostAction(Request $request) { $view = new View(null, Response::HTTP_BAD_REQUEST); $post = new Post; $form = $this->createForm(PostType::class, $post, ['method' => $request->getMethod()]); $form->handleRequest($request); if ($form->isValid()) { $this->getDoctrine()->getRepository('BlogBundle:Post')->persist($post); $view->setStatusCode(Response::HTTP_CREATED); $postUrl = $this->generateUrl('get_post', ['id' => $post->getId()], UrlGeneratorInterface::ABSOLUTE_URL); $view->setHeader('Location', $postUrl); } else { $view->setData($form->getErrors()); } return $this->handleView($view); } /** * @param $id * @param Request $request * @return Response */ public function patchPostAction($id, Request $request) { $view = new View(null, Response::HTTP_BAD_REQUEST); $post = $this->getDoctrine()->getRepository('BlogBundle:Post')->find($id); if ($post === null) { $view->setStatusCode(Response::HTTP_NOT_FOUND); } else { $form = $this->createForm(PostType::class, $post, ['method' => $request->getMethod()]); $form->handleRequest($request); if ($form->isValid()) { $this->getDoctrine()->getRepository('BlogBundle:Post')->persist($post); $view->setStatusCode(Response::HTTP_NO_CONTENT); $postUrl = $this->generateUrl('get_post', ['id' => $post->getId()], UrlGeneratorInterface::ABSOLUTE_URL); $view->setHeader('Location', $postUrl); } else { $view->setData($form->getErrors()); } } return $this->handleView($view); } /** * @param $id * @return Response */ public function deletePostAction($id) { $view = new View(null, Response::HTTP_NOT_FOUND); $post = $this->getDoctrine()->getRepository('BlogBundle:Post')->find($id); if ($post !== null) { $this->getDoctrine()->getRepository('BlogBundle:Post')->delete($post); $view->setStatusCode(Response::HTTP_NO_CONTENT); } return $this->handleView($view); } }

باستخدام FOSRestBundle ، لا تحتاج إلى إعلان مسار لكل طريقة ؛ ما عليك سوى اتباع اصطلاح أسماء طرق وحدة التحكم ، وسيقوم JMSSerializerBundle تلقائيًا بتحويل النماذج الخاصة بك إلى JSON.

REST API في Laravel

أولا ، تحتاج إلى تحديد المسارات. يمكنك القيام بذلك في قسم API لقواعد المسار لإيقاف تشغيل بعض مكونات البرامج الوسيطة الافتراضية وتشغيل المكونات الأخرى. يقع قسم API في ملف routes/api.php .

 <?php // routes/api.php Route::resource('/posts', 'BlogController');

في النموذج ، يجب تحديد خاصية $fillable لتمرير المتغيرات في طرق الإنشاء والتحديث الخاصة بالنموذج:

 <?php // app/Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { protected $fillable = ['title', 'content']; // …

الآن دعنا نحدد وحدة التحكم:

 <?php // app/Http/Controllers/BlogController.php namespace App\Http\Controllers; use App\Post; use Illuminate\Http\Request; use Illuminate\Http\Response; class BlogController extends Controller { public function show(Post $post) { return $post; } public function store(Request $request) { $post = Post::create($request->get('post')); return response(null, Response::HTTP_CREATED, ['Location'=>'/posts/'.$post->id]); } public function update(Post $post, Request $request) { $post->update($request->get('post')); return response(null, Response::HTTP_NO_CONTENT, ['Location'=>'/posts/'.$post->id]); } public function destroy(Post $post) { $post->delete(); return response(null, Response::HTTP_NO_CONTENT); } }

في Symfony ، أنت تستخدم FosRestBundle ، والتي تغلف الأخطاء في JSON. في Laravel ، عليك أن تفعل ذلك بنفسك. تحتاج إلى تحديث طريقة العرض في معالج الاستثناء لإرجاع أخطاء JSON لتوقع طلبات JSON:

 <?php // app/Exceptions/Handler.php namespace App\Exceptions; use Exception; use Illuminate\Auth\AuthenticationException; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; class Handler extends ExceptionHandler { /** * Render an exception into an HTTP response. * * @param \Illuminate\Http\Request $request * @param \Exception $exception * @return \Illuminate\Http\Response */ public function render($request, Exception $exception) { if ($request->expectsJson()) { $status = 400; if ($this->isHttpException($exception)) { $status = $exception->getStatusCode(); } elseif ($exception instanceof ModelNotFoundException) { $status = 404; } $response = ['message' => $exception->getMessage(), 'code' => $exception->getCode()]; return response()->json($response, $status); } return parent::render($request, $exception); } // ... }

واجهة برمجة تطبيقات REST: Symfony مقابل Laravel

كما ترى ، بالنسبة إلى واجهة برمجة تطبيقات REST النموذجية ، فإن Laravel أبسط بكثير من Symfony.

اختيار الفائز: Symfony أم Laravel؟

لا يوجد فائز واضح بين Laravel و Symfony ، لأن كل شيء يعتمد على هدفك النهائي.

يعد Laravel خيارًا أفضل إذا:

  • هذه هي تجربتك الأولى مع إطار العمل ، لأنه من السهل التعلم وله بنية أبسط ومواد تعليمية أفضل.
  • أنت تبني منتجًا لبدء التشغيل وتتحقق من فرضيتك ، لأنها جيدة لتطوير التطبيقات بسرعة ويسهل العثور على مطوري Laravel.

Symfony هو الخيار الأفضل إذا:

  • أنت تبني تطبيقًا معقدًا للمؤسسات ، حيث إنه قابل للتطوير للغاية ، ويمكن صيانته ، ومنظم جيدًا.
  • أنت تبني عملية ترحيل لمشروع كبير طويل الأجل ، حيث أن Symfony لديها خطط إصدار يمكن التنبؤ بها للسنوات الست المقبلة ، لذلك فمن غير المرجح أن تكون هناك أي مفاجآت.