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 [];
}
}