20200101のPHPに関する記事は8件です。

【PHP】PHPの型宣言は弱い型付け

型宣言とは

次の例のように、関数に引数を与えるような場合などに引数の型に制約を与えることができます。
正しくない種類の型が渡された場合、Fatal errorが発生します。

<?php
function includeTax(int $price, bool $take_out = true) {
    if ($take_out) {
        return $price * 1.08;
    } else {
        return $price * 1.1;
    }
}

includeTax(100, false); // 100
includeTax('税抜100円', false); // Fatal error: Uncaught TypeError: Argument 1 passed to includeTax() must be of the type int, string given

上記の関数inculdeTaxの引数$priceint型を指定しているため、正しい型である100を渡すことに成功しましたが、string型である税抜100円を渡した時にエラーを吐いています。

型宣言として使用有効な型は、以下の通りです。
スクリーンショット 2020-01-01 20.59.55.png

引用 PHPマニュアル > 言語リファレンス > 関数 > 型宣言

PHPの型宣言は厳格ではない

しかし、スカラー型1の型宣言の場合にはには落とし穴があります。
スカラー型の型宣言は弱い型付けなので、一致しない型が引数と渡されたとして一致させることができる場合暗黙の型変換がされます。

具体的には以下のような例のばあいです。

<?php
function includeTax(int $price, bool $take_out = true) {
    // $priceは(int)100に変換される。
    if ($take_out) {
        return $price * 1.08;
    } else {
        return $price * 1.1;
    }
}

// string型で渡す。
includeTax('100', false); // 100
// float型で渡す。
includeTax(100.5, false); // 100

どちらも、int型に変換できる形だったので、関数に渡した時にint型に黙って変換されて処理が行われました。
特に、後者のfloat型で渡したときには、開発者は110.55が返ってくることを期待しましたが、別の結果が帰ってきたことに困惑するかもしれません。

この挙動は、返り値の型宣言やプロパティ型指定2でも同様です。

弱い型付けを無効にするには

厳格な型付けを望みたい場合、ファイルの先頭でdeclare(strict_types = 1)を宣言します。

<?php
declare(strict_types = 1);

function includeTax(int $price, bool $take_out = true) {
    if ($take_out) {
        return $price * 1.08;
    } else {
        return $price * 1.1;
    }
}

includeTax('100', false); // Uncaught TypeError: Argument 1 passed to includeTax() must be of the type int, string given
includeTax(100.5, false); // Uncaught TypeError: Argument 1 passed to includeTax() must be of the type int, string given

しかし、この厳格な型付けは全体には適応できません。厳格な型付けを望みたい個々のファイルごとに宣言する必要があります。

参考文献

PHPマニュアル
はじめてのPHP
【導入決定!】PHP7で実装されるスカラー型宣言とは?


  1. 論理値 (boolean)整数 (integer)浮動小数点数 (float, double)文字列 (string)の4種類。 

  2. プロパティ型指定は7.4からの新機能。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Htmlspecialcharsでいたずら(XSS)を防いでみた

前回のあらすじ

・if文を使ってアンケート内容に不備があったら、注意文を表示させたり進むボタンが出ないようにさせたりしたぞ

今日やったこと

・$変数=htmlspecialchars(変数);
と入力したら、アンケート内にHTMLタグとか仕込んでみても(これをクロスサイトスクリプティングと言うらしい)文字列として処理されるようになった!
(今回の教科書『いきなりはじめるPHP ワクワク・ドキドキの入門教室』)

感想

とにかく、入力情報の無毒化はセキュリティ対策で必要な処理なんだなーと思った

次回の目標

データベースにアンケート情報を保管できるようにしよう!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPの学習でアンケートシステム作ってるなう

前回のあらすじ

・ローカル鯖構築(XAMPP3.2.4にて)
・前のページからデータを受け取る作業を学んだ
・if文で入力に不備があったらお知らせ文を表示させてみた

今日やったこと

・if文を利用して、アンケートに空欄があったら進むボタンが表示できない設定にした
・history.backを使って戻るボタンを作成したら、入力した内容を記憶させたまま入力フォームに戻ることができた
(参考教科書『いきなりはじめるPHP ワクワク・ドキドキの入門教室』)

