webx/routes

2.0.14 2018-11-19 20:49 UTC

README

webx-routes的主功能和设计目标

  • 简洁性 - 轻松遵循请求/响应路由到自然分层模型中的业务逻辑。
  • 可扩展性 - 永远不加载不必要的文件
  • 依赖注入(IOC)无处不在。
  • 可测试性 - 通过粘合所有内容来清晰地分离视图和业务对象。

入门指南

composer.json 中添加

 {
    "require" : {
        "webx/routes" : "major.minor.patch"
    }
 }

编写您的第一个路由索引.php public/index.php

    use WebX\Routes\Api\RoutesBootstrap;
    use WebX\Routes\Api\Response;

    require_once "../vendor/autoload.php"; // $_SERVER["DOCUMENT_ROOT"] points to 'public' folder.

    RoutesBootstrap::run(function(Response $response) {
        $response->typeJson([
            "user" =>
                ["name" => "Mr. Andersson"]
            ]
        ]);
        $response->data(1998,"user.popular"); //Merges the data into 'user.popular'
    });

将生成JSON响应

    {
        "user" : {
            "name" => "Mr. Andersson",
            "popular" => 1998
        }
    }

Routes中的内置响应类型

Routes默认支持以下响应类型

  • JsonResponse 以Json格式渲染数据(默认响应类型)。
  • TemplateResponseType 使用模板(Twig)渲染数据
  • RawResponseType 以原样渲染数据。
  • DownloadResponseType 以可下载文件渲染数据。
  • RedirectResponseType 301或302重定向到不同的URL。
  • FileContentResponseType 以自动检测内容类型的文件内容发送到浏览器。

Routes中的路由

    use WebX\Routes\Api\RoutesBootstrap;
    use WebX\Routes\Api\Routes;
    use WebX\Routes\Api\Response;

    RoutesBootstrap::run(function(Routes $routes) {

        $routes->onSegment("api",function(Routes $routes) {

            $routes->onMatch("v(?P<version>\d+)$",function(Routes $routes,$version) {
                $routes->load("api_v{$version}");                      // $version from RegExp

            })->onAlways(Response $response) {
                $response->typeJson(["message"=>"Not a valid API call"]);
            });

        })->onAlways(function(Response $response){
            $response->typeRaw();
            $response->data("Sorry, page not found.");
            $response->status(404);

        })
    });

