• WEB

マイクロフレームワークSilexとfullcalender.jsを利用してスケジュール管理サイトを作成しました

  • きん
    きん システムちーむ
  • このエントリーをはてなブックマークに追加
マイクロフレームワークSilexとfullcalender.jsを利用してスケジュール管理サイトを作成しました

ご無沙汰しております。きんです。
最近は2歳児の夜泣きに毎日苦しめられております。
いきなりMAXの音量で泣き始めるため、毎度心臓バクバクで飛び起きています。
いわゆるかんの強い子なのでしょうか。
…完全にママ似です。ごめんね☆

先日業務で利用する機会があったSilexが個人的にかなりお気に入りです。
CakePHPやLaravelなどに比べて学習コストも少なくて済み、私のようなめんどくさがりさんや、ちょこっと何か作りたい時にはぜひおすすめです。

Silexについての情報は詳しくは公式サイトをご覧くださいまし。

Silex利用手順

vagrant作業環境

  • php5.3.3
  • CentOS6.6

※composerが入っている前提です。

手順

composerから作業フォルダにsilexインストール

$ composer require silex/silex:~1.2

公式サイトを参考に.htaccessを設定

Options -MultiViews

    RewriteEngine On
    #RewriteBase /path/to/app
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [QSA,L]

index.phpを作成

<?php
    require_once __DIR__ . '/vendor/autoload.php';
    
    // Silex
    $app = new Silex\Application();
    
    # トップページ
    $app->get('/', function() use($app) {
        
        return 'Hello World';
    });
    
    $app->run();

とりあえず表示まではこれでOKです。
ただ、SilexのテンプレートにはTwigがありますが、こちらはデフォルトではインストールされていませんので、別途TwigServiceProviderからインストールする必要があります。
また、DBも同様にDoctrineServiceProviderからインストールしてきます。

ではcomposer.jsonを編集します。

{
    "require": {
        "silex/silex": "~1.2",
        "symfony/twig-bridge": "~2.6",
        "doctrine/dbal": "~2.5"
    }
}
$ composer update

これで開発に必要な環境はすべて整いました。

fullcalender.jsを利用してスケジュール管理サイトの作成

 

calender

今回はこちらを利用してスケジュール帳を作りました♪
ちょいちょい俺流記述があるかと思いますので、お見苦しい点はご容赦下さい。。。

[デモ]
※デモはDBに接続していません。

DB

テーブル名:schedules
id int(11)
title varchar(50)
start datetime
end datetime
status tinyint(1)
allDay tinyint(1) デフォルト値0
deleted tinyint(1) デフォルト値0

javascriptライブラリ

ソース

index.php


<?php
require_once __DIR__ . '/vendor/autoload.php'; 
// Silex
$app = new Silex\Application();
$app['debug'] = TRUE;
// Twigテンプレートエンジン
$app->register(new Silex\Provider\TwigServiceProvider(), array(
  'twig.path' => __DIR__ . '/views',
));
    
// DB
$app->register(new Silex\Provider\DoctrineServiceProvider(), array(
    'db.options' => array(
        'dbname'   => 'dbname',
        'user'     => 'user',
        'password' => 'password',
        'host'     => 'localhost',
        'driver'   => 'pdo_mysql',
    )
));
    
//ルーティング
$app->register(new Silex\Provider\UrlGeneratorServiceProvider());
    
//ステータス
$statusList = array('会議', '往訪', '来訪', 'その他');
    
    
# トップページ
$app->get('/', function() use($app, $statusList) {        
    return $app['twig']->render('index.twing', array(
        'statusList' => $statusList
    ));
})->bind('index');
    
    
# スケジュールの取得(トップページ)
$app->get('/getSchedule', function() use($app, $statusList) {
        
    //未削除データをすべて取得
    $stmt = $app['db']->query(
        'SELECT * FROM schedules WHERE deleted = 0'
    );
        
    $scheduleList = array();
    while ($row = $stmt->fetch()) {
        $scheduleList[] = array(
            'id' => $row['id'],
            'status' => $row['status'],
            'title' => '【'
                . $statusList[$row['status']] . '】' . $row['title'],
            'start' => date('Y-m-d H:i', strtotime($row['start'])),
            'end' => date('Y-m-d H:i', strtotime($row['end'])),
            'allDay' => (bool) $row['allDay']
        );
    }
    return json_encode($scheduleList);
});
    
    
# スケジュールの登録
$app->post('/addSchedule', function() use($app) {
        
    $start = $app['request']->get('date') . ' '
             . $app['request']->get('startHour')
             . ':' . $app['request']->get('startMinute');
    
    $end = $app['request']->get('date') . ' '
           . $app['request']->get('endHour')
           . ':' . $app['request']->get('endMinute');
        
    $allDay = 0;
    if($app['request']->get('allDay')) {
        $allDay = $app['request']->get('allDay');
    }
    $app['db']->insert(
        'schedules',
            array(
                'title' => $app['request']->get('title'),
                'status' => $app['request']->get('status'),
                'start' => $start,
                'end' => $end,
                'allDay' => $allDay,
            )
    );
    return $app->redirect($app['url_generator']->generate('index'));
});
    
