Laravel 多模塊開發:caffeinated/modules

Laravel Application 的默認目錄結構以下 php

app/
├─Console
│  └─Commands
├─Events
├─Exceptions
├─Http
│  ├─Controllers
│  │  └─Auth
│  ├─Middleware
│  └─Requests
├─Jobs
├─Listeners
├─Models
├─Policies
└─Providers

雖然咱們能夠利用 psr-4 標準的自動載入靈活的使用命名空間對 controller/model 分組來簡單的模擬實現「模塊」,但這種方式嚴格上講並非標準的模塊,尤爲是在版本迭代較快的 Api 開發中,我比較喜歡將一個版本的 Api 做爲一個獨立的模塊去管理。html

一個模塊應該有本身獨立的一套 MVC,laravel 自帶的約定結構並不能實現,因此咱們須要藉助第三方包來完成這項功能。laravel

caffeinated/modules

caffeinated/modules 是 laravel5 實現多模塊開發擴展包,安裝完成後可使用 artisan 命令建立一個獨立的 mvc 模塊,方便咱們快速的構建 laravel 的多模塊應用。web

packagist 的地址:https://packagist.org/packages/caffeinated/modulesapi

因爲我用的 wamp 集成環境 php 仍是 5.5 的版本,因此 composer 給我裝的 laravel ^5.2,因此 composer 會默認給我安裝 caffeinated/modules ^3.2.* 版本,後續的版本須要 larvael ^5.3 了,這裏就不糾結這問題了。bash

安裝依賴

#在項目目錄執行依賴安裝命令
composer require caffeinated/modules

執行成功後擴展包就安裝到咱們當前項目了服務器

註冊服務器提供者和門面

# file: config/app.config

# 在 providers 配置項中添加註冊服務提供者
Caffeinated\Modules\ModulesServiceProvider::class

# 在 alias 配置項中添加註冊門面
'Module' => Caffeinated\Modules\Facades\Module::class

到這裏就安裝完成了,下面咱們能夠構建本身的模塊了。閉包

生成模塊

好比咱們想建立一個 Api 模塊:php artisan make:module Apimvc

php artisan make:module Api
*-----------------------------------------------*
|                                               |
|              Copyright (c) 2016               |
|                  Shea Lewis                   |
|                                               |
|         Thanks for using Caffeinated!         |
*-----------------------------------------------*
______  ___     _________      ______
___   |/  /___________  /___  ____  /____________
__  /|_/ /_  __ \  __  /_  / / /_  /_  _ \_  ___/
_  /  / / / /_/ / /_/ / / /_/ /_  / /  __/(__  )
/_/  /_/  \____/\__,_/  \__,_/ /_/  \___//____/

