同步釋出於 SXYBlog
本文將講述如何將 Slim 弄成 MVC 架構,以及在其中使用 Eloquent ORM 和 Blade。
這是我習慣的目錄結構,供諸位參考。
第一步:基本配置
按照 Slim 官方的教程,先建立一個專案框架。
$ composer create-project slim/slim-skeleton [my-app-name]
然後安裝需要用到的擴充套件。
$ composer require illuminate/database rubellum/slim-blade-view
再將 public/index.php
改成這樣。
<?php
// index.php
use Carbon\Carbon;
use Illuminate\Database\Capsule\Manager;
use Slim\App;
if (PHP_SAPI == 'cli-server') {
// To help the built-in PHP dev server, check if the request was actually for
// something which should probably be served as a static file
$url = parse_url($_SERVER['REQUEST_URI']);
$file = __DIR__ . $url['path'];
if (is_file($file)) {
return false;
}
}
require __DIR__ . '/../vendor/autoload.php';
session_start();
// 建立 App 例項
$settings = require __DIR__ . '/../bootstrap/settings.php';
$app = new App($settings);
$container = $app->getContainer();
// 設定 Carbon 的語言以及預設時區
Carbon::setLocale('zh');
date_default_timezone_set('Asia/Shanghai');
// 啟動 Eloquent ORM
$capsule = new Manager;
$capsule->addConnection($container->get('settings')['database']);
$capsule->setAsGlobal();
$capsule->bootEloquent();
// 載入依賴
$dependencies = require __DIR__ . '/../bootstrap/dependencies.php';
$dependencies($app);
// Utils.php 是我自己寫的函式庫,你可以去掉
require_once __DIR__ . '/../bootstrap/utils.php';
// 註冊全域性中介軟體
$middleware = require __DIR__ . '/../bootstrap/middleware.php';
$middleware($app);
// 註冊路由
require __DIR__ . '/../routes/web.php';
// 執行 App
$app->run();
<?php
// dependencies.php
use Slim\App;
use Slim\Container;
use Slim\Views\Blade;
return function (App $app) {
// 獲取容器例項
$container = $app->getContainer();
// 配置 Blade 作為模板引擎
$container['renderer'] = function (Container $c) {
$settings = $c->get('settings')['renderer'];
return new Blade($settings['template_path'], $settings['cache_path']);
};
// 配置 Eloquent ORM
$container['db'] = function (Container $c) {
global $capsule;
return $capsule;
};
};
<?php
// middleware.php
use Slim\App;
return function (App $app) {
// 這裡需要返回一箇中介軟體,可以是函式或者類。
// 如果傳入類,該類需要有 __invoke 方法。
// 具體可參閱官方文件: http://www.slimframework.com/docs/v3/concepts/middleware.html
// e.g: $app->add(new \Slim\Csrf\Guard);
};
<?php
// settings.php
return [
'settings' => [
'displayErrorDetails' => true, // 是否顯示錯誤詳情。務必線上上設為 false !!!
'addContentLengthHeader' => false, // 設為 true 以在每次響應中新增 Content-Length Header
// Blade,或者說模板引擎的配置
'renderer' => [
'template_path' => __DIR__ . '/../resources/view/',
'cache_path' => __DIR__ . '/../storage/cache',
],
// 資料庫配置
'database' => [
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'lw',
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
],
],
];
<?php
// utils.php
use App\Http\Models\Model;
use App\Http\Models\User;
use Overtrue\Validation\Translator;
use Overtrue\Validation\Validator;
use Slim\Http\Response;
// 跟 Laravel 的 view 用法基本一致
function view($path, array $args = [])
{
global $container;
return $container->get('renderer')->render($container->get('response'), $path, $args);
}
// 獲取 Response 例項
function response(): Response
{
global $container;
return $container->get('response');
}
// 跟 Laravel 用途一致
function asset($path)
{
$schema = $_SERVER['HTTPS'] == 'on' ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'];
$path = trim($path, '/');
$url = "{$schema}://{$host}/{$path}";
return $url;
}
// 跟 Laravel 用途一致
function route($name, $args = [])
{
global $container;
if (is_object($args) && $args instanceof Model) {
$args = [$args->getRouteKeyName() => $args->getRouteKey()];
}
return $container->get('router')->pathFor($name, $args);
}
// 在 Blade 模板中使用 `@auth` 會呼叫此函式。
function auth()
{
// 見下方 Auth 類
return new Auth();
}
// 在 Blade 模板中使用 `@method` 會呼叫此函式。
function method_field($method)
{
return "<input type='hidden' name='_METHOD' value='{$method}'>";
}
// 一個(偽)認證類
class Auth
{
public function guard()
{
return $this;
}
public function check()
{
if (isset($_SESSION['uid']) && is_numeric($_SESSION['uid'])) {
return true;
}
return false;
}
public function user()
{
$user = User::find($_SESSION['uid']);
return $user;
}
}
第二步:控制器
我建議大家建立一個 Controller 類,封裝一些常用方法,再由每個控制器繼承該類。
<?php
// Controller.php
namespace App\Http\Controllers;
use Illuminate\Database\Capsule\Manager;
class Controller
{
protected $db;
public function __construct()
{
global $capsule;
// 可在控制器中透過 `$this->db` 獲取資料庫例項,類似 Laravel 的 DB Facade。
$this->db = $capsule;
}
}
下面是一個使用者控制器的例子。
<?php
// UserController.php
namespace App\Http\Controllers;
use App\Http\Models\User;
use Slim\Http\Request;
// 繼承了 Controller 類
class UserController extends Controller
{
public function profile(Request $request)
{
// 透過 User 模型獲取
// $request->getAttribute($name) 用於獲取路由引數
$user = User::query()->find($request->getAttribute('id'));
// 返回檢視
return view('users.profile', compact('user'));
}
public function edit(Request $request)
{
$user = User::query()->find($request->getAttribute('id'));
return view('users.edit', compact('user'));
}
public function secure(Request $request)
{
$user = User::query()->find($request->getAttribute('id'));
// 使用 `$this->db` 運算元據庫
$auths = $this->db->getConnection()->table('local_auths')->where('user_id', $user->id)->get();
return view('users.secure', compact('user', 'auths'));
}
public function update(Request $request)
{
$user = User::query()->find($request->getAttribute('id'));
// 判斷請求方法
if ($request->isMethod('PUT')) {
// $request->getParams(array $only = null) 用於獲取請求引數
$user->update($request->getParams(['nickname']));
// 重定向
return response()->withRedirect(route('users.profile', $user));
} else {
$data = $request->getParams();
// 這是我自己封裝的資料驗證
$v = validate($data, [
'current_password' => 'required',
'new_password' => 'required|confirmed|between:6,18',
]);
if ($v->fails()) {
return response()->write($v->errors()->first());
}
$auth = $this->db->getConnection()->table('local_auths')->where('user_id', $user->id)->where('username', $data['username']);
if (password_verify($data['current_password'], $auth->first()->password)) {
$auth->update([
'password' => password_hash($data['new_password'], PASSWORD_DEFAULT)
]);
unset($_SESSION['uid']);
return response()->withRedirect(route('login'));
} else {
// 輸出文字
return response()->write('當前密碼錯誤');
}
}
}
}
<?php
// User.php
namespace App\Http\Models;
class User extends Model
{
// Eloquent ORM 預設會自動維護這兩欄位
const UPDATED_AT = null;
const CREATED_AT = 'registered_at';
protected $guarded = [];
protected $dates = ['registered_at'];
public function getAvatarAttribute($value)
{
return $value ?: 'https://api.adorable.io/avatars/300/' . ($this->email ?: 'mail@example.com');
}
}
第三步:路由
<?php
// routes/web.php
use App\Http\Controllers\Auth\LoginController;
use App\Http\Controllers\Auth\RegisterController;
use App\Http\Controllers\PageController;
use App\Http\Controllers\UserController;
use App\Http\Middleware\VerifyUserPermission;
$app->get('/', PageController::class . ':index')->setName('index');
$app->map(['get', 'post'], '/login', LoginController::class . ':login')->setName('login');
$app->map(['get', 'post'], '/register', RegisterController::class . ':register')->setName('register');
$app->post('/logout', LoginController::class . ':logout')->setName('logout');
$app->get('/about', PageController::class . ':about')->setName('about');
$app->get('/users/{id}', UserController::class . ':profile')->setName('users.profile');
$app->group('/users', function () use ($app) {
$app->get('/{id}/edit', UserController::class . ':edit')->setName('users.edit');
$app->get('/{id}/secure', UserController::class . ':secure')->setName('users.secure');
$app->put('/{id}', UserController::class . ':update')->setName('users.update');
$app->patch('/{id}', UserController::class . ':update')->setName('users.update');
// TODO: 完善使用者登出功能
$app->delete('/{id}', UserController::class . ':delete')->setName('users.delete');
})->add(new VerifyUserPermission());
大功告成
本作品採用《CC 協議》,轉載必須註明作者和本文連結