Help us understand the problem. What is going on with this article?

PHPの軽量フレームワークSlim 4とSQLiteで簡易Todoリストを作成して勉強してみた

私はPHP素人です。Slimもほぼ初めてさわってみてググりながら書いた内容ですので注意してください。


私が最終的に作成したコードはこちらです。

https://github.com/okumurakengo/slim-simple-todo


公式のサンプルなどをみながら作成してみたのですが、この辺のライブラリが必要だったので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

ITGpMhOSfs.gif

todoリストを動かすことができました。見ていただいてありがとうございました。m(_ _)m

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした