وقت جيد ، سيداتي وسادتي.
منذ وقت ليس ببعيد واجهت ظاهرة الكود المكرر والمكرر عند مراجعة مشروع واحد في 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 { protected $model; protected $resourceCollection; protected $jsonResource; public function __construct( $model, $resourceCollection = null, $jsonResource = null ) { $this->model = $model; $this->resourceCollection = $resourceCollection; $this->jsonResource = $jsonResource; } public function index(Request $request) { return $this->resourceCollection::make($this->model->get()); } 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) { $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 ويمكنك التحكم في البيانات التي تم إرجاعها.