سرفصل‌های آموزشی
آموزش معماری MVC
آشنایی با نحوهٔ افزودن ماژولی جدید به فریمورک

آشنایی با نحوهٔ افزودن ماژولی جدید به فریمورک

یکی از مزایای ماژولار بودن یک فریمورک آن است که به سادگی قادر خواهیم بود تا فیچرهایی که مرتبط با یکدیگر هستند را در قالب یک ماژول یا کامپوننت تعریف کرده به طوری که مستقل از سایر ماژول‌ها خواهند بود و این در حالی است می‌توانیم ماژول مربوطه را کپی کرده و در سایر پروژه‌ها مورد استفاده قرار داد.

همان‌طور که در طی این دورهٔ آموزشی دیدیم، فریمورکی که بر پایهٔ معماری سه‌لایهٔ MVC توسعه دادیم دارای یک ماژول اصلی به نام Base بود. حال در این آموزش قصد داریم ببینیم که به چه شکل می‌توان ماژولی جدید و مستقل از ماژول اصلی این فریمورک ساخت.

برای این منظور، داخل فولدر app فولدر جدیدی با نامی دلخواه همچون Api می‌سازیم که می‌تواند برای توسعهٔ یک وب سرویس مورد استفاده قرار گیرد (جهت آشنایی بیشتر با این اصطلاح، می‌توانید به آموزش API چیست؟ مراجعه نمایید.) از این پس، ساختار فولدر app به صورت زیر خواهد بود:

app
├── Api
├── Base
└── Core

با توجه به اینکه در توسعهٔ وب سرویس کلیهٔ تبادل اطلاعات از طریق پروتکل HTTP صورت می‌گیرد، مسلماً نیازی به لایهٔ ویو نخواهم داشت و از همین روی داخل فولدر Api اقدام به ساخت دو پوشه به منظور نگهداری کنترلرها و مدل‌ها می‌کنیم:

app
├── Api
│   ├── Controllers
│   └── Models
├── Base
└── Core

در ادامه و صرفاً جهت تست، یک کنترلر پیش‌فرض داخل پوشهٔ Controllers ساخته و آن را مورد بررسی قرار می‌دهیم. برای این منظور، کنترلری با نامی دلخواه همچون ApiController به صورت زیر می‌سازیم:

<?php
namespace Api\Controllers;

use Core\BaseController;

class ApiController extends BaseController
{
    public function articles()
    {
        echo "ApiController articles action";
    }
}

همان‌طور که ملاحظه می‌شود، ابتدا به ساکن و طبق روال گذشته نِیم‌اِسپیس این فایل را تعریف کرده‌ایم به طوری که پس از نوشتن کیورد namespace ابتدا نام پوشه‌ای که داخل آن قرار داریم یا به عبارتی Api را نوشته سپس نام پوشهٔ زیرشاخه یا همان Controllers را آورده‌ایم.

ثبت نِیم‌اِسپیس جدید از طریق کامپوزر

نکته‌ای که در اینجا حائز اهمیت است اینکه کِرنِل فریمورک در این مرحله از کار هرگز چنین نِیم‌اِسپیس را نخواهد شناخت چرا که همچون دو نِیم‌اِسپیس گذشته آن را در فایل composer.json ثبت نکرده‌ایم. برای این منظور، فایل composer.json را باز کرده و آن را به صورت زیر تکمیل می‌کنیم:

{
    "autoload": {
        "psr-4": {
            "Core\\": "app/Core",
            "Base\\": "app/Base",
            "Api\\": "app/Api"
        }
    }
}

همچون دو کامپوننت گذشته، نامی همچون Api برای نِیم‌اِسپیس جدید در نظر گرفته و مسیر آن را برابر با پوشهٔ app/Api قرار داده‌ایم.

    هشدار 
همواره به خاطر داشته باشیم که در این فایل پس از درج آخرین نِیم‌اِسپیس هرگز نباید علامت , گذاشت که در این صورت کامپوزر قابلیت اجرا نخواهد داشت.

کماکان به این نِیم‌اِسپیس دسترسی نخواهیم داشت چرا که کامند dump-autoload را اجرا نکرده‌ایم که برای این منظور دستور را زیرا در مسیر mvc اجرا می‌کنیم:

/var/www/mvc$ composer dump-autoload -o
Generated optimized autoload files containing 9 classes

اکنون چنانچه به فایل autoload_classmap.php رجوع کنیم، خواهیم داشت:

<?php