# スケジュールの修正または削除
$app->post('/updateSchedule', function() use($app) {
    if (!$app['request']->get('deleted')) {
        //編集時
        $start = $app['request']->get('date') . ' '
                 . $app['request']->get('startHour')
                 . ':' . $app['request']->get('startMinute') . ':00';

        $end = $app['request']->get('date') . ' '
               . $app['request']->get('endHour')
               . ':' . $app['request']->get('endMinute') . ':00';
            
        $app['db']->update(
            'schedules',
                array(
                    'title' => $app['request']->get('title'),
                    'start' => $start,
                    'end' => $end,
                    'status' => $app['request']->get('status'),
                    'allDay' => $app['request']->get('allDay'),
                ),
                array(
                    'id' => $app['request']->get('id')
        ));
    } else {
        //削除時
        $app['db']->update(
            'schedules',
                array(
                    'deleted' => $app['request']->get('deleted'),
                ),
                array(
                    'id' => $app['request']->get('id')
        ));
    }
    return $app->redirect($app['url_generator']->generate('index'));
});
    
# エラー時
$app->error(function (\Exception $e, $code) use ($app) {
    if ($app['debug']) {
        return;
    }
    echo $e->getMessage();
});
    
$app->run();
    

views/index.twing

<!DOCTYPE html>
<html>
  <head>
    <link media="screen" href="./css/style.css" rel="stylesheet">
    <link href="./js/fullcalendar/fullcalendar.min.css" rel="stylesheet" type="text/css">
        
    <script type="text/javascript" src="./js/fullcalendar/moment.min.js"></script>
    <script type="text/javascript" src="./js/jquery.min.js"></script>
    <script type="text/javascript" src="./js/fullcalendar/jquery-ui.custom.min.js"></script>
    <script type="text/javascript" src="./js/fullcalendar/fullcalendar.js"></script>
    <script type="text/javascript" src="./js/fullcalendar/ja.js"></script>
    <script type="text/javascript" src="./js/jquery.lightbox_me.js"></script>
    <script type="text/javascript" src="./js/index.js"></script>

    <meta charset="utf-8" />
  </head>
  <body>
    <div id="header">
      <h1>スケジュール</h1>
    </div>
    <div id="content">
      <div id="calendar" style="width:95%"></div>
      <div id="schedule_form">
        *スケジュール登録
        <form method='post' action="" id="scheduleForm" class="formTable">
          <table>
            <tr id="deleteArea">
              <th>削除</th>
              <td>
                <input type="checkbox" name="deleted" value="1" />
              </td>
            </tr>
            <tr class="inputArea">
              <th>ステータス</th>
              <td>
                <select name="status">
                  {% for key, status in statusList %}
                    <option value="{{key}}">{{status}}</option>
                  {% endfor %}
               </select>
              </td>
            </tr>
            <tr class="inputArea">
              <th>内容</th>
              <td><input required type="text" name="title" /></td>
            </tr>
            <tr class="inputArea">
              <th>日時</th>
              <td><input type="text" name="date" value="" /></td>
            </tr>
            <tr class="inputArea">
              <th>終日</th>
              <td>
                <input type="checkbox" name="allDay" value="1" />
              </td>
            </tr>
            <tr class="inputArea timeArea">
              <th>開始時間</th>
              <td>
                <select name="startHour">
                  {% for i in 0..23 %}
                    <option value="{{i}}">{{i}}</option>
                  {% endfor %}
                </select>
                :
                <select name="startMinute">
                  <option value="0">00</option>
                  <option value="15">15</option>
                  <option value="30">30</option>
                  <option value="45">45</option>
                </select>
              </td>
            </tr>
            <tr class="inputArea timeArea">
              <th>終了時間</th>
              <td>
                <select name="endHour">
                  {% for i in 0..23 %}
                    <option value="{{i}}">{{i}}</option>
                  {% endfor %}
                </select>
                :
                <select name="endMinute">
                  <option value="0">00</option>
                  <option value="15">15</option>
                  <option value="30">30</option>
                  <option value="45">45</option>
                </select>
              </td>
            </tr>
          </table>
          <input type="hidden" name= "id" value="" />
          <br />
          <div class="submit">
            <input type="submit" value="送信" id="submit" />
          </div>
        </form>
      </div>      
    </div>
    <div id="footer"></div>
  </body>
</html>

js/index.js


