mysqlと連携したtodoアプリです。削除に際して、チェックボックスはajaxを使っています。
reference
openssl_random_pseudo_bytes:Generate a pseudo-random string of bytes
<?php session_start(); require_once(__DIR__ . '/config.php'); require_once(__DIR__ . '/functions.php'); require_once(__DIR__ . '/Todo.php'); // get todos $todoApp = new \MyApp\Todo(); $todos = $todoApp->getAll(); //var_dump($todos); //exit; // create table todos( // id int not null auto_increment primary key, // state tinyint(1) default 0, // title text // ); // // insert into todos (state, title) values // (0, 'todo 0'), // (0, 'todo 1'), // (1, 'todo 2'); ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Todo app</title> <link rel="stylesheet" href="styles.css"> <style> </style> </head> <body> <div id="container"> <h1>Todos</h1> <form action="" id="new_todo_form"> <input type="text" id="new_todo" placeholder="What needs to be done?"> </form> <ul id="todos"> <?php foreach ($todos as $todo) : ?> <li id="todo_<?= h($todo->id); ?>" data-id="<?= h($todo->id); ?>"> <input type="checkbox" class="update_todo" <?php if ($todo->state === '1') { echo 'checked'; } ?>> <span class="todo_title <?php if ($todo->state === '1') { echo 'done'; } ?>"><?= h($todo->title); ?></span> <div class="delete_todo">x</div> </li> <?php endforeach; ?> <li id="todo_template" data-id=""> <input type="checkbox" class="update_todo"> <span class="todo_title"></span> <div class="delete_todo">x</div> </li> </ul> </div> <input type="hidden" id="token" value="<?= h($_SESSION['token']); ?>"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script> <script src="todo.js"></script> </body> </html>
$(function() { 'use strict'; $('#new_todo').focus(); // update $('#todos').on('click', '.update_todo', function() { // idを取得 var id = $(this).parents('li').data('id'); // ajax処理 $.post('_ajax.php', { id: id, mode: 'update', token: $('#token').val() }, function(res) { if (res.state === '1') { $('#todo_' + id).find('.todo_title').addClass('done'); } else { $('#todo_' + id).find('.todo_title').removeClass('done'); } }) }); // delete $('#todos').on('click', '.delete_todo', function() { // idを取得 var id = $(this).parents('li').data('id'); // ajax処理 if (confirm('are you sure?')){ $.post('_ajax.php', { id: id, mode: 'delete', token: $('#token').val() }, function() { $('#todo_' + id).fadeOut(800); }); } }); // create $('#new_todo_form').on('submit', function() { // titleを取得 var title = $('#new_todo').val(); // ajax処理 $.post('_ajax.php', { title: title, mode: 'create', token: $('#token').val() }, function(res) { // liを追加 var $li = $('#todo_template').clone(); $li .attr('id', 'todo_' + res.id) .data('id', res.id) .find('.todo_title').text(title); $('#todos').prepend($li.fadeIn()); $('#new_todo').val('').focus(); }); return false; }); });
<?php session_start(); require_once(__DIR__ . '/config.php'); require_once(__DIR__ . '/functions.php'); require_once(__DIR__ . '/Todo.php'); $todoApp = new \MyApp\Todo(); if ($_SERVER['REQUEST_METHOD'] === 'POST') { try { $res = $todoApp->post(); header('Content-Type: application/json'); echo json_encode($res); exit; } catch (Exception $e) { header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500); echo $e->getMessage(); exit; } }
<?php namespace MyApp; class Todo { private $_db; public function __construct() { $this->_createToken(); try { $this->_db = new \PDO(DSN, DB_USERNAME, DB_PASSWORD); $this->_db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); } catch (\PDOException $e) { echo $e->getMessage(); exit; } } private function _createToken(){ if (!isset($_SESSION['token'])){ $_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(16)); } } public function getAll() { $stmt = $this->_db->query("select * from todos order by id desc"); return $stmt->fetchAll(\PDO::FETCH_OBJ); } public function post() { $this->_validateToken(); if (!isset($_POST['mode'])) { throw new \Exception('mode not set!'); } switch ($_POST['mode']) { case 'update': return $this->_update(); case 'create': return $this->_create(); case 'delete': return $this->_delete(); } } private function _validateToken(){ if ( !isset($_SESSION['token']) || !isset($_POST['token']) || $_SESSION['token'] !== $_POST['token'] ) { throw new \Exception('invalid token!'); } } private function _update() { if (!isset($_POST['id'])) { throw new \Exception('[update] id not set!'); } $this->_db->beginTransaction(); $sql = sprintf("update todos set state = (state + 1) %% 2 where id = %d", $_POST['id']); $stmt = $this->_db->prepare($sql); $stmt->execute(); $sql = sprintf("select state from todos where id = %d", $_POST['id']); $stmt = $this->_db->query($sql); $state = $stmt->fetchColumn(); $this->_db->commit(); return [ 'state' => $state ]; } private function _create() { if (!isset($_POST['title']) || $_POST['title'] === '') { throw new \Exception('[create] title not set!'); } $sql = "insert into todos (title) values (:title)"; $stmt = $this->_db->prepare($sql); $stmt->execute([':title' => $_POST['title']]); return [ 'id' => $this->_db->lastInsertId() ]; } private function _delete() { if (!isset($_POST['id'])) { throw new \Exception('[delete] id not set!'); } $sql = sprintf("delete from todos where id = %d", $_POST['id']); $stmt = $this->_db->prepare($sql); $stmt->execute(); return []; } }