感想

if文って便利!入力する項目が多くなったら、フラグ制御というものを使うらしいけど・・・後々学べるのかしら

次回の目標

引き続き、入力するプログラムをしっかり見つつ、理解していこう

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アンケートシステムでif文学んだ

前回のあらすじ

・ローカル鯖構築(XAMPP3.2.4にて)
・前のページからデータを受け取る作業を学んだ
・if文で入力に不備があったらお知らせ文を表示させてみた

今日やったこと

・if文を利用して、アンケートに空欄があったら進むボタンが表示できない設定にした
・history.backを使って戻るボタンを作成したら、入力した内容を記憶させたまま入力フォームに戻ることができた
(参考教科書『いきなりはじめるPHP ワクワク・ドキドキの入門教室』)

感想

if文って便利!入力する項目が多くなったら、フラグ制御というものを使うらしいけど・・・後々学べるのかしら

次回の目標

引き続き、入力するプログラムをしっかり見つつ、理解していこう

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Heroku masterへの道 buildpack

Dockerは、まんまOSそのものから構築できる。
Buidpackは、OSが入った状態の後から構築できる。

ので、Buidpackだとconfigをあれこれして、起動すればkusanagiに近づけるはず。
で、やってみた。
起動の流れとしては、

Procfile -> app_boot.sh -> nginx みたいな感じで、

中身は、

Procfile
web: sh support/app_boot.sh

app_boot.sh
vendor/bin/heroku-php-nginx \
-c support/nginx.conf.php \
-C support/nginx.inc.conf \
-f support/php-fpm.conf \
-F support/php-fpm.inc.conf \
-i support/php.ini \
public.built/

な感じ。このオプションと、コンフィグのどこまで書いて良いの?が公式サイト内には見つからなかった。
で、オフィシャルのBuidpackをガチ読み。あった。

が、下記

正月から頑張った。まだ頑張らないと如何が頑張った。うむ。

https://github.com/heroku/heroku-buildpack-php/blob/master/bin/heroku-hhvm-nginx

| Options: |
|--:||:--|
|  |  |   -C <nginx.inc.conf>     The path to the configuration file to include inside |
|  |  |                           the Nginx server config (see option -c below). Will |
|  |  |                           be included inside the 'server { ... }' block just |
|  |  |                           after the 'listen', 'root' etc directives. |
|  |  |                           Recommended approach when customizing Nginx's config |
|  |  |                           in most cases, unless you need to set http or |
|  |  |                           fundamental server level options. |
|  |  |                           [default: <BPDIR>/conf/nginx/default_include.conf.php, |
|  |  |                           or a more version-specific file from a subdirectory] |
|  |  |   -c <nginx.conf>         The path to the full configuration file that is |
|  |  |                           included after Heroku's main Nginx config has been |
|  |  |                           loaded. It must contain an 'http { ... }' block with a |
|  |  |                           'server { ... }' inside that contains 'listen' and |
|  |  |                           'root' (see option -C above) directives. |
|  |  |                           [default: <BPDIR>/conf/nginx/heroku.conf.php, |
|  |  |                           or a more version-specific file from a subdirectory] |
|  |  |   -h, --help              Display this help screen and exit. |
|  |  |   -I <php.extra.ini>      The path to an extra php.ini to use in addition to the |
|  |  |                           default HHVM php.ini (see option -i below). |
|  |  |   -i <php.ini>            The path to the php.ini file to use. It is highly |
|  |  |                           recommended to use the -I option (see above) instead. |
|  |  |                           [default: <BPDIR>/conf/hhvm/php.ini.php, |
|  |  |                           or a more version-specific file from a subdirectory] |
|  |  |   -l <tailme.log>         Path to additional log file to tail to STDERR so its |
|  |  |                           contents appear in 'heroku logs'. If the file does not |
|  |  |                           exist, it will be created. Wildcards are allowed, but |
|  |  |                           must be quoted and must match already existing files. |
|  |  |                           Note: this option can be repeated multiple times. |
|  |  |   -p <PORT>               Port to listen on for HTTP traffic. If this argument |
|  |  |                           is not given, then the port number to use is read from |
|  |  |                           the \$PORT environment variable, or a random port is |
|  |  |                           chosen if that variable does not exist. |
|  |  |   -v, --verbose           Be more verbose during startup. |
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP+SQLite3】恐竜登録ツール2