// autoload_classmap.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'Api\\Controllers\\ApiController' => $baseDir . '/app/Api/Controllers/ApiController.php',
    'Base\\Config\\Database' => $baseDir . '/app/Base/Config/Database.php',
    'Base\\Controllers\\DefaultController' => $baseDir . '/app/Base/Controllers/DefaultController.php',
    'Base\\Models\\User' => $baseDir . '/app/Base/Models/User.php',
    'Core\\App' => $baseDir . '/app/Core/App.php',
    'Core\\BaseController' => $baseDir . '/app/Core/BaseController.php',
    'Core\\Interfaces\\ControllerInterface' => $baseDir . '/app/Core/Interfaces/ControllerInterface.php',
    'Core\\Interfaces\\UserInterface' => $baseDir . '/app/Core/Interfaces/UserInterface.php',
    'Core\\Routing' => $baseDir . '/app/Core/Routing.php',
);

می‌بینیم که بر اساس حروف الفبا و درخط اول آرایهٔ فوق نِیم‌اِسپیس مرتبط با ماژول جدید Api افزوده شده است به طوری که از این پس به سادگی قادر خواهیم بود از کلاس‌های تعریف‌شده داخل این ماژول در سایر بخش‌ها استفاده کرده و کامپوزر هم به صورت خودکار آن‌ها را ایمپورت خواهد کرد.

در تفسیر کدهای فایل ApiController.php می‌توان گفت که پس از تعریف نِیم‌اِسپیس، اقدام به use کردن BaseController کرده‌ایم چرا که قرار است تا ApiController از آن ارث‌بری کند که برای این منظور و در حین ایجاد این کلاس، پس از درج نام کلاس کلیدواژهٔ extends را نوشته سپس کلاسی را آورده‌ایم که قصد داریم خصوصیات آن را در این کنترلر به ارث ببریم.

این کنترلر صرفاً حاوی یک متد یا اَکشن به نام ()articles است که به نوعی می‌تواند به منظور دریافت ریکوئست‌هایی مرتبط با نمایش لیست مقالات مورد استفاده قرار گیرد. داخل بدنهٔ این متد صرفاً یک متن ساده را چاپ کرده‌ایم که چنانچه یوآرال مربوطه با این کنترلر و اَکشن مپ شود، این متن نمایش داده خواهد شد.

در تکمیل این ماژول،‌ برای آنکه بتوانیم به این کنترلر و اَکشن دست پیدا کنیم، می‌باید فایل Routing.php را به صورت زیر به‌روزرسانی کنیم:

<?php
namespace Core;

class Routing
{
    public $routes = [
        [
            'route' => '',
            'module' => 'Base',
            'controller' => 'DefaultController',
            'action' => 'homepage',
        ],
        [
            'route' => 'default/homepage',
            'module' => 'Base',
            'controller' => 'DefaultController',
            'action' => 'homepage',
        ],
        [
            'route' => 'default/about',
            'module' => 'Base',
            'controller' => 'DefaultController',
            'action' => 'about',
        ],
        [
            'route' => 'default/users',
            'module' => 'Base',
            'controller' => 'DefaultController',
            'action' => 'users',
        ],
        [
            'route' => 'api/articles',
            'module' => 'Api',
            'controller' => 'ApiController',
            'action' => 'articles',
        ],
    ];

    public function __construct()
    {
        return $this->routes;
    }
}

همان‌طور که ملاحظه می‌شود، در پراپرتی routes$ اِلِمان جدیدی حاوی یکسری Key/Value به صورت زیر تعریف کرده‌ایم:

  • کلید route با مقدار api/articles که به منزلهٔ لینکی است که می‌توانیم با مراجعه به آن به این ماژول جدید دست پیدا کنیم.
  • کلید module که دربرگیرندهٔ نام ماژول جدید تحت عنوان Api است.
  • کلید controller که حاوی مقدار ApiController است.
  • کلید action که در آن نام تنها متد موجود در این کنترلر تحت عنوان articles را نوشته‌ایم. 

به منظور تست صحت کارکرد این ماژول جدید، لینک http://mvc.local/api/articles را در مرورگر وارد می‌کنیم و به طوری که خواهیم دید استرینگی که در بندهٔ تابع ()articles در نظر گرفته‌ بودیم چاپ شده است:

ApiController articles action

همچنین اگر لینکی همچون http://mvc.local/api/test را در نظر بگیریم، با خروجی زیر مواجه خواهیم شد:

404
The page you`re looking for is not found

می‌بینیم که سیستم روتینگ به درستی کار کرده و از آنجا که چنین اَکشنی داخل کنترلر این ماژول تعریف نشده است، با صفحهٔ 404 مواجه خواهیم شد. به همین منوال،‌ بسته به نیاز/نیازهای خود می‌توانیم کنترلرها و مدل‌های مختلفی به این ماژول اضافه نموده و آن‌ها را مورد استفاده قرار داد.

online-support-icon