jQuery(document).ready(function($){
    
    //イベント登録用フォーム
    $('#schedule_form').hide();
    
    //カレンダー設定
    $('#calendar').fullCalendar({
        // ヘッダーのタイトルとボタン
        header: {
            left: 'prev,next today',
            center: 'title',
            right: 'month'
        },
        timeFormat: 'H:mm',
        theme: false,
        height: 500,
        // 日付クリック時
        dayClick: function (date) {
            
            //日付のみフォームにセット
            var m = moment(date._d);
            var month = m.month() + 1;
            var setDate = m.year() + '/' + month + '/' + m.date();
            setFormData(setDate);
            
            //フォームのポップアップ表示
            $('#schedule_form').lightbox_me({
                centered: true,
                overlaySpeed: 'fast',
            });
        },
        //イベントクリック時
        eventClick: function(event) {
            
            //更新時は削除用のチェックボックス表示
            var formArr = [];
            formArr.deleteArea = 'show';
            
            //日付のセット
            var m = moment(event._start._i, event._start._f);
            var date = m.format('YYYY/MM/DD'); //日付
            
            formArr.startHour = m.hours();//時(start)
            formArr.startMinute = m.minutes(); //分(start)
            
            if (event.allDay == false) {
                var m = moment(event._end._i, event._end._f);
            }
            formArr.endHour = m.hours(); //時(end)
            formArr.endMinute = m.minutes(); //分(end)
            
            formArr.title = event.title.replace(/^【.+?】/, '');//内容
            formArr.allDay = event.allDay;//終日フラグ
            formArr.id = event.id;//イベントID
            formArr.status = event.status;//イベントステータス
            
            //各データをフォームにセット
            setFormData(date, formArr);
            
            //フォームのポップアップ表示
            $('#schedule_form').lightbox_me({
                centered: true,
                overlaySpeed: 'fast',
            });
            
        },
        // 初期表示ビュー
        defaultView: 'month',
        //スケジュール取得
        events : './getSchedule'
    });
    
    //イベント登録送信(新規登録と編集で送信先変更)
    $('#submit').click(function() {
        if ($(':input[name="id"]').val() != '') {
            $(this).parents('form').attr('action', './updateSchedule');
        } else {
            $(this).parents('form').attr('action', './addSchedule');
        }
        $(this).parents('form').submit();
    });
    
    //削除ボタンをチェックしたらそれ以下は入力不可に
    $(':input[name="deleted"]').change(function() {
        if($(this).is(':checked')) {
            $('.inputArea :input').prop('disabled', true);
        } else {
            $('.inputArea :input').prop('disabled', false);
        }
    });
    
    //終日ボタンをチェックしたらそれ以下は入力不可に
    $(':input[name="allDay"]').change(function() {
        if($(this).is(':checked')) {
            $('.timeArea :input').prop('disabled', true);
        } else {
            $('.timeArea :input').prop('disabled', false);
        }
    });
});

/*
 * フォームの値をセット
 */
function setFormData(date, dataArr) {
    
    //データがない時は初期値セット
    if (dataArr == undefined) {
        var dataArr = {
            'startHour' : 0,
            'startMinute' : 0,
            'endHour' : 0,
            'endMinute' : 0,
            'title' : '',
            'id' : '',
            'status' : 0,
            'allDay' : false,
            'deleteArea' : 'hidden'
        };
    }
    //各データをフォームにセット
    $(':input[name="date"]').val(date);
    $(':input[name="startHour"]').val(dataArr.startHour);
    $(':input[name="startMinute"]').val(dataArr.startMinute);
    $(':input[name="endHour"]').val(dataArr.endHour);
    $(':input[name="endMinute"]').val(dataArr.endMinute);
    $(':input[name="title"]').val(dataArr.title);
    $(':input[name="id"]').val(dataArr.id);
    $(':input[name="status"]').val(dataArr.status);
    
    $(':input[name="deleted"]').prop('checked', false);
    $('.inputArea :input').prop('disabled', false);
    
    //イベント更新時は削除フォームを表示
    if (dataArr.deleteArea == 'show') {
        $('#deleteArea').show();
    } else {
        $('#deleteArea').hide();
    }
    
    //終日の場合は時間の選択不可
    $(':input[name="allDay"]').prop('checked', true);
        $('.timeArea :input').prop('disabled', true);
    if(dataArr.allDay == true) {
        $(':input[name="allDay"]').prop('checked', true);
        $('.timeArea :input').prop('disabled', true);
    } else {
        $(':input[name="allDay"]').prop('checked', false);
        $('.timeArea :input').prop('disabled', false);
    }
    
}

解説

indexを表示すると
fullcadenderのオプションで設定した

events : './getSchedule'

でイベントデータをDBから引っ張ってきてカレンダーに反映させます。

※イベント登録
カレンダー上の日付をクリックすると、dayClickのfunctionが呼ばれます。
初期値をセットして、フォームが書かれたdivをポップアップ表示します。
こちらはイベントの更新時にも使用するフォームのため、idのあるなしでフォームの送信先をかえています。

※イベント更新・削除
カレンダー上に表示しているイベントをクリックすると、eventClickのfunctionが呼ばれます。
もろもろフォームにセットしてポップアップ表示。
送信するとphpからDBの更新をしてくれます。

なんか色々とっ散らかってる感じが満載ですが、今回はこんなもんでご勘弁下さい。
またいずれ時が来たら修正しますので。。。(逃)

このエントリーをはてなブックマークに追加

きんが最近書いた記事

WRITERS POSTS もっと見る

他にもこんな記事が読まれています!

  • WEB
  • マーケティング
  • サーバー・ネットワーク
  • ライフスタイル
  • お知らせ