前回の記事の内容を実装してみた。

共通処理のプログラム

commom_dino.php
<?php 
//
function Checkdino($db0,$qry0,$val1,$type1)
{
    $flg=0;
    $result3=$db0->prepare($qry0);
    //パラメータをセット
    $result3->bindparam(1,$val1);
    $result3->bindparam(2,$type1);
    $result3->execute();
    //検索結果を配列に格納する
    $select_data=$result3->fetch();
    return $select_data;
}
//クエリ実行して得られる全データ
function get_AllData($db0,$query0)
{
    $result_a=$db0->prepare($query0);
    $result_a->execute();
    return $result_a;
}
//$str1:検索対象文字列,$str_mark:探したい文字: 戻り値、探したい文字より前にある文字列
function get_BeforeMarkString($str1,$str_mark)
{
    $idx0=strpos($str1, $str_mark);
    $retstr=substr($str1,0,$idx0);
    return $retstr;
}

//$str1:検索対象文字列,$str_mark:探したい文字、探したい文字より後にある文字列
function get_AfterMarkString($str1,$str_mark)
{
    $idx0=strpos($str1, $str_mark);
    $retstr=substr($str1,$idx0+1);
    return $retstr;
}
 ?> 

開始画面のコード

start_dino.php
<!DOCUTYPE html>
<html lang="ja">
<meta charset="utf-8">
<title>恐竜登録にようこそ</title>
<h1>開始</h1>
<form action="select_dinotype1.php" method="post">

<?php 
require 'common_dino.php';
$select_all = "select count(*) from tbl_dino";
$db_name='dino_collect.db';
$ext=file_exists($db_name);
//恐竜情報テーブル作成
$query_c="CREATE TABLE tbl_dino(id integer primary key autoincrement,dname0 text,type0 text,n_src1 integer,type1 text,n_src2 integer,type2 text)";
$ret0 = 0;
//dbが存在しない時は
if(!$ext){
    $ret0 = 0;
    $db1 = new PDO('sqlite:'.$db_name);
    $result_a=$db1->prepare($query_c);
    $result_a->execute();

}else{
    //データベースを開く
    $db1 = new PDO('sqlite:'.$db_name);
    $result_a=get_AllData($db1,$select_all);
    //$count
    $count = $result_a->fetchColumn();
    $ret0=$count;

}

 ?>
<!—- 開始ボタンと一緒に、登録件数を送る ->
<input type="hidden" name="btn1" value="<?=$ret0?>">
<input type="submit" value="開始">
</form>
</html>

恐竜のタイプ(ハイブリッドか非ハイブリッド)を選択する画面

  • 登録件数が3体未満だと、非ハイブリッドの登録画面になる。
  • 3体以上の場合、非ハイブリッドかハイブリッドを選べる。
select_dinotype1.php
<!DOCUTYPE html>
<html lang="ja">
<meta charset="utf-8">
<title>恐竜タイプ選択1</title>
<body>
<?php 
if(isset($_POST["btn1"])){
    $ret0=$_POST["btn1"];
    echo "<h1>恐竜タイプ選択1</h1>";
}
 ?>

<?php if($ret0<3){ ?>
<form action="single_dino1.php" method="post">
<input type="text" name="dname0">
カテゴリー:
<select name="dtype0">
<option>normal</option>
<option>rare</option>
<option>epic</option>
</select>
<input type="submit" value="確定" name="btn2">
</form>

<?php }else{ 

require "common_dino.php";
$db_name='dino_collect.db';
$db1 = new PDO('sqlite:'.$db_name);
$query_type0 = "SELECT DISTINCT type0 from tbl_dino";

?>
<form action="select_dinotype2.php" method="post">
カテゴリー:
<select name="l_category">
<option>ハイブリッド</option>
<option>非ハイブリッド</option>
</select><br>
生成元タイプ1:(ハイブリッド型は必須)
<?php $result_a=get_AllData($db1,$query_type0); ?>  
<select name="s_type1">
<?php while($select_data=$result_a->fetch(PDO::FETCH_ASSOC)){ ?>
<option><?php echo $select_data['type0']; ?></option>
<?php } ?>
</select><br>

生成元タイプ2:(ハイブリッド型は必須)
<?php $result_a=get_AllData($db1,$query_type0); ?>  
<select name="s_type2">
<?php while($select_data2=$result_a->fetch(PDO::FETCH_ASSOC)){ ?>
<option><?php echo $select_data2['type0']; ?></option>
<?php } ?>
</select><br>

<input type="submit" value="確定" name="btn2">
</form>

<?php   } ?>

<form action="start_dino.php" method="post">
<input type="submit" value="戻る" name="btn3">
</form>

</body>
</html>

恐竜の名前とタイプの決定画面(3体以上登録時)

ハイブリッド選択時と非ハイブリッド選択時で画面項目の数は
異なるが、最終的に生成する恐竜名とタイプを決定する部分は同じ。

select_dinotype2.php
<!DOCUTYPE html>
<html lang="ja">
<meta charset="utf-8">
<title>恐竜タイプ選択2</title>
<h1>恐竜タイプ選択2</h1>

<?php 
$db_name='dino_collect.db';
$db1 = new PDO('sqlite:'.$db_name);

$type0=isset($_POST['l_category'])?htmlspecialchars($_POST['l_category']):null;;
$type1=isset($_POST['s_type1'])?htmlspecialchars($_POST['s_type1']):null;;
$type2=isset($_POST['s_type2'])?htmlspecialchars($_POST['s_type2']):null;;
if(strcmp($type0,"非ハイブリッド")==0){ ?>
<form action="single_dino1.php" method="post">
<input type="text" name="dname0">
カテゴリー:
<select name="dtype0">
<option>normal</option>
<option>rare</option>
<option>epic</option>
</select>

<input type="submit" value="確定" name="btn2">
</form>

<?php 
}else{

    $query_all="SELECT dname0,type0 FROM tbl_dino WHERE  type0 = ?";

function get_typedata($db0,$query0,$val1)
{
    $result_a=$db0->prepare($query0);
    $result_a->bindparam(1,$val1);
    $result_a->execute();
    return $result_a;
}   
 ?>
<form action="hibrid_dino1.php" method="post">
<input type="text" name="hdname0">

カテゴリー:
<select name="dtype0">
<option>rare</option>
<option>epic</option>
<option>regend</option>
<option>unique</option>
</select><br>
<?php $result_a=get_typedata($db1,$query_all,$type1); ?>
生成元1:
<select name="src1">
<?php while($select_data=$result_a->fetch(PDO::FETCH_ASSOC)){ ?>
<option><?php echo $select_data['dname0']; ?>:<?php echo $select_data['type0']; ?></option>
<?php } ?>
</select><br>

<?php $result_a=get_typedata($db1,$query_all,$type2); ?>
生成元2:
<select name="src2">
<?php while($select_data2=$result_a->fetch(PDO::FETCH_ASSOC)){ ?>
<option><?php echo $select_data2['dname0']; ?>:<?php echo $select_data2['type0']; ?></option>
<?php } ?>
</select>

<input type="submit" value="確定" name="btn2">
</form>

<?php }
    require 'common_dino.php';
    $select_all = "select count(*) from tbl_dino";
    $result_a=get_AllData($db1,$select_all);
    //$count
    $count = $result_a->fetchColumn();
    $ret0=$count;

 ?>
<form action="select_dinotype1.php" method="post">
<input type="hidden" name="btn1" value="<?=$ret0?>">
<input type="submit" value="戻る">
</form>

</html>

非ハイブリッド登録結果

single_dino1.php
<!DOCUTYPE html>
<html lang="ja">
<meta charset="utf-8">
<title>恐竜シングル</title>
<h1>恐竜シングル結果</h1>

<?php 
require 'common_dino.php';
$db_name='dino_collect.db';
$db1 = new PDO('sqlite:'.$db_name);

$dtype1=isset($_POST['dtype0'])?htmlspecialchars($_POST['dtype0']):null;
$dname1=isset($_POST['dname0'])?htmlspecialchars($_POST['dname0']):null;
$query_ins1="INSERT INTO tbl_dino(dname0,type0,n_src1,n_src2) VALUES(?,?,?,?)";
$select_all = "select count(*) from tbl_dino";
$result_a=get_AllData($db1,$select_all);

