Slim
Slim
2020/04/18
2020/04/27 (補充內容)
介紹
Slim 是一個 PHP 微型框架,可以協助快速建立強勁的 Web應用和 API。即便 Slim 所提供的功能沒有 Laravel 完整,但對於撰寫一個 API 也是十分足夠的了。
安裝環境
*下載 Slim 前請先安裝 Composer ;啟動伺服器前要先安裝 PHP。
slim-skeleton
Slim 官網有提供 slim-skeleton,這是已經做好初始設置的 Slim 專案,讓我們可以更快速地建構一個後端服務。
在你要放置專案資料夾的路徑下,打開終端機輸入以下指令:
$ composer create-project slim/slim-skeleton [my-app-name]
[ my-app-name ] 的部分輸入專案資料夾的名稱。
例如:
$ composer create-project slim/slim-skeleton myApp
執行後,會產生一個新資料夾,可以進入該資料夾。
終端機前往創建的專案資料夾下;
$ cd [my-app-name]
例如:
$ cd myApp
執行php,-S 指定server的位址與port,-t 指定開啟的目錄
輸入 php 指令開啟伺服器,這邊使用 port:8080 為載入點。
$ php -S localhost:8080 -t public public/index.php
也可以利用composer來啟動 (事實上跟上一個動作是一樣的)
$ composer start
在瀏覽器打開localhost:8080,會看到
Hello world!
到創建的專案資料夾底下,可以看到一些檔案及資料夾,這些都是透過slim-skeleton產生的,有:
app
logs
public
src
tests
var
詳參: Slim 4 Skelenton
但是 slim-skeleton 是一個完整的後端專案,結構與內容對新手來說過於複雜,修改內容也相較比較困難。讓我們先從基礎的 Slim 開始練習:
Installation
前面的做法會幫我們新增一個資料夾,但是,以下的做法不一樣,要先自己新增一個專案資料夾。
首先在專案資料夾安裝 slim 與 psr7,打開終端機輸入以下指令:
$ composer require slim/slim:"4.*" slim/psr7
這個指令要求composer安裝slim以及psr7。
PSR,全名 PHP Standards Recommendations,是一個由非官方委員會 PHP-FIG 所提出的一系列標準。PSR-7主要是定義HTTP 訊息介面。(詳參: Day 03:PSR-12 概述、PSR-7 帶來的變革)
安裝完成後,會發現在資料夾下多了兩個檔案: composer.json及composer.lock,這兩個檔案是composer的設定檔。並且多了一個vendor的檔案夾,裡面就是剛剛我們利用composer安裝slim及psr的檔案。
接下來,在專案資料夾新增名為 public 的資料夾,並新增 index.php 的檔案。
在 index.php 將 slim 官網提供的程式碼貼上:
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
$app->get('/', function (Request $request, Response $response, $args) {
$response->getBody()->write("Hello world!");
return $response;
});
$app->run();
後面會針對以上程式碼作一些簡單的講解。
在php裡,use就好像是java的 import一樣,這些都是php的檔案,放在對應的路徑中。
接下來開啟伺服器:
$ php -S localhost:8080 -t public public/index.php
如果覺得這個太長,很難記,可以修改一下composer.json,加上scripts
{
"require": {
"slim/slim": "4.*",
"slim/psr7": "^1.0"
},
"scripts": {
"start": "php -S localhost:8080 -t public"
}
}
這樣的話,就可以利用composer啟動伺服器了
$ composer start
這時候在網址列打上 http://localhost:8080/,會看到預設的首頁:
Hello World!
資料庫
接下來繼續介紹如何抓取資料庫資料,並透過 API路徑傳值。
這邊繼續使用上面手動建置的專案,首先在專案目錄新增一個 config 資料夾,並新增一個 database.php。
config / database.php
<?php
return [
'host' => '127.0.0.1',
'port' => '8889',
'charset' => 'utf8',
'database' => 'test_wallet',
'username' => 'root',
'password' => 'root',
];
database.php 直接回傳連線資料庫的初始設置:
host : 連線本機使用 127.0.0.1 。(這邊不能用 localhost,不然會連線失敗)
port:看個人資料庫所使用的埠號。(一般的 MYSQL 使用的是 3306,我個人使用 MAMP 預設埠號是 8889)
charset:資料庫使用的語言編碼。
database:欲連線的資料庫名稱。
username:連線的使用者帳號。
password:連線的使用者密碼。
接著在專案資料夾下再新增一個 lib 資料夾,並新增 DB.php 及 bootstrap.php 兩個檔案。
可以參考先前課堂上所教的連線方式,直接接取PDO物件引入之後的檔案:
lib / DB.php
<?php
$config = require __DIR__ . '/../config/database.php';
$host = $config['host'] ?? '127.0.0.1';
$port = $config['port'] ?? '8889';
$charset = $config['charset'] ?? 'utf8';
$database = $config['database'] ?? 'test_wallet';
$username = $config['username'] ?? 'root';
$password = $config['password'] ?? 'root';
try
{
$conn = new PDO(
"mysql:host={$host};port={$port};charset={$charset};dbname={$database}",
$username,
$password
);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
echo "Connection failed: " . $e->getMessage();
}
lib / bootstrap.php
<?php
require __DIR__ . '/DB.php';
接著在 index.php 將資料庫引入:
public / index.php
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../lib/bootstrap.php';
$app = AppFactory::create();
$app->get('/', function (Request $request, Response $response, $args) {
$response->getBody()->write("Hello world!");
return $response;
});
這樣基本的資料庫物件就設置好了。
API 路由
資料庫建置好了,接下來就要來介紹如何將資料庫的資料接出,並設置路徑輸出,讓前端可以透過這個 API 位址獲得資料。
打開 index.php,首先先介紹一下路由設置的內容:
$app->get('/', function (Request $request, Response $response, $args) {
$response->getBody()->write("Hello World");
return $response;
});
$app 是我們透過 slim 建置的服務應用,仔細看 index.php 完整程式碼的話,會注意到最後我們會透過 $app->run() 執行這個應用。
get( '/url ' ,function(...) { /// }); ,get 的第一個參數為 API 路由的位址設定,前往 localhost:8080/ 會回傳 get( '/' ) 後面 function 所輸出的 response。
get 是 Http Request ( 請求方法 ),在客戶端(通常是前端頁面)向後端要求服務時,所指定的 Http Request。即使路徑位址一樣,Http Request 不同,後端回傳的內容也會有所不同;e.g. $app->get('/',... 跟 $app->post('/',... 是兩個不同的服務位址。(詳參: REST)
$response 是對於請求的回應,即客戶端向後端要求服務後,後端會回傳給客戶端的內容。這邊我們回傳訊息“Hello World!”,所以在網址 localhost:8080 下看到的就是我們輸出的訊息。
新增一個服務位址輸出資料庫,這邊以資料庫中的 users 資料表為例:
$app->get('/users', function (Request $request, Response $response, $args) {
global $conn;//讀取全域變數
$sql="SELECT * FROM `USERS`";
$stmt = $conn->prepare($sql);
$stmt->execute();
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);//二維陣列讀取
$response->getBody()->write(json_encode($users));//json_encode()將陣列格式轉換成Json型態
return $response;
});
讀取資料表中所有資料
$app->get('/users/{id}', function (Request $request, Response $response, $args) {
global $conn;
$id = $args['id'];
$sql="SELECT * FROM `USERS` WHERE id = :id";
$stmt = $conn->prepare($sql);
$stmt->execute(['id'=> $id ?? '1']);
$users = $stmt->fetch(PDO::FETCH_ASSOC);//一維陣列讀取
$response->getBody()->write(json_encode($users));
return $response;
});
讀取資料表中符合條件( id )的所有欄位資料
第二個範例較特別的是,路由可以讀取 url 中的值作為變數。這邊路徑 /users 後面的位址設置為變數 id,透過 $args['id'] 讀取,所以可以依照路徑不同抓取不同 id 的資料,而不用為每個 id 個別設置一個位址。
這時候在網址列打上 http://localhost:8080/users,會看到資料表輸出的資料:
http://localhost:8080/users/1 讀取資料表指定 id 資料:
如果,資料表裡的資料有中文字,會被encode。
[{"id":"1","role":"M","account":"405401372","name":"\u4f0d\u5ead\u5100","seat":"22","credit":"500","wish_list":"[]","purchase_list":"[]","created_at":"2019-10-27","updated_at":"2019-11-22 15:09:31","deleted_at":null},{"id":"2","role":"M","account":"405401607","name":"\u9ad8\u5b50\u8ed2","seat":"66","credit":"0","wish_list":null,"purchase_list":"[]","created_at":"2019-10-27","updated_at":"2019-11-22 13:30:30","deleted_at":null}]
所以,就要加上JSON_UNESCAPED_UNICODE,告訴json_encode,不要把unicode (utf-8)的文字encode。
$response->getBody()->write(json_encode($users, JSON_UNESCAPED_UNICODE));
這時候,中文字就不會被encode了。
[{"id":"1","role":"M","account":"405401372","name":"伍庭儀","seat":"22","credit":"500","wish_list":"[]","purchase_list":"[]","created_at":"2019-10-27","updated_at":"2019-11-22 15:09:31","deleted_at":null},{"id":"2","role":"M","account":"405401607","name":"高子軒","seat":"66","credit":"0","wish_list":null,"purchase_list":"[]","created_at":"2019-10-27","updated_at":"2019-11-22 13:30:30","deleted_at":null}]
完整的index.php:
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../lib/bootstrap.php';
$app = AppFactory::create();
$app->get('/', function (Request $request, Response $response, $args) {
$response->getBody()->write("Hello world, Ben!");
return $response;
});
$app->get('/users', function (Request $request, Response $response, $args) {
global $conn;//讀取全域變數
$sql="SELECT * FROM `USERS`";
$stmt = $conn->prepare($sql);
$stmt->execute();
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);//二維陣列讀取
$response->getBody()->write(json_encode($users, JSON_UNESCAPED_UNICODE));//json_encode()將陣列格式轉換成Json型態
return $response;
});
$app->get('/users/{id}', function (Request $request, Response $response, $args) {
global $conn;
$id = $args['id'];
$sql="SELECT * FROM `USERS` WHERE id = :id";
$stmt = $conn->prepare($sql);
$stmt->execute(['id'=> $id ?? '1']);
$users = $stmt->fetch(PDO::FETCH_ASSOC);//一維陣列讀取
$response->getBody()->write(json_encode($users, JSON_UNESCAPED_UNICODE));
return $response;
});
$app->run();