*-----------------------------------------------*
|                                               |
|          Step #1: Configure Manifest          |
|                                               |
*-----------------------------------------------*


 Please enter the name of the module: [Api]:
 > Api

 Please enter the slug for the module: [api]:
 > api

 Please enter the module version: [1.0]:
 > 1.0

 Please enter the description of the module: [This is th
 > application api module

 Please enter the author of the module: [ ]:
 > big_cat

 Please enter the module license: [MIT]:
 >

You have provided the following manifest information:
Name:        Api
Slug:        api
Version:     1.0
Description: application api module
Author:      big_cat
License:     MIT

 Do you wish to continue? (yes/no) [no]:
 > yes

Thanks! That's all we need.
Now relax while your module is generated for you.
 0/4 [>---------------------------]   0%
 1/4 [=======>--------------------]  25%
 2/4 [==============>-------------]  50%
 3/4 [=====================>------]  75%
 4/4 [============================] 100%
Module generated successfully.

建立完成!app

這時咱們再看下 app 目錄的結構

├─Console
│  └─Commands
├─Events
├─Exceptions
├─Http
│  ├─Controllers
│  │  └─Auth
│  ├─Middleware
│  └─Requests
├─Jobs
├─Listeners
├─Modules
│  └─api
│      ├─Console
│      ├─Database
│      │  ├─Migrations
│      │  └─Seeds
│      ├─Http
│      │  ├─Controllers
│      │  ├─Middleware
│      │  └─Requests
│      ├─Providers
│      └─Resources
│          ├─Lang
│          └─Views
├─Policies
└─Providers

能夠看到 modules 模塊目錄下存放着咱們新建的 api 模塊,擁有本身獨立的 mvc 結構'且擁有本身的獨立的路由配置:咱們能夠方便的在此處配置模塊的路由

<?php
//file:app/modules/api/Http/route.php
/*
|--------------------------------------------------------------------------
| Module Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for the module.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the Closure to execute when that URI is requested.
|
*/

Route::group(['prefix' => 'api'], function() {

	Route::get('/', function() {
		dd('This is the Api module index page.');
	});
    
    //經過這則路由演示下模塊的命名空間以及其視圖的命名空間
	Route::get('/index', 'IndexController@index');

});

訪問 http://domain/api 便可訪問此模塊

模塊加載機制

模塊服務註冊

Caffeinated\Modules\ModulesServiceProvider

ModulesServiceProvider 主要做用是加載咱們建立的模塊服務,將各模塊的環境參數,最主要的是路由規則註冊到服務容器,讓容器能夠管理各模塊。一次請求在咱們本身的代碼層什麼最爲重要?確定是路由註冊啦,路由註冊做爲服務容器分發請求到控制器的起點,必需要做爲核心服務加載到服務容器中。

好比咱們後面生成了一個 Api 模塊,會有一個模塊入口服務

app/Modules/api/Providers/ApiServiceProvider.php

模塊的入口服務在初始一些模塊環境參數,同時會加載本模塊的路由服務

app/Modules/api/Providers/RouteServiceProvider.php

 ApiServiceProvider 的服務爲模塊入口服務:加載路由服務,同時設置了模塊的視圖命名空間

public function register()
{
   // 加載本模塊的路由服務
   App::register('App\Modules\Api\Providers\RouteServiceProvider');
   
   // 爲 語言包 添加 api命名空間 索引 檢索路徑
   Lang::addNamespace('api', realpath(__DIR__.'/../Resources/Lang'));
   // 爲 視圖 添加 api命名空間 索引 檢索路徑
   // 這裏我不在把模塊視圖放在 resources/views 下 註冊到模塊本身的視圖路徑中
   // View::addNamespace('api', base_path('resources/views/vendor/api'));
   View::addNamespace('api', realpath(__DIR__.'/../Resources/Views'));
}

RouteServiceProvider 的服務:註冊模塊的路由規則

// 本模塊路由註冊時的基命名空間
protected $namespace = 'App\Modules\Api\Http\Controllers';

// 此處將模塊獨立的路由配置加載到服務容器中
public function map(Router $router)
{
    $router->group([
        'namespace'  => $this->namespace, // 本模塊默認的命名空間 
        'middleware' => ['web'] // 本模塊默認加載的中間件
    ], function ($router) {
        require (config('modules.path').'/Api/Http/routes.php'); //將路由配置併入服務容器
    });
}

視圖命名空間

視圖經過添加命名空間能夠爲某命名空間指定相對應的視圖路徑,並且能夠指定多個,laravel 會以最早匹配的爲準。laravel 默認的視圖路徑是在 resoures/views 下面,而咱們經過爲視圖添加新的命名空間能夠自定義視圖的加載路徑:

View::addNamespace($namespace, $alternate_views_dir_path);

如此一來當咱們使用

return view("api::index.index");

時會根據註冊的視圖命名空間檢索對應的視圖路徑,源碼中的邏輯會先檢索 

resources/views/vendor/api

若是不存對應視圖則繼續則檢索

app/Modules/api/Resources/Views

路由命名空間

RouteServiceProvider 服務爲服務容器提供路由註冊/解析的功能組件

這裏簡單說一下路由的默認命名空間:

laravel 路由註冊控制器的命名空間並非開放的,這意味着你沒法在註冊控制器時訪問根命名空間(handler 若是是閉包函數則不受影響)。如下講解皆爲 handler 爲控制器的場景:

laravel 路由服務自己有本身的 控制器 handler 基命名空間,以下:

// file:app/Providers/RouteServiceProvider.php
protected $namespace = 'App\Http\Controllers';
// file:app/Modules/api/Providers/RouteServiceProvider.php
protected $namespace = 'App\Modules\Api\Http\Controllers';

在路由服務中註冊的任何命名空間都是以本路由服務中的基命名空間爲前綴向後拼加的,因此咱們在註冊控制器的時候能夠不用補全控制器的全路徑。但這也形成你沒辦法利用 psr-4 規範靈活的在當前模塊下注冊綁定其餘模塊的控制器來處理請求,好比:

<?php
Route::group(['namespace' => '\App\Api\Controllers'], function () {
    Route::get('/api', "IndexController@index");
});

並不會從 app\Api\Controllers 路徑加載控制器,此路由規則的完整命名空間會被拼接成

\App\Http\Controllers\App\Api\Controllers

不過這樣也就省去了咱們本身要書寫可能長的讓你抓狂的命名空間的煩惱,並且這也是模塊化的體現,相互隔離,不該該交織訪問。

模塊實例

路由註冊

<?php
//file:app/modules/api/Http/route.php
/*
|--------------------------------------------------------------------------
| Module Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for the module.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the Closure to execute when that URI is requested.
|
*/

Route::group(['prefix' => 'api'], function() {

	Route::get('/', function() {
		dd('This is the Api module index page.');
	});
    
    // 注意當前路由服務的基命名空間
    // 完整的類名爲:App\Modules\Api\Http\Controllers\IndexController
	Route::get('/index/{name?}', 'IndexController@index');

});

 IndexController 控制器

<?php
//file:app/Modules/api/Http/Controllers/IndexController.php
/**
 * Created by PhpStorm.
 * User: think
 * Date: 2017/4/27 0027
 * Time: 20:47
 */

namespace App\Modules\Api\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class IndexController extends Controller
{
    public function __construct()
    {
    }

    public function index(Request $request, $name = "")
    {
        $msg = "hello " . $name;
        // 在 ApiServiceProvider 中我指定了命名空間api 的視圖路徑爲模塊視圖
        return view('api::index.index', ["msg" => $msg]);
    }
}

 視圖:注意我在模塊的入口服務中註冊的視圖命名空間

<!--file:app/Modules/api/Resources/Views/index/index.blade.php-->
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
{{ $msg }}
</body>
</html>

訪問 http://domain/api/index/big_cat 便可看到模塊正常運行了!

多模塊構建和開發完畢,再總結須要注意的幾點:

一、視圖命名空間:咱們能夠經過定義視圖的命名空間來指定新的視圖路徑,從而更好的配合模塊開發

二、路由命名空間:路由規則的 handler 爲指定的控制器時,會啓用當前路由服務的基命名空間,你是沒辦法訪問根命名空間的,除非你把基命名空間設爲 "\",咱們能夠在自帶的 http 模塊中載入其餘模塊,但這樣又使得你 handler 必須補全完整的類名,折中考慮構建多模塊開發纔是方便。

相關文章
相關標籤/搜索