//登録件数
$count = $result_a->fetchColumn();
$total_dino=$count;

//echo $dname1.":".$dtype1;
$query_select1="SELECT * FROM tbl_dino WHERE dname0 = ? AND type0 = ?";
//検索文字列、置換文字列,検索対象文字列の順番
$dname2=trim(str_replace(" "," ",$dname1));
if(isset($dname1) && empty($dname2) == false){
    //すでに登録済みの恐竜か否かチェックする
    $select_data=Checkdino($db1,$query_select1,trim($dname2),$dtype1);
    if($select_data){
        echo "登録済みです";

    }else{
        $result1=$db1->prepare($query_ins1);
        //パラメータをセット
        $result1->bindparam(1,$dname2);
        $result1->bindparam(2,$dtype1);
        //合成なしの場合、合成元恐竜がいないため
        $num0=0;
        $result1->bindparam(3,$num0);
        $result1->bindparam(4,$num0);
        $result1->execute();
        echo trim($dname2).":".$dtype1."の登録が完了しました";
    }
}else{
    echo "値が不正です";
}
 ?>
<form action="select_dinotype1.php" method="post">
<input type="hidden" name="btn1" value="<?=$total_dino?>">
<input type="submit" value="戻る">
</form>
</html>

ハイブリッド恐竜の登録結果

hibrid_dino1.php
<!DOCUTYPE html>
<html lang="ja">
<meta charset="utf-8">
<title>ハイブリッド恐竜登録結果</title>
<h1>ハイブリッド恐竜登録結果</h1>

<?php 
require 'common_dino.php';
$db_name='dino_collect.db';
$db1 = new PDO('sqlite:'.$db_name);
$hdname1=isset($_POST['hdname0'])?htmlspecialchars($_POST['hdname0']):null;
$dtype1=isset($_POST['dtype0'])?htmlspecialchars($_POST['dtype0']):null;
$srctype1=isset($_POST['src1'])?htmlspecialchars($_POST['src1']):null;
$srctype2=isset($_POST['src2'])?htmlspecialchars($_POST['src2']):null;
$query_ins2="INSERT INTO tbl_dino(dname0,type0,n_src1,type1,n_src2,type2) VALUES(?,?,?,?,?,?)";

//登録件数
$select_all = "select count(*) from tbl_dino";
$result_a=get_AllData($db1,$select_all);
    //登録件数
$count = $result_a->fetchColumn();
$total_dino=$count;

$query_select1="SELECT * FROM tbl_dino WHERE dname0 = ? and type0 = ?";
//全角の空白を半角スペースに変換 
$hdname2=trim(str_replace(" "," ",$hdname1));

if(isset($hdname1) && empty($hdname2) == false){
    if(strcmp($srctype1,$srctype2)==0){
        echo "生成元1と生成元2は違う種類を選んでください。";
    }else{
    //すでに登録済みの恐竜か否かチェックする
        $select_data0=Checkdino($db1,$query_select1,trim($hdname2),$dtype1);

        if($select_data0){
            echo "登録済みです";
        }else{
            //生成元タイプ1と生成元恐竜1に分ける。
            $src1=get_BeforeMarkString($srctype1,":");
            $stype1=get_AfterMarkString($srctype1,":");
            //生成元タイプ2と生成元恐竜2に分ける。
            $src2=get_BeforeMarkString($srctype2,":");
            $stype2=get_AfterMarkString($srctype2,":");  
            $select_data1=Checkdino($db1,$query_select1,$src1,$stype1);
            //生成元1の恐竜番号を取得
            $num_src1=$select_data1['id'];
            $select_data2=Checkdino($db1,$query_select1,$src2,$stype2);
            //生成元2の恐竜番号を取得
            $num_src2=$select_data2['id'];
            //登録クエリ   
            $result1=$db1->prepare($query_ins2);

            //パラメータをセット
            $result1->bindparam(1,$hdname2);
            $result1->bindparam(2,$dtype1);
            $result1->bindparam(3,$num_src1);
            $result1->bindparam(4,$stype1);
            $result1->bindparam(5,$num_src2);
            $result1->bindparam(6,$stype2);
            $result1->execute();
            echo $hdname2.":".$dtype1."を登録しました";
        }
    }
}else{
    echo "名前が不正です";
}

 ?>
