jQurey Circliful[後編] 進捗度管理 ajax × MySQL
実装概要
前回の、Circlifulプラグインを利用して、ボタンを押して進捗度をリアルタイムで表示するというものを作りましたが、前回の場合(サンプル② ボタンを押して進捗度を表示する)は、データーベースに書き込んでいないので、ページを更新すると、情報が消えてしまい0パーセントになってしまいました。実際の実装では、ボタンを押すごとに、データーベースに、挿入・削除を行うので、それらの処理を非同期(ページを更新せずに、変更する処理)で、今回は実装してみます。
サンプルファイル構成
今回は、会員サイトで、エクセルの入門を提供していて、ユーザー毎に進捗度を管理するようなレッスン一覧を想定しています。従って、テーブルは下記のようにしました。lessonテーブルとusersテーブルの情報は固定の情報です。それらをもとに、jointテーブルに値を入れます。仕組みとしては、各レッスンの「未完了」ボタンを押すと、現在のユーザーidと lesson_id がjointテーブルに挿入され、それと同時に、ボタンが「完了済」に切り替わり、完了レッスンの値・進捗度%の値・グラフの値にも反映されるという流れです。
・usersテーブル…ユーザーの情報が登録されている。
・jointテーブル…lessonテーブルのidとusersテーブルのidを紐付けするためのテーブル。初期状態は空です。
usersテーブルに関しては、作りましたが、使いません(なら作るななという感じですが… )。というのも実際には会員サイト、ログイン時に、セッション変数にユーザーidを格納してユーザー情報を識別したりしているので、今回は、usersテーブルにあるSumiDaiさんのidである’1’を定数として定義して使うことにしました。
まずはデータベースの準備
lessonテーブル
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
name varchar(50) NOT NULL,
content text NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO lesson (name, content) VALUES
(‘エクセル入門1’, ‘エクセル入門1’),
(‘エクセル入門2’, ‘エクセル入門2’),
(‘エクセル入門3’, ‘エクセル入門3’),
(‘エクセル入門4’, ‘エクセル入門4’);
usersテーブル
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(30) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO users (name) VALUES
(‘SumiDai’);
jointテーブル
lesson_id bigint(20) NOT NULL,
user_id bigint(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
データベースの準備が完了したら、phpでMySQLに接続します。接続情報を記載したファイルを別途つくって、それをあとで読み込んで使うようにします。
db.php
<?php function db_connect(){ $dsn = 'mysql:host=localhost;dbname=sample;charset=utf8'; $user = 'root'; $password = 'japan'; try{ $dbh = new PDO($dsn, $user, $password); return $dbh; }catch (PDOException $e){ print('Error:'.$e->getMessage()); die(); } } ?>
このあたりは、host dbname $user $password の部分は、環境にあわせて、変更してください。
lesson.phpの解説
lesson.phpの上部にあるコード(データベース接続 SELECT部分)では、まず、レッスンの一覧を取得することはもちろんのこと、それらが、[完了済]又は、[未完了]のどちらかであることを確認しなければなりません。つまり、完了済みであれば、jointテーブルにlesson_idとuser_idが登録されているはずなので、SELECT文の中で、こちらの双方のデータのlessonテーブルのid と jointテーブルのlesson_idをLEFT JOINで紐付けします。
<?php define("USER_ID", 1); require_once("db.php"); $dbh = db_connect(); try{ $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $sql = "SELECT lesson.id, lesson.name, joint.lesson_id, joint.user_id FROM lesson left join joint on lesson.id = joint.lesson_id"; $stmt = $dbh->query($sql); $data = $stmt->fetchAll(); }catch (PDOException $e){ print('Error:'.$e->getMessage()); die(); } ?>
上記の$dataには、SELECTで取得したデータが格納さてれいますので、それらをHTML部分でforeachで表示します。このときに、1つ目の条件分岐より、user_idとlesson_idが存在すれば、classに ONを表示させます。これは、[完了済]/[未完了]でボタンの色を変えるためと、あとで、jQueryで[完了済]/[未完了]を判断するためです。2つ目の条件分岐では、一つ目の条件分岐と同様、user_idとlesson_idが存在すれば、[完了済]を表示し、それ以外なら[未完了]を表示します。
<?php foreach ($data as $key => $value){ ?> <?=$value['name'] ?> <button class="switch <?php if ($value['user_id']==USER_ID&&$value['id']==$value['lesson_id'] ){ echo 'ON'; } ?>" value="<?=$value['id']?>" ><?php if ($value['user_id']==USER_ID&&$value['id']==$value['lesson_id'] ){ echo '完了済'; }else{ echo '未完了' ;} ?></button><br> <?php } ?>
次に、[完了済]/[未完了]のどちらかのボタンを押したら、それぞれの処理がされなければならないので、このあたりをajaxで処理します。下記に、javascriptの部分を全て、表示しますが、重要な部分は、ajaxの部分です。特に、ボタンを押した際のクリックイベント$(“button.switch”).click(function(){}の部分のなかで、クリックしたら、先ほどphpにより表示したクラスのONの有無で、完了済みなのか未完了なのかを取得し、変数のhtmlに挿入します。そして、そのhtmlの中身は、’完了済’又は’未完了’になりますので、それらを条件分岐で、次の行動を選びます。もし変数htmlが’完了済’なら、既に、jointテーブルにはuser_idとlesson_idが関連した状態になりますので削除するために、ajaxを使って、非同期で、delete.phpに削除するlesson_idの情報をPOSTデータとして渡さなければなりません。また、その値は、ボタンにあるvalue値にデータベースよりセレクトして表示してあるので、それらを$(this).val();で取得して、lesson_idに格納しています。その後 ajax()メソッドを使って非同期でPOSTします。ご覧のとおり、送信先のURL(url: ‘add.php’),送信のタイプ(type: ‘POST’)、引き渡すデータ(data:{ lesson_id:lesson_id })を定義します。これにより、外部のファイルにデータが非同期で、送信され、送信先のdelet.phpで、データを受け取り、処理してもらいます。 一方で、先ほど説明したもし変数htmlが’完了済’以外なら、これとは逆に、未完了な状態なりますので、新たに、非同期で、add.phpにlesson_idデータを引渡し、add.phpでデータベースへ挿入してもらいます。
<script type="text/javascript"> $(function(){ var cmp = $('button').filter(function(){return $(this).html()==="完了済";}).length; //読込時に完了済みを数える。 var ratio = 25*cmp; var ang = 0; $("#cmp").html(cmp); //読込時に完了数を書き換える。 $("#ratio").html(ratio); //読込時に%を表示する。 $("#circle").circliful({ //グラフの表示 foregroundColor: '#9ACD32', backgroundColor: '#B0E0E6', fillColor: '#E0FFFF', //pointColor:'#E6E6FA', percent: ratio }); $("button.switch").click(function(){ var html = $(this).hasClass("ON")?"未完了":"完了済"; //classにONが存在するかどうかで分岐 var lesson_id = $(this).val(); //Buttonのvalue値を取得 if(html=="完了済"){ cmp = cmp + 1 //+1加算 ratio = ratio + 25 //+25加算 ang = ratio*3.6 //360度表記に変換 1%は3.6 $(".circle").attr("stroke-dasharray", ang + ', 20000'); $(".number").html(ratio); //% 書き換え $("#cmp").html(cmp); //完了数書き換え $("#ratio").html(ratio); //% 書き換え $.ajax({ url: 'add.php', //送信先 type: 'POST', //送信タイプ data:{ lesson_id:lesson_id }, //渡すデータ この場合、受取側で$_POST['lesson_id']で受け取る timeout: 10000, //タイムアウトになる秒 dataType: 'text' //データタイプ }) .done(function( data ) { //alert(data); //通信ができているか確認の為使う。 }); }else{ cmp = cmp - 1 ratio = ratio - 25 ang = ratio*3.6 $(".circle").attr("stroke-dasharray", ang + ', 20000'); $(".number").html(ratio); $("#cmp").html(cmp); $("#ratio").html(ratio); $.ajax({ url: 'delete.php', type: 'POST', data:{ lesson_id:lesson_id }, timeout: 10000, dataType: 'text' }) .done(function( data ) { //alert(data); }); } $(this).toggleClass("ON").html(html); }); }); </script>
add.phpの解説
add.php では、lesson.phpの「未完了」ボタンを押した際に、jQueryのajax通信より引き渡されたlesson_idのPOSTデータを受け取り、ユーザーIDとともにjointテーブルに挿入します。
<?php define("USER_ID", 1); //ユーザーID require_once("db.php"); //db.php読み込み $dbh = db_connect(); try{ $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $sql = "INSERT INTO joint (lesson_id, user_id) VALUES(:lesson_id, :user_ID)"; $stmt = $dbh->prepare($sql); $stmt->bindValue(':lesson_id',$_POST['lesson_id']); $stmt->bindValue(':user_ID', USER_ID); $stmt->execute(); }catch (PDOException $e){ print('Error:'.$e->getMessage()); die(); } ?>
delete.phpの解説
delete.php では、lesson.phpの「完了済」ボタンを押した際に、add.phpで挿入した該当のデータを削除します。jQueryのajax通信より引き渡されたlesson_idのPOSTデータを受け取り、その情報と、ユーザーIDが該当する部分を削除します。
<?php define("USER_ID", 1); //ユーザーID require_once("db.php"); //db.php読み込み $dbh = db_connect(); try{ $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $sql = "DELETE FROM joint WHERE lesson_id = :lesson_id AND user_id = :user_id"; $stmt = $dbh->prepare($sql); $stmt->bindValue(':lesson_id',$_POST['lesson_id']); $stmt->bindValue(':user_id', USER_ID); $stmt->execute(); }catch (PDOException $e){ print('Error:'.$e->getMessage()); die(); } ?>
感想
この実装の、特にコアな部分はlesson.phpのjavascriptの部分だと思います。特に、ajax通信の解説を詳しくしましたが、その他の細かな部分でも、jQueryが多様されており、一見複雑に見えますが、簡単なものがいくつかが絡み合っているだけの状態なので、分からない点があったら一つ一つを調べていけば理解できると思います。