كتابة رمز أقل مكررة باستخدام المجلدات في Laravel

صورة

وقت جيد ، سيداتي وسادتي.

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

خلاصة القول هي: يحتوي النظام على بنية API الداخلية لطلبات AJAX ، ويعود بشكل أساسي إلى مجموعة من شيء ما من قاعدة البيانات (الطلبات ، المستخدمون ، الحصص ، إلخ ...). بيت القصيد من هذا الهيكل هو إرجاع JSON بالنتائج ، لا أكثر. في مراجعة الكود ، قمت بحساب 5 أو 6 فصول باستخدام نفس الكود ، وكان الاختلاف الوحيد في حقن التبعية لـ ResourceCollection و JsonResource والنموذج نفسه. بدا هذا النهج خاطئًا جدًا بالنسبة لي ، وقررت إجراء التغييرات الصحيحة على هذه الشفرة ، كما أعتقد ، وذلك باستخدام أداة DI القوية التي يوفرها إطار Laravel.

لذا ، كيف أتيت إلى ما سأتحدث عنه لاحقًا.

لديّ بالفعل حوالي سنة ونصف من الخبرة التطويرية لـ Magento 2 ، وعندما واجهت هذا CMS لأول مرة ، شعرت بالصدمة تجاه DI. بالنسبة لأولئك الذين لا يعرفون: في Magento 2 ، لا يوجد جزء صغير من النظام مبني على ما يسمى "الأنواع الافتراضية". وهذا يعني ، في إشارة إلى فئة معينة ، نحن لا نتحول دائما إلى فئة "حقيقية". نحن نشير إلى نوع افتراضي تم "تجميعه" استنادًا إلى فئة "حقيقية" محددة (على سبيل المثال ، مجموعة لشبكة الإدارة ، تم تجميعها من خلال DI). وهذا يعني أنه يمكننا في الواقع تجميع أي فئة للاستخدام مع تبعياتنا بمجرد كتابة شيء مشابه في DI:

<virtualType name="Vendor\Module\Model\ResourceModel\MyData\Grid\Collection" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult"> <arguments> <argument name="mainTable" xsi:type="string">vendor_table</argument> <argument name="resourceModel" xsi:type="string">Vendor\Module\Model\ResourceModel\MyData </argument> </arguments> </virtualType> 

الآن ، عن طريق طلب فئة Vendor \ Module \ ResourceModel \ MyData \ Grid \ Collection ، نحصل على مثيل لـ Magento \ Framework \ View \ Element \ UiComponent \ DataProvider \ SearchResult ، ولكن مع تعيين تبعيات mainTable إلى "vendor_table" و resourceModel - " البائع \ الوحدة النمطية \ النموذج \ ResourceModel \ MyData. "

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

العودة إلى لارافيل.

بنيت DI Laravel على "حاوية الخدمة" - كيان يدير المجلدات والتبعيات في النظام. وبالتالي ، يمكننا ، على سبيل المثال ، الإشارة إلى واجهة DummyDataProviderInterface بتنفيذ هذه الواجهة DummyDataProvider نفسها.

 app()->bind(DummyDataProviderInterface::class, DummyDataProvider::class); 

ثم ، عندما نطلب DummyDataProviderInterface في حاوية الخدمة (على سبيل المثال ، من خلال مُنشئ الفصل الدراسي) ، نحصل على مثيل لفئة DummyDataProvider.

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

يمكن لارافيل "ربط" ليس فقط الكيانات الحقيقية ، مثل واجهة معينة ، ولكن أيضا إنشاء ما يسمى "أنواع افتراضية" (ويعرف أيضا باسم الأسماء المستعارة). وحتى في هذه الحالة ، لا يتعين على Laravel اجتياز فصل يقوم بتنفيذ نوعك. يمكن أن تأخذ طريقة bind () دالة مجهولة كوسيطة ثانية ، مع تمرير معامل التطبيق $ هناك - وهو مثيل لفئة التطبيق. بشكل عام ، نحن الآن ندخل أكثر ملاءمة للسياق ، حيث يعتمد ما ننقله إلى الفصل الذي ينفذ "النوع الظاهري" على الموقف الحالي.

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

لذلك ، لبداية سنقرر ما الذي سيكون بمثابة طبقة "حقيقية". باستخدام مثال المشروع الذي جاء إلي في مراجعة الكود ، دعنا نأخذ نفس الموقف مع طلبات الموارد (في الواقع ، CRUD ، ولكن خفضت بعض الشيء).

لنلقِ نظرة على تطبيق وحدة تحكم Crud الشائعة:

 <?php namespace Wolf\Http\Controllers\Backend\Crud; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; use Wolf\Http\Controllers\Controller; class BaseController extends Controller { /** * @var Model */ protected $model; /** * @var \Illuminate\Http\Resources\Json\ResourceCollection|null */ protected $resourceCollection; /** * @var \Illuminate\Http\Resources\Json\JsonResource|null */ protected $jsonResource; /** * BaseController constructor. * @param Model $model * @param \Illuminate\Http\Resources\Json\ResourceCollection|null $resourceCollection * @param \Illuminate\Http\Resources\Json\JsonResource|null $jsonResource */ public function __construct( $model, $resourceCollection = null, $jsonResource = null ) { $this->model = $model; $this->resourceCollection = $resourceCollection; $this->jsonResource = $jsonResource; } /** * Display a listing of the resource. * * @param Request $request * @return \Illuminate\Http\Resources\Json\ResourceCollection */ public function index(Request $request) { return $this->resourceCollection::make($this->model->get()); } /** * Display the specified resource. * * @param int $id * @return \Illuminate\Http\Resources\Json\JsonResource */ public function show($id) { return $this->jsonResource::make($this->model->find($id)); } } 