<form action="select_dinotype1.php" method="post">
<input type="hidden" name="btn1" value="<?=$total_dino?>">
<input type="submit" value="戻る">
</form>
</html>

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel5.8+fullcalendar v4+ajaxでDBと接続しeventを表示・操作

前置き

Fullcalendarをlaravelで実装している記事がネット上に少なかったように感じたので紹介します。

フロント側だけでのイベント操作は、ほとんどコード書かなくてもできますが、それだけではページを更新するたびにリセットされてしまいます。したがって今記事では、ajaxでデータを受送信し、DBの値を取得・操作してみます。

具体的には
・DBから値を取得してカレンダーにイベントを表示
・日付をクリックするとイベント追加
・イベントをドラックアンドドロップで日付変更
を実現します。

スクリーンショット 2020-01-01 0.43.04.png
↑完成イメージ↑

環境

環境を紹介

各バージョン

OS : macOS Catalina 10.15.1
PHP : 7.3.1
mysql : Ver 14.14 Distrib 5.6.42, for osx10.14 (x86_64) using EditLine wrapper
Laravel : 5.8.35
Fullcalendar : v4

Fullcalendarはバージョンが違うと動かないので気をつけてください。ネット上はにいろんな記事が落ちてると思いますが、まず最初にバージョンを確認するといいです。

データベース設計

今記事で扱うデータベースの設計です。

2019_12_30_144053_create_events_table.php
class CreateEventsTable extends Migration
{
    public function up()
    {
        Schema::create('events', function (Blueprint $table) {
            $table->string('event_id', 34)->primary();
            //僕はuuidにしてます
            $table->date('date');
            $table->string('title');
            $table->timestamps();
        });
    }
    public function down()
    {
        Schema::dropIfExists('events');
    }
}

今回はid,title,dateと最小限のカラムだけ用意しました。

js側のeventObjectのstart,endはそれぞれイベントの開始時間と終了時間を示しますが、今回、内容を簡潔化するために
・日付のみで、時間のデータは持たない
・日付は1日のみ。複数日を跨がない
ため、js側のeventObjectからはstartだけを受け取り、dateとしてテーブルに入れます。

具体的にはそれぞれ
・event_id => EV20e0e2cde562b0c8cdfad8b975dab7c6
・date => 2019-12-22
・title => 友達と遊ぶ
といったデータを想定しています。

手順

ファイル読み込み

まずfullcalendarを読み込みたいと思います。

いくつかやり方はありますが、zipファイルをダウンロードしてlaravelプロジェクトに追加する方法でやりました。
下のリンクの"Download"ってところからファイルを解凍してください。
https://fullcalendar.io/docs/getting-started

いろいろファイルがありますが、必要なのはpackage下の各ディレクトリのmain.jsとmain.cssです。(cssファイルはディレクトリによってはないこともある??)

daygridとかinteraction,timegridみたいのはプラグインの名前です。みなさんが使いたいものを選んで読み込んでください。
https://fullcalendar.io/docs/plugin-index

僕は今回、coreに加えてdaygrid,interactionを読み込みました。
概要は以下です。
core →Calendarクラスを提供
daygrid →カレンダーを月別か日別に表示
interaction → dateClick, selectableアクションが使え、eventをドラックアンドドロップ、リサイズができる

僕はこんな感じで設置しました。
スクリーンショット 2019-12-28 4.13.56.png

んでbladeファイルの方でこれらを読み込んで行きます。

resources/views/layouts/app.blade.php
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

<script src='/js/fullcalendar/core/main.js'></script>
<script src='/js/fullcalendar/daygrid/main.js'></script>
<script src='/js/fullcalendar/interaction/main.js'></script>

<script src="/js/ajax-setup.js"></script>
<script src='/js/fullcalendar.js'></script>
<script src='/js/event-control.js'></script>
ここ上の3個はあとで使います。ファイルを作成した後、あらかじめ読み込んでおきます。

