私はPHP素人です。Slimもほぼ初めてさわってみてググりながら書いた内容ですので注意してください。
私が最終的に作成したコードはこちらです。
公式のサンプルなどをみながら作成してみたのですが、この辺のライブラリが必要だったのでcomposerで追加しました
composer require slim/slim slim/psr7 slim/php-view php-di/php-di
composer.json
{
"require": {
"slim/slim": "^4.2",
"slim/psr7": "^0.5.0",
"slim/php-view": "^2.2",
"php-di/php-di": "^6.0"
}
}
Todoリスト作成
public/index.php
を作成します。基本的な処理は全てここに書きました。
エラー処理をほとんど書いていないのですが、指定のhtmlを表示、jsからデータをやり取りするapiの部分まで作成、という自分なりに目標にしていた部分ができたので今回はこんな感じになりました。
public/index.php
<?php
use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
use Slim\Routing\RouteCollectorProxy;
use Slim\Views\PhpRenderer;
require __DIR__ . '/../vendor/autoload.php';
$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions([
'settings' => [
'db' => [
'connection' => 'sqlite',
'user' => null,
'pass' => null,
'dbname' => __DIR__.'/db.sqlite',
],
'view' => [
'template_path' => 'views',
],
],
]);
$containerBuilder->addDefinitions([
'db' => function (ContainerInterface $c) {
['db' => [
'connection' => $connection,
'user' => $user,
'pass' => $pass,
'dbname' => $dbname,
]] = $c->get('settings');
return new PDO("{$connection}:{$dbname}", $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
},
]);
$containerBuilder->addDefinitions([
'renderer' => function (ContainerInterface $c) {
['view' => ['template_path' => $templatePath]] = $c->get('settings');
return new PhpRenderer($templatePath);
},
]);
$container = $containerBuilder->build();
$container->get('db')->exec(
'CREATE TABLE IF NOT EXISTS todos (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
text VARCHAR NOT NULL,
completed BOOLEAN NOT NULL
);'
);
AppFactory::setContainer($container);
$app = AppFactory::create();
$app->addErrorMiddleware(true, true, true);
$app->get('/', function (Request $request, Response $response, $args) {
return $this->get('renderer')->render($response, 'index.html');
});
$app->group('/api', function (RouteCollectorProxy $group) {
$group->get('/list', function (Request $request, Response $response) {
$response->getBody()
->write(json_encode($this->get('db')->query('SELECT * FROM todos')->fetchAll()));
return $response;
});
$group->post('/add', function (Request $request, Response $response) {
['text' => $text] = $request->getParsedBody();
$db = $this->get('db');
$stmt = $db->prepare('INSERT INTO todos(text, completed) VALUES(?, ?)');
$stmt->execute([$text, 0]);
$response->getBody()->write(json_encode(['res' => 'ok']));
return $response;
});
$group->post('/complete/{id}', function (Request $request, Response $response, array $args) {
['id' => $id] = $args;
$db = $this->get('db');
$stmt = $db->prepare('UPDATE todos SET completed = NOT(completed) WHERE id = ?');
$stmt->execute([$id]);
$response->getBody()->write(json_encode(['res' => 'ok']));
return $response;
});
})->add(function ($request, $handler) {
return $handler->handle($request)
->withHeader('Content-Type', 'application/json');
});
$app->run();
views/index.html
を作成します。jsの部分は結構適当です。
views/index.html
<!DOCTYPE html>
<meta charset="UTF-8">
<title>Document</title>
<h1>Simple Todo List</h1>
<form><input type="text" name="text"><button>追加</button></form>
<ul></ul>
<template>
<li>
<label>
<input type="checkbox" name="todo[]">
<span>hoge</span>
</label>
</li>
</template>
<script>
const ul = document.querySelector("ul")
const li = document.querySelector("template").content.querySelector("li")
// リスト取得
const getList = async () => {
ul.innerHTML = ""
for (const { id, text, completed } of await fetch("./api/list").then(res => res.json())) {
const clone = li.cloneNode(true)
const checkbox = clone.querySelector("[type=checkbox]")
checkbox.checked = !!Number(completed)
checkbox.setAttribute("data-id", id)
checkbox.addEventListener("change", async e => {
// チェックする
await fetch(`./api/complete/${e.target.dataset.id}`, { method: "POST" })
getList()
})
const span = clone.querySelector("span")
span.textContent = text
span.style.textDecoration = !!Number(completed) ? "line-through" : ""
ul.insertAdjacentElement("beforeend", clone)
}
}
// リスト追加
document.querySelector("form").addEventListener("submit", async e => {
e.preventDefault()
const input = document.querySelector("[type=text]")
if (!input.value) return;
await fetch("./api/add", { method: "POST", body: new FormData(e.target) })
getList()
input.value = ""
})
getList()
</script>
この状態でサーバーを起動します。
php -S localhost:8080 -t public public/index.php
todoリストを動かすことができました。見ていただいてありがとうございました。m(_ _)m