لم أكن مهتمًا بالتنفيذ ، لأن المشروع في مرحلة التخطيط ، في الواقع.

لدينا طريقتان يجب أن ترجعان شيئًا إلينا: الفهرس ، والذي يُرجع مجموعة من الكيانات من قاعدة البيانات ، ويعرض ، والذي يُرجع مورد json لكيان معين.

إذا استخدمنا فصولًا حقيقية ، فسنقوم في كل مرة بإنشاء فئة تحتوي على 1-2 أداة تحديد تحدد الفئات للنماذج والموارد والمجموعات. تخيل العشرات من الملفات ، منها تطبيق معقد حقًا هو 1-2 فقط. يمكننا تجنب مثل هذه "الحيوانات المستنسخة" باستخدام DI Laravel.

لذا ، فإن بنية هذا النظام ستكون بسيطة ، ولكن يمكن الاعتماد عليها كساعة سويسرية.
يوجد ملف json يحتوي على مجموعة من "الأنواع الافتراضية" مع الإشارة المباشرة إلى الفئات التي سيتم استخدامها كمجموعات ، نماذج ، موارد ، إلخ ...

على سبيل المثال ، هذا:

 { "Wolf\\Http\\Controllers\\Backend\\Crud\\OrdersResourceController": { "model": "Wolf\\Model\\Backend\\Order", "resourceCollection": "Wolf\\Http\\Resources\\OrdersCollection", "jsonResource": "Wolf\\Http\\Resources\\OrderResource" } } 

بعد ذلك ، باستخدام ربط Laravel ، سنقوم بتعيين Wolf \ Http \ Controllers \ Backend \ Crud \ BaseController كطبقتنا لنوعنا الافتراضي Wolf \ Http \ Controllers \ Backend \ Crud \ OrdersResourceController كفئة تنفيذية (لاحظ أن الفئة يجب ألا تكون مجردة ، لأنه عند طلب Wolf \ Http \ Controllers \ Backend \ Crud \ OrdersResourceController ، يجب أن نحصل على مثيل لـ Wolf \ Http \ Controllers \ Backend \ Crud \ BaseController ، وليس فئة مجردة).

في CrudServiceProvider ، في طريقة التمهيد () ، ضع التعليمات البرمجية التالية:

 $path = app_path('etc/crud.json'); if ($this->filesystem->isFile($path)) { $virtualTypes = json_decode($this->filesystem->get($path), true); foreach ($virtualTypes as $virtualType => $data) { $this->app->bind($virtualType, function ($app) use ($data) { /** @var Application $app */ $bindingData = [ 'model' => $app->make($data['model']), 'resourceCollection' => $data['resourceCollection'], 'jsonResource' => $data['jsonResource'] ]; return $app->makeWith(self::BASE_CRUD_CONTROLLER, $bindingData); }); } } 

يحتوي BASE_CRUD_CONTROLLER الثابت على اسم الفئة التي تنفذ منطق وحدة تحكم CRUD.

بعيدا عن المثالية ، لكنه يعمل :)

هنا نذهب من خلال مجموعة مع أنواع افتراضية وتعيين المجلدات. لاحظ أننا نحصل فقط على مثيل الطراز من حاوية الخدمة ، وأن ResourceCollection و JsonResource هما مجرد أسماء صنف. لماذا هذا لا يلزم أن يأخذ النموذج سمات يجب ملؤها ، بل يمكن أن يفعلها بدونها. ولكن يجب أن تأخذ المجموعات نوعًا من الموارد التي ستحصل منها على البيانات والكيانات. لذلك ، في BaseController ، نستخدم مجموعة الأساليب الثابتة () ونجعل () على التوالي (من حيث المبدأ ، يمكننا إضافة مجموعات ديناميكية من شأنها أن تضع شيئًا ما في المورد وتعيد إلينا مثيلًا ، لكنني سأترك هذا لك) سيعيدنا إلى مثيلات هذه نفس المجموعات ، ولكن مع البيانات المنقولة إليهم.

في الواقع ، يمكنك ، من حيث المبدأ ، إحضار كامل لارافيل ملزمة لمثل هذه الحالة.

إجمالاً ، عند الطلب من Wolf \ Http \ Controllers \ Backend \ Crud \ OrdersResourceController ، نحصل على مثيل لوحدة التحكم Wolf \ Http \ Controllers \ Backend \ Crud \ BaseController ، ولكن باستخدام تبعيات مضمّنة من نموذجنا ومواردنا وجمعنا. يبقى فقط لإنشاء ResourceCollection و JsonResource ويمكنك التحكم في البيانات التي تم إرجاعها.

Source: https://habr.com/ru/post/ar451462/


All Articles