<link href='/css/fullcalendar/core/main.css' type="text/css" rel='stylesheet' />
<link href='/css/fullcalendar/daygrid/main.css' type="text/css" rel='stylesheet' />

カレンダー表示まで

んで次に任意のbladeファイルを用意して、そこに書いてください。viewはこれだけで大丈夫です。

calendar.blade.php
@extends('layouts.app')
@section('content')
    <div id="calendar"></div>
@endsection

次にjsです。さっきapp.blade.phpに読み込んだファイルに、カレンダーの設定等を書き込みます。
scriptタグで囲ってあげて、calendar.blade.phpの下に書いてもいいと思います。

public/js/fullcalendar.js
document.addEventListener('DOMContentLoaded', function() {
    var calendarEl = document.getElementById('calendar');

    var calendar = new FullCalendar.Calendar(calendarEl, {
        plugins: [ 'interaction', 'dayGrid' ],
        //プラグイン読み込み
        defaultView: 'dayGridMonth',
        //カレンダーを月ごとに表示
        editable: true,
        //イベント編集
        firstDay : 1,
        //秋の始まりを設定。1→月曜日。defaultは0(日曜日)
        eventDurationEditable : false,
        //イベントの期間変更
        selectLongPressDelay:0,
        // スマホでタップしたとき即反応
        events: [
            {
                title: 'イベント',
                start: '2019-01-01'
            }
        ],
        //一旦イベントのサンプルを表示。動作確認用。

        eventDrop: function(info){
        //eventをドラッグしたときの処理
             //editEventDate(info);
            //あとで使う関数
        },

        dateClick: function(info) {
        //日付をクリックしたときの処理
            //addEvent(calendar,info);
            //あとで使う関数
        },
    });
    calendar.render();
});

今回使用しているプロパティは一部です。他にもたくさんあるのでドキュメンや別の記事を見るといいです。
用意した関数の中に、これから処理を書いていって、ajax通信をします。
ここまででひとまずフロント側は動きました。

DBの値からeventを表示

web.php
Route::get('/setEvents', 'EventController@setEvents')

setEventsというURLにいくとEventController@setEventsが動くようrouteを設定しました。

public/js/fullcalendar.js
        events: "/setEvents";
        // eventObjectsを取得するJSONフィードのURLを指定

先ほど書いたサンプルデータではなく、上のようにDBの値を呼び出すためのURLに書き換えます。

EventController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Event;

class EventController extends Controller
{
    public function setEvents(Request $request){

        $start = $this->formatDate($request->all()['start']);
        $end = $this->formatDate($request->all()['end']);
        //表示した月のカレンダーの始まりの日を終わりの日をそれぞれ取得。

        $events = Event::select('id', 'title', 'date')->whereBetween('date', [$start, $end])->get();
        //カレンダーの期間内のイベントを取得

        $newArr = [];
        foreach($events as $item){
            $newItem["id"] = $item["event_id"];
            $newItem["title"] = $item["title"];
            $newItem["start"] = $item["date"];
            $newArr[] = $newItem;
        }
        //新たな配列を用意し、 EventsObjectが対応している配列にキーの名前を変更する

        echo json_encode($newArr);
        //json形式にして出力
    }

    public function formatDate($date){
        return str_replace('T00:00:00+09:00', '', $date);
    }
    // "2019-12-12T00:00:00+09:00"のようなデータを今回のDBに合うように"2019-12-12"に整形

}

ドキュメント( https://fullcalendar.io/docs/events-json-feed )を日本語訳すると、以下のような説明があります。

FullCalendarは、新しいイベントデータが必要になるたびにURLにアクセスします。これは、ユーザーがprev / nextをクリックするか、ビューを変更すると発生します。FullCalendarは、イベントが必要な日付範囲を決定し、GETパラメーターでその情報を渡します。

今回のコードでは、"/setEvents?start=2013-12-01T00:00:00-05:00&end=2014-01-12T00:00:00-05:00"といったURLにアクセスします。ですので、controllerのアクションのRequestで、カレンダーの月別の始まりと終わりの日付が受け取れます。

これでデータベースの値を表示できました。

event操作

ajaxでCSRF保護をしてあげないと419 (unknown status)になっちゃいます。laravelのドキュメントに載ってるのをまんま書きます。
https://readouble.com/laravel/5.8/ja/csrf.html

public/js/ajax-setup.js
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});