Routes中的路由 - 带URL参数的示例

    use WebX\Routes\Api\RoutesBootstrap;
    use WebX\Routes\Api\Routes;
    use WebX\Routes\Api\Response;

    RoutesBootstrap::run(function(Routes $routes) {
        $routes->onAlways(function(Response $response, $myParam="default") {
            $response->typeRaw($myParam);
        }
    });

将渲染

  • hello/hello 的请求
  • default/ 的请求

路由开关

路由开关按从上到下的顺序评估。如果执行了路由开关,则不会评估和执行同一作用域内的其他开关。

支持以下路由开关

  • onAlways($action) 无评估执行。
  • onTrue($expression,$action) 如果 $expression 评估为 true 则执行
  • onSegment("url-segment",$action) 评估当前URL段(由 / 分爆的完整URL)。在路由开关匹配中,当前URL段将前移一个位置。
  • onMatch("reg-exp",$action) 将正则表达式与字符串(默认为URL)进行评估。正则表达式中的匹配参数将用于在 $action 中使用相同变量名时。

使用Twig

page.twig

    <html>
        <body>
            <h1>Welcome {{user.name}}</h1>
            Your input was {{input}}
        </body>
    </html>
    use WebX\Routes\Api\RoutesBootstrap;
    use WebX\Routes\Api\Response;
    use WebX\Routes\Api\Request;

    RoutesBootstrap::run(function(Response $response, Request $request) {
          $response->typeTemplate()->id("page");
          $response->data(["name"=>"Mr. Andersson"],"user");
          $response->data($request->parameter("input"), "input");
    });

读取输入

Routes提供了一种统一且类型安全的方式来从查询参数和JSON表单编码的请求中读取请求输入。

    use WebX\Routes\Api\RoutesBootstrap;
    use WebX\Routes\Api\Response;
    use WebX\Routes\Api\Request;

    RoutesBootstrap::run(function(Response $response, Request $request) {
          $reader = $request->reader(Request::INPUT_AS_JSON);
          $response->typeJson(["greeting"=>"Hello, {$reader->asString("user.name")}"]);
    });

配置Twig

在引导时间加载配置 changetwig(可以是任何名称)。

示例:将Twigs的标记分隔符更改为 {{{}}}(为了简化同一页面中的混合Angular和Twig)。

    use WebX\Routes\Api\RoutesBootstrap;
    use WebX\Routes\Api\Routes;
    use WebX\Routes\Api\Response;

    RoutesBootstrap::run([function(Routes $routes) {

        $routes->onAlways(function(Response $response) {
              $response->templateType()->id("page");
              $response->data(["name"=>"Mr. Andersson"],"user");
        })

    },"changetwig"]);

覆盖 TemplateResponseType 的设置以添加Twig配置器 config/changetwig.php

    return [
        "responseTypes" => [
            "WebX\\Routes\\Api\\ResponseTypes\\TemplateResponseType" => [
                "config" => [
                    "configurator" => function(Twig_Environment $twig) {
                        $lexer = new Twig_Lexer($twig, array(
                            'tag_variable'  => array('{{{', '}}}')
                        ));
                        $twig->setLexer($lexer);
                    },
                    "options" => [    // Passed as second argument to Twig_Environment
                        "cache" => "/tmp/twig_cache"
                    ]
                ]
            ]
        ]
    ]

加载配置和IOC容器

在Routes中,所有逻辑都在 actions 中执行。操作可以是

  • \Closure
  • string(格式为 "ControllerClass#method")

为了支持配置的延迟加载,Routes允许将操作定义为格式为 array 的: [$action,"config1","config2","configN"]

src/MyBusiness/Impl/Services/AdminService.php

    use MyBusiness\Api\Services\IAdminService;

    class AdminService implements IAdminService {
        public function __construct() {}

        public function countAdmins() {
            return 3;
        }
    }

config/admin.php:

    use MyBusiness\Impl\Services\AdminService;

    return [
        "ioc" => [
            "register" => [
                [AdminService::class]
            ]
        ]
    ]
    use WebX\Routes\Api\RoutesBootstrap;
    use WebX\Routes\Api\Routes;
    use WebX\Routes\Api\Response;
    use MyBusiness\Api\Services\IAdminService;

    RoutesBootstrap::run(function(Routes $routes) {

        $routes->onSegment("admin",[function(Response $response, IAdminService $adminService) {
              $response->data($adminService->countAdmins(),"count");
        },"admin"]);

        // The admin-configuration is only loaded if routes matched the `admin` segment.
    });

配置文件结构 The 配置文件,可以在任何级别加载,是正常的php文件,预期返回一个 array

最小配置文件(什么都不做)

<?php

return [];

?>

Ioc容器

WebX-Ioc容器 嵌入在WebX-Routes框架中。WebX-Routes支持在配置 ioc 部分中动态注册/静态调用服务。

使用 config/someconfig.php 动态注册WebX-Ioc容器中的服务。

<?php

return [
    "ioc" => [
        "register" => [ // The classes will be scanned for all their implemented interfaces.
            [MyClass:class],
            [MyOtherClass:class]
        ],
        "initStatic" => [ //
            [MyValueObject::class,"initMethod"] // The static "initMethod" will be invoked with declared dependencies.
        ]
    ],
    "mappings" => [
        "closureParameterName" => "iocInstanceId"
    ]

]

与控制器路由一起使用时,支持更传统的控制器结构。控制器是具有方法和构造函数的简单类,它们通过IOC支持进行调用。

路由支持将$action定义为格式为ControllerClass#methodstring

    use WebX\Routes\Api\RoutesBootstrap;
    use WebX\Routes\Api\Routes;

    RoutesBootstrap::run(function(Routes $routes) {

        $routes->onSegment("admin",['MyBusiness\\Controllers\\AdminCtrl','adminConfig']
        // The admin-configuration is only loaded if routes matched the `admin` segment. Methods on the
        // controller will automatically be mapped by the next available segment

        // If no next segment exist Routes will map the request to `index()` of the controller instance
    });

src/MyBusiness/Controllers/AdminController.php

    namespace MyBusiness\Controllers;

    class AdminController {

        private $logService;

        public function __construct(ILogService $logService) {
            $this->logService = $logService;
        }

        public function countAdmins(Response $response, RawResponseType $responseType, IAdminService $adminService) {
            $response->type($responseType);
            $response->data("Hello there " + $adminService->countAdmins() + " admin(s)");
        }
    }
    #Controller functions can be invoked with user parameters. Parameters, taking precedence over IOC injected ones,
    #can be defined in the last arguemnt `$parameters` array or with parametes defiend in the `onMatch` switch.

为加载控制器定义默认命名空间

通过在动态配置的namespaces部分添加命名空间,可以省略完整的类名。

config/admin.php:

    return [
        "namespaces" => ["MyBusiness\\Controllers"]
    ]

创建自己的ResponseType

要实现自己的ResponseType,只需创建一个扩展ResponseType的接口,并在配置文件中使用ioc/register进行配置。请参阅bootstrap_config.php以了解配置方法。

在配置文件中配置自己的ResponseType

    return [
        "responseTypes" => [
            "YourNamespace\\YourResponseTypeInterface" => [
                "class" => "YourNamespace\\YourResponseTypeClass",
                "config" => [
                    "yourSetting" => false // Will be available by Configuration to ResponseType.
                ]
            ]
        ]
    ]

配置路由

路由的标准配置基于相对于$_SERVER['DOCUMENT_ROOT']的应用程序目录。

配置RoutesBootstrap

    RoutesBootstrap::run($action,[
        "home" => "../"         // Default.
                                // Use '/' to have application in same directory
                                // as public files (not recommended).
    ]);

Routes应用程序的默认目录结构

    /
        /config          (Config files loaded by [$action, "someconfig"]
            someconfig.php

        /routes          (Files loaded by Routes->load("someroute")
            someroute.php

        /templates       (Templates loaded by TemplateResponse->setTemplate("sometemplate")
            sometemplate.twig

        /public          ($_SERVER['DOCUMENT_ROOT'])
            index.php

        /vendor          (Composer)
            /webx
                /routes
                /ioc

测试

在根目录中执行

    phpunit -c tests