Slim Framework 的路由建立在 Fast Route 組件之上,速度快且穩定。即使我們使用此組件來進行所有路由,應用程式的核心已完全與其分離,並已放入介面來為使用其他路由函式庫鋪路。
您可以在 Slim\App
執行個體上使用代理方法來定義應用程式路由。Slim Framework 提供最熱門的 HTTP 方法的使用方式。
您可以新增一條路由,此路由只處理 GET
HTTP 請求,方法是使用 Slim 應用程式 get()
方法。它接受兩個引數
$app->get('/books/{id}', function ($request, $response, array $args) {
// Show book identified by $args['id']
});
您可以新增一條路由,此路由只處理 POST
HTTP 請求,方法是使用 Slim 應用程式 post()
方法。它接受兩個引數
$app->post('/books', function ($request, $response, array $args) {
// Create new book
});
您可以新增一條路由,此路由只處理 PUT
HTTP 請求,方法是使用 Slim 應用程式 put()
方法。它接受兩個引數
$app->put('/books/{id}', function ($request, $response, array $args) {
// Update book identified by $args['id']
});
您可以使用 Slim 應用程式裡的 delete()
方法,來新增只處理 DELETE
HTTP 要求的路由。它接受兩個參數
$app->delete('/books/{id}', function ($request, $response, array $args) {
// Delete book identified by $args['id']
});
您可以使用 Slim 應用程式裡的 options()
方法,來新增只處理 OPTIONS
HTTP 要求的路由。它接受兩個參數
$app->options('/books/{id}', function ($request, $response, array $args) {
// Return response headers
});
您可以使用 Slim 應用程式裡的 patch()
方法,來新增只處理 PATCH
HTTP 要求的路由。它接受兩個參數
$app->patch('/books/{id}', function ($request, $response, array $args) {
// Apply changes to book identified by $args['id']
});
您可以使用 Slim 應用程式裡的 any()
方法,來新增處理所有 HTTP 要求方法的路由。它接受兩個參數
$app->any('/books/[{id}]', function ($request, $response, array $args) {
// Apply changes to books or book identified by $args['id'] if specified.
// To check which method is used: $request->getMethod();
});
請注意第二個參數是回呼。您可以指定實作 __invoke()
方法的類別,而不是用 Closure。然後您可以在其他地方執行對應
$app->any('/user', 'MyRestfulController');
您可以使用 Slim 應用程式裡的 map()
方法來新增處理多個 HTTP 要求方法的路由。它接受三個參數
$app->map(['GET', 'POST'], '/books', function ($request, $response, array $args) {
// Create new book or list all books
});
上面描述的每個路由方法都接受回呼例程作為最後一個參數。此參數可以是任何 PHP 可呼叫的,且預設情況下它接受三個參數。
要求
第一個參數是表示目前 HTTP 要求的 Psr\Http\Message\ServerRequestInterface
物件。回應
第二個參數是表示目前 HTTP 回應的 Psr\Http\Message\ResponseInterface
物件。引數
第三個參數是包含目前路徑命名佔位符值的關聯式陣列。有兩種方式可以撰寫內容到 HTTP 回應
使用 Response 物件的 $response->getBody()->write('我的內容');
方法。
只要從路由回呼 echo()
內容。如果您新增 輸出緩衝 Middleware,此內容將會附加或置於目前的 HTTP 回應物件之前。
請注意,自從 Slim 4 之後,您必須傳回 Psr\Http\Message\ResponseInterface
物件。
如果你使用 相依性容器,並使用 Closure
實體作為路徑回呼函式,這個 closure 的狀態會繫結到 Container
實體。這表示你將可以透過 $this
關鍵字,在 Closure 內部 存取 DI 容器實體
$app->get('/hello/{name}', function ($request, $response, array $args) {
// Use app HTTP cookie service
$this->get('cookies')->set('name', [
'value' => $args['name'],
'expires' => '7 days'
]);
});
Slim 不支援 static
closures。
你可以新增一條路徑,使用 Slim 應用程式的 redirect()
方法,將 GET
HTTP 要求重新導向到另一個 URL。它接受三個參數
中
重新導向的
位置,可以是 字串
,也可以是 Psr\Http\Message\UriInterface302
)$app->redirect('/books', '/library', 301);
redirect()
路徑會回應要求的狀態碼,並將 Location
標頭設定為第二個參數。
路徑回呼函式的簽章是由路徑策略決定的。預設上,Slim 預期路徑回呼函式要接受要求、回應和一組路徑佔位符參數。這稱為 RequestResponse 策略。不過,你只要使用另一種策略,就能變更預期的路徑回呼函式簽章。舉例來說,Slim 提供了稱為 RequestResponseArgs
的另類策略,這個策略會接受要求和回應,並將各條路徑佔位符當成個別的參數。
以下是使用這個另類策略的一個範例
<?php
use Slim\Factory\AppFactory;
use Slim\Handlers\Strategies\RequestResponseArgs;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
/**
* Changing the default invocation strategy on the RouteCollector component
* will change it for every route being defined after this change being applied
*/
$routeCollector = $app->getRouteCollector();
$routeCollector->setDefaultInvocationStrategy(new RequestResponseArgs());
$app->get('/hello/{name}', function ($request, $response, $name) {
$response->getBody()->write($name);
return $response;
});
或者,你也可以針對每條路徑設定不同的呼叫策略
<?php
use Slim\Factory\AppFactory;
use Slim\Handlers\Strategies\RequestResponseArgs;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
$routeCollector = $app->getRouteCollector();
$route = $app->get('/hello/{name}', function ($request, $response, $name) {
$response->getBody()->write($name);
return $response;
});
$route->setInvocationStrategy(new RequestResponseArgs());
你可以透過實作 Slim\Interfaces\InvocationStrategyInterface
來提供自己的路徑策略。
上述各路徑方法都接受 URL 模式,這個模式會與目前的 HTTP 要求 URI 相符。路徑模式可以使用命名的佔位符,來動態比對 HTTP 要求 URI 片段。
路徑模式佔位符從 {
開始,接著是佔位符名稱,最後以 }
結束。以下是一個名為 name
的範例佔位符
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
// ...
$app->get('/hello/{name}', function (ServerRequestInterface $request, ResponseInterface $response, array $args) {
$name = $args['name'];
$response->getBody()->write("Hello, $name");
return $response;
});
要讓部分內容變成選擇性的,只要以方括弧括起來就可以了
$app->get('/users[/{id}]', function ($request, $response, array $args) {
// responds to both `/users` and `/users/123`
// but not to `/users/`
return $response;
});
透過巢狀撰寫,可以支援多個選擇性參數
$app->get('/news[/{year}[/{month}]]', function ($request, $response, array $args) {
// responds to `/news`, `/news/2016` and `/news/2016/03`
// ...
return $response;
});
對於「無限」選擇性參數,你可以這樣做
$app->get('/news[/{params:.*}]', function ($request, $response, array $args) {
// $params is an array of all the optional segments
$params = explode('/', $args['params']);
// ...
return $response;
});
在此範例中,/news/2016/03/20
的 URI 會產生包含三個元素的 $params
陣列:['2016', '03', '20']
。
預設情況下,佔位符寫在 {}
內部,且可接受任何值。然而,佔位符還可以要求 HTTP 請求 URI 符合特定正規表示法。如果當前的 HTTP 請求 URI 不符合佔位符正規表示法,路徑便不會呼叫。這是名為 id
的佔位符範例,它需要一個或多個數字。
$app->get('/users/{id:[0-9]+}', function ($request, $response, array $args) {
// Find user identified by $args['id']
// ...
return $response;
});
應用程式路徑可被指定一個名稱。如果您想透過 RouteParser 的 urlFor()
方法,以程式化方式產生特定路徑的 URL,這會很有用。上面說明的每一個路由方法都會回傳一個 Slim\Route
物件,而該物件會公開一個 setName()
方法。
$app->get('/hello/{name}', function ($request, $response, array $args) {
$response->getBody()->write("Hello, " . $args['name']);
return $response;
})->setName('hello');
使用應用程式 RouteParser 的 urlFor()
方法產生此已命名路徑的 URL。
$routeParser = $app->getRouteCollector()->getRouteParser();
echo $routeParser->urlFor('hello', ['name' => 'Josh'], ['example' => 'name']);
// Outputs "/hello/Josh?example=name"
RouteParser 的urlFor()
方法接受三個引數:
$routeName
路徑名稱。可透過 $route->setName('name')
設定路徑名稱。路徑對應方法會回傳 Route
的實例,因此您可以在對應路徑後直接設定名稱。例如:$app->get('/', function () {...})->setName('name')
$data
路徑樣式佔位符和替換值的關聯式陣列。$queryParams
要附加到所產生 URL 的查詢參數的關聯式陣列。為了協助將路徑組織成邏輯群組,Slim\App
也提供了一個 group()
方法。每個群組的路徑樣式都會附加在群組中所包含的路徑或群組之前,而群組樣式中的任何佔位符引數最終都會提供給巢狀的路徑。
use Slim\Routing\RouteCollectorProxy;
// ...
$app->group('/users/{id:[0-9]+}', function (RouteCollectorProxy $group) {
$group->map(['GET', 'DELETE', 'PATCH', 'PUT'], '', function ($request, $response, array $args) {
// Find, delete, patch or replace user identified by $args['id']
// ...
return $response;
})->setName('user');
$group->get('/reset-password', function ($request, $response, array $args) {
// Route for /users/{id:[0-9]+}/reset-password
// Reset the password for user identified by $args['id']
// ...
return $response;
})->setName('user-password-reset');
});
群組樣式可以為空,這允許將不共用一般模式的路徑進行邏輯性分組。
use Slim\Routing\RouteCollectorProxy;
// ...
$app->group('', function (RouteCollectorProxy $group) {
$group->get('/billing', function ($request, $response, array $args) {
// Route for /billing
return $response;
});
$group->get('/invoice/{id:[0-9]+}', function ($request, $response, array $args) {
// Route for /invoice/{id:[0-9]+}
return $response;
});
})->add(new GroupMiddleware());
請注意,在群組封閉中,Slim 會將封閉繫結到容器實例。
$this
會繫結到 Psr\Container\ContainerInterface
的實例。您也可以將中間件附加到任何路徑或路徑群組。
use Slim\Routing\RouteCollectorProxy;
// ...
$app->group('/foo', function (RouteCollectorProxy $group) {
$group->get('/bar', function ($request, $response, array $args) {
// ...
return $response;
})->add(new RouteMiddleware());
})->add(new GroupMiddleware());
可以透過 RouteCollector::setCacheFile()
啟用路由快取。請參閱以下範例:
<?php
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
/**
* To generate the route cache data, you need to set the file to one that does not exist in a writable directory.
* After the file is generated on first run, only read permissions for the file are required.
*
* You may need to generate this file in a development environment and committing it to your project before deploying
* if you don't have write permissions for the directory where the cache file resides on the server it is being deployed to
*/
$routeCollector = $app->getRouteCollector();
$routeCollector->setCacheFile('/path/to/cache.file');
你不一定要為你的路由定義函數。在 Slim 中有多種不同的方式來定義路由動作函數。
除了函數以外,你還可以:使用
__invoke()
方法的類別此功能是由 Slim 的 Callable 解析器類別啟用的。它會把字串項目轉換成函數呼叫。範例
$app->get('/', '\HomeController:home');
或者,你可以利用 PHP 的 ::class
算子,此算子與 IDE lookup 系統搭配使用效果良好,而且會產生相同的結果
$app->get('/', \HomeController::class . ':home');
你也可以傳遞陣列,其中第一個元素會包含類別名稱,第二個元素會包含要呼叫的方法名稱
$app->get('/', [\HomeController::class, 'home']);
在上面的這段程式碼中,我們定義了 /
路由,並告訴 Slim 在 HomeController
類別上執行 home()
方法。
Slim 會先在容器中尋找 HomeController
的項目,如果找到了,就會使用該執行個體,否則它就會呼叫建構函式,並把容器當成第一個引數。一旦建立了類別的執行個體之後,就會呼叫指定的方法,使用你已定義的任何策略。
建立一個具有 home
動作方法的控制器。建構函式應該接受需要的依存項。舉例來說:
<?php
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Views\Twig;
class HomeController
{
private $view;
public function __construct(Twig $view)
{
$this->view = $view;
}
public function home(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
{
// your code here
// use $this->view to render the HTML
// ...
return $response;
}
}
在容器中建立工廠,讓它使用依存項來建置控制器
use Psr\Container\ContainerInterface;
// ...
$container = $app->getContainer();
$container->set(\HomeController::class, function (ContainerInterface $container) {
// retrieve the 'view' from the container
$view = $container->get('view');
return new HomeController($view);
});
這能讓你利用容器進行依存項注入,而且還能讓你將特定的依存項注入到控制器中。
或者,如果類別在容器中沒有項目,那麼 Slim 會將容器的執行個體傳遞到建構函式。你可以使用具有多個動作的控制器,而不是只有處理一個動作的可呼叫類別來建立控制器。
<?php
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class HomeController
{
private $container;
// constructor receives container instance
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function home(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
{
// your code to access items in the container... $this->container->get('');
return $response;
}
public function contact(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
{
// your code to access items in the container... $this->container->get('');
return $response;
}
}
你可以這樣使用你的控制器方法。
$app->get('/', \HomeController::class . ':home');
$app->get('/contact', \HomeController::class . ':contact');
你不需要在你的路由可呼叫中指定任何方法,而且可以只把它設成可呼叫類別,例如:
<?php
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class HomeAction
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
{
// your code to access items in the container... $this->container->get('');
return $response;
}
}
你可以這樣使用這個類別。
$app->get('/', \HomeAction::class);
就像控制器一樣,如果你使用容器註冊類別名稱,那麼你就可以建立工廠,並在動作類別中注入你需要的特定依存項。
有時候在中間件中,你需要你路由的參數。
在此範例中,我們首先檢查使用者是否已登入,其次檢查使用者是否有權限觀看他們嘗試觀看的那部影片。
$app->get('/course/{id}', Video::class . ':watch')
->add(PermissionMiddleware::class);
<?php
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Routing\RouteContext;
class PermissionMiddleware
{
public function __invoke(Request $request, RequestHandler $handler)
{
$routeContext = RouteContext::fromRequest($request);
$route = $routeContext->getRoute();
$courseId = $route->getArgument('id');
// do permission logic...
return $handler->handle($request);
}
}
若要從路線中取得基礎路徑,只要執行下列動作即可
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
use Slim\Routing\RouteContext;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
$app->get('/', function(Request $request, Response $response) {
$routeContext = RouteContext::fromRequest($request);
$basePath = $routeContext->getBasePath();
// ...
return $response;
});