次に、fullcalendar.jsにて、もうすでに関数を書いておいたので、それらをコメントアウトを解除して、その中身を書きます。

public/js/event-control.js
function addEvent(calendar,info){
    //addEvent()を使うためにfullcalendar.jsで定義したcalendarを引数で受け取る

    var title = "サンプルイベント";
    //ホントはjsでformのvalue取得とかするんだと思いますが、説明を簡潔にするために割愛します。
    $.ajax({
        url: '/ajax/addEvent',
        type: 'POST',
        dataTape: 'json',
        data:{
            "title":title,
            "date":info.dateStr
            //日程取得
        }
    }).done(function(result) {
        calendar.addEvent({
            id:result['event_id'],
            //php側から受け取ったevent_idをeventObjectのidにセット
            title:title,
            start: info.dateStr,
        });
        //ajaxに成功したらフロント側にeventを追加で表示
    });
}

function editEventDate(info){
    var event_id = info.event.id;
    var date = formatDate(info.event.start);

    $.ajax({
        url: '/ajax/editEventDate',
        type: 'POST',
        data:{
            "id":event_id,
            "newDate":date
            //ドロップしたあとの日付をphp側に渡す
        }
    })
}

function formatDate(date) {
    var year = date.getFullYear();
    var month = date.getMonth() + 1;
    var day = date.getDate();
    var newDate = year + '-' + month + '-' + day;
    return newDate;
}
//info.event.startの日付を"2019-12-12"のように整形する関数

使用したプロパティ、関数のドキュメントです。
eventClick → https://fullcalendar.io/docs/eventClick
dateClick → https://fullcalendar.io/docs/dateClick
addEvent() → https://fullcalendar.io/docs/Calendar-addEvent

web.php
Route::post('/ajax/addEvent', 'EventController@addEvent');
Route::post('/ajax/editEventDate', 'EventController@editEventDate');

各ajaxのroute設定をします。

EventController.php
public function addEvent(Request $request)
    {
        $data = $request->all();
        $event = new Event();
        $event->event_id = $this->generateId();
        //僕はmodel.phpでuuidを作成する関数を書いていましたが、みなさんはご自由に。
        $event->date = $data['date'];
        $event->title = $data['title'];
        $event->save();

        return response()->json(['event_id' => $event->event_id ]);
    }
    // ajaxで受け取ったデータをデータベースに追加し、今度はidを返す。

    public function editEventDate(Request $request){
        $data = $request->all();
        $event = Event::find($data['id']);
        $event->date = $data['newDate'];
        $event->save();
        return null;
    }
    // ajaxで受け取ったデータからデータベースの日付データを変更。

これで終わりです

参考

https://tech.arms-soft.co.jp/entry/2017/02/14/154000
https://teratail.com/questions/189446
https://github.com/oclean66/prolygon
https://teratail.com/questions/170934
https://qiita.com/SOJO/items/bb24e7d09320ea96cfc3

終わりに

いろいろググったりしましたが、結局一番ドキュメントを読みました。ある程度人の記事を読んで概要を掴んだらドキュメントをたくさん読んで自分の作りたいもの
fullcalendarのv3とv4で結構違うっぽいので気をつけてください。

自分が実際に書いたコードを記事ように整形しただけなので、厳密に動作確認できてるわけじゃないので、変なところや間違ってたりしたらコメントください。
ちなみに自分の奴はこれです (https://github.com/shlia34/MyTraining )

やる気になるんで、いいねとかストックもください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP 配列まとめ

目的

  • PHPで配列に値を格納して取り出す方法をまとめる

書き方の例

  • 配列に格納し任意の値を出力する方法を記載する。
$変数A = array("値A", "値B", "値C");

//値Aを出力
echo $変数A[0];

//値Bを出力
echo $変数A[1];

//値Cを出力
echo $変数A[2];

//配列の末尾に値Dを追加するしたい時
$変数A[] = "値D";

//値Aを値Eで上書きたいとき
$変数A[0] = "値E";
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む