17
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

私は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

ITGpMhOSfs.gif

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

17
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?