作成日:2021/04/12 更新日:2022/01/27

#1 PHPとjsで簡単なアプリを作ってみよう

今回はこのようなアプリを作ってみたいと思います。
自分で登録した名前をdiv要素化して、その要素をJavaScriptドラッグして配置場所を保存しておくというアプリです。 ドラッグされた要素は、PHPでxy座標をデータベースに保存させます。

PHPの準備

phpファイルはhtmlファイルと同じだと考えて大丈夫です。 拡張子をindex.htmlではなくindex.phpにしてマークアップします。 イメージとしてはhtml文書を作って、その中にphpの機能を追記していく感じになります。 サイトの機能が複雑になると「MVC(Model View Controller)」という考え方になり、 機能(PHP)と見栄え(html+css)を分けて書く事になります。
今回は理解度を優先させるために、機能を全てindex.phpに記述します。

構造体を作る

まずは以下の文章をコピーして、index.phpとcssを作ってください。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>sortable</title>
  <link href="css/style.css" rel="stylesheet">
</head>
<body>
<div id="wrapper">

<div id="drag-area">
drag area
</div>

</div>
</body>
</html>

/*reset*/
body,div,p{margin:0;}
.clearfix:after {
    content:"";
    display:block;
    clear:both;
}

/*frame*/
body {
  height:100%;
  color:#ccc;
  font-size:10px;
}
#wrapper {
  border: 2px dashed #ccc;
  margin:15px;
  border-radius: 6px;
  height: 600px;
}

/*drag*/
#drag-area {
  position: relative;
  height: 500px;
  width:980px;
  margin: 5px auto;
  border: 1px solid #333;
  border-radius: 6px;
}
.drag {
  position: absolute;
  padding: 1em;
  color: #666;
  border-radius: 5px;
  background: #efefef;
  border: 3px solid #ccc;
}
.name {
  font-size:12px;
}
簡単なhtml文書とcssですね。(めんどうのでresetの重要なものだけ上部に入れました)cssは1枚でいきます。 どのセレクタがどんな役割を持っているのか、文章を見ただけで把握しましょう。
ディレクトリはこんな感じです。
アドレス(localhost:8001)を入力するとブラウザではこんな表示になると思います。

#2 PHPのエラーをブラウザに表示させる

PHPでは間違った記述をしているときに、エラーを表示してくれる機能がありますので、表示させる内容を明示しておきます。 内容を確認し、修正できるようにしましょう。

<?php
ini_set('display_errors', 1);  /* MAMPの設定次第では記述が必要 1は表示、0は非表示*/
error_reporting(-1);  /* 0は表示させない、-1はすべて表示 */
?>
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
</head>
<body>
<?php
$name = '大橋太郎';
$measage = 'こんにちは!';

echo $names . 'さん' . $measages . '
'; ?> </body> </html>
エラーが表示されます。どこが間違っているのか、表示される内容を読み解き、解決してみましょう。
 Notice: Undefined variableアン ディファインド バリアブル: = 注意: 未定義の変数です
 Parse error: syntax errorシンタックス エラー, = 解析エラー: 構文エラーです
 Fatal error: Call to undefined functionコール トゥー アンディファインド ファンクション = 致命的なエラー: 未定義の関数を呼び出してます
 Warning: Missing argumentミッシング アーギュメント = 警告: 引数がありません
英語で表示されますが、翻訳ソフトなどを利用し積極的に読み解いていくようにしましょう。
ブラウザクリックで翻訳できるアドオン(Chrome版 Firefox版もあります) S3.Translator

▼解説
表示する/非表示にする

PHPのエラーを画面に表示するように設定

MAMPのphp.iniの「display_errors = 」がOFFの場合、以下の記述でPHP書類上で上書きします。 引数「1」は表示、 「0」は表示させない

ini_set('display_errors', 1);

出力するエラーの内容設定

error_reporting(引数); 引数→ 0=オフ、E_ALL=全て表示、E_ERROR=重大なエラー、E_DEPRECATED=非推奨コード など

error_reporting(E_ALL);
error_reporting(E_ALL & ~ E_DEPRECATED & ~ E_USER_DEPRECATED & ~ E_NOTICE);

文字化け対策

PDOの使い方は後ほど

$options = array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SET CHARACTER SET 'utf8'");
エラー表示の部分は、本番環境では削除しましょう。

エラーログの確認

MAMP内にphp_error.logがあるので、stgフォルダにエイリアスを作っておくと、 エラーlogから確認することもできます。
※エラーログに表示される日付を日本時間にしましょう。
アプリケーション > MAMP > bin > php > [MAMPで指定したphpバージョン] > conf > php.ini
date.timezoneで検索(910行目あたり)の

[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
;date.timezone = "Europe/Berlin" /*Asia/Tokyoになっている場合はコメントアウトを外すのみ*/
↓
date.timezone = "Asia/Tokyo"
と変更しMAMPを再起動ます。

エラーログの書き出し

Webサーバやローカル開発環境で、エラーログファイルを書き出して確認する方法です。

if($_SERVER["HTTP_HOST"] === 'creativeresourceinstitute.com'){  /*ドメイン名*/
  ini_set('error_log', $_SERVER['DOCUMENT_ROOT'].'/../../error.log');  /*サーバ上のファイルを書き出してほしい場所*/
}else{
  ini_set('error_log', $_SERVER['DOCUMENT_ROOT'].'/../error.log');  /*ローカルのファイル場所が違う場合*/
}
if文で、ローカル環境と公開サーバ用と切り分けます。
指定の場所にエラーログが出力されるので便利です。

#3 データベースに接続する

データベースに接続するための前準備

接続設定の定例文を書いてみましょう。

<?php
error_reporting(-1);

/* データベース設定 */
define('DB_DNS', 'mysql:host=localhost; dbname=cri_sortable; charset=utf8');
define('DB_USER', 'root');
define('DB_PASSWORD', 'root');
?>
まずは定数を作ります。定数とは、変数に対するもので、 変数とは“中身が変えられる数”ですが、定数は一度設定すると中身を変えることはできません。

使い方は、define('定数名', '定数に入れる値');
 define('DB_DSN', '接続先種別:host=ローカルホスト; dbname=データベースの名前; charset=文字コード');
 define('DB_USER', 'ユーザー名');
 define('DB_PASSWORD', 'パスワード');
※ID, PWの「root」はMAMPに入っているMySQLの初期設定値です。

データベースに接続する前にデータベースの情報を入れておきます。 接続先名やユーザーIDやパスワードは基本的には変わらないのでdefine()関数で定義してあげます。 例えるなら、FTPを設定する時にアプリの画面でホスト名やユーザID、パスワードを登録したのと同じようなことです
define()関数は、定数(変わらないもの)を定義(決める)する関数です。
defineで作られた定数はグローバル定数となります。
グローバル定数とは、変わらないもの、上書きできないもの。プログラムのどこからでも呼び出せるもの、になります。
※DNS は Data Source Name の略。

#4 データベースのデータをhtmlで表示させる

まずデータベースに接続する

接続の定例文を書いてみましょう。

<?php
/* データベース設定 */
define('DB_DNS', 'mysql:host=localhost; dbname=cri_sortable; charset=utf8');
define('DB_USER', 'root');
define('DB_PASSWORD', 'root');

/* データベースへ接続 */
try {
  $dbh = new PDO(DB_DNS, DB_USER, DB_PASSWORD);
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
} catch (PDOException $e){
    echo $e->getMessage();
    exit;
}
?>
データベースに接続するときにはtry catch構文で、PDOクラスをnewする(PDOインスタンスを生成)というこの構文を思い出しましょう。※PDOは「PHP Data Objects」の略。
dbhはデータベースハンドルの略で、ハンドルは車のハンドルをイメージしてください。データベースを運転できる人という感じです。

▼解説
解説は一度目を通し、仕組みをなんとなく把握できたら忘れても構いません。
表示する/非表示にする

try catch構文(例外処理)

ある処理と、エラー時の処理を実行できる構文

try {
    /*例外が発生するおそれがあるコード*/
} catch(例外クラス名 例外を受け取る変数名){
    /*例外発生時の処理*/
}

データベース接続処理

①まずデータベースにログインする情報をPDOオブジェクトとして変数 $dbh に格納。
new演算子:クラス(型)からインスタンス(実体)を生成
つまり、PHPで準備されているDB接続クラスである「PDO」を使って、今回の接続する情報を$dbhに代入している。

$dbh = new PDO(DB_DNS, DB_USER, DB_PASSWORD);
②接続時のオプションを記述します。

/*アロー演算子(->)を使って変数雨や関数を呼び出します。*/
$インスタンスを代入した変数 -> 呼び出したいインスタンスの、プロパティ(変数)またはメソッド(関数)名

/*属性をセットする*/
setAttribute( );

/* デフォルトのエラー発生時の処理 Exception の形で例外を投げる */
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

/* パフォーマンス向上のためプリペアドステートメント機能を使う設定。*/
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
プリペアド(準備された)ステートメント(文章)とは簡単に言えば、あらかじめ型を作っておき、中に入れるデータは安全を確かめて型に流し込むフィルターのようなものです。 直接データを型に流し込むより安全性が保たれます。
またオプションについては下の書き方Bのように、
new PDO(DB_DNS, DB_USER, DB_PASSWORD, $option);として、連想配列で値を渡すこともできます。

/*書き方A*/
$dbh = new PDO(DB_DNS, DB_USER, DB_PASSWORD);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

/*書き方B*/
$dbh = new PDO(DB_DNS, DB_USER, DB_PASSWORD, $option);
$option = [
  PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  PDO::ATTR_EMULATE_PREPARES => false
]

メッセージを出力

echoで結果(エラー内容)をhtmlに出力します

echo $e->getMessage();

終了

メッセージを出力し、現在のスクリプトを終了する

exit;
setAttribute();の中に書いてある属性に関しては、まだ理解できなくても良いです。 色んな種類があるので、やりたい処理に合わせて調べてきましょう。 今回のはエラーモードに関する記述と、MySQLのある機能を使うか使わないかを書いてあります。

接続のときの基本的な記述内容になるので、別のアプリを作ったときも同じ構文になります。

データベースのデータ内容をブラウザで確認する


<div id="drag-area">
<?php
$sql = 'SELECT * FROM sortable';
$stmt = $dbh->query($sql);
$result = $stmt->fetch(PDO::FETCH_ASSOC);

print_r ($result);  /* print_rはechoのようなもので、配列の中身を出力できる*/
?>
</div>
<div id="drag-area">内に、データベースに接続して取ってきたデータを出力しましょう。 結果はこうなります。

Array
(
  [id] => 1
  [name] => 新垣結衣
  [left_x] => 810
  [top_y] => 56
)
きちんと、データが取れていることが確認できます。
▼解説
表示する/非表示にする
■MySQLで使えるSQL文(SQLステートメント)の SELECT * FROM sortable> という文字列を、変数$sqlに格納

$sql = 'select * from sortable';
■PDOオブジェクトの$dbhから、queryメソッド" query( ) "で SQLステートメント" $sql "を実行して、 PDOStatementオブジェクトを取得" -> "し、変数$stmtに格納

$stmt = $dbh->query( $sql );
$stmt = $dbh->query( 'select * from sortable' );  /*このように書いても同様*/
■PDO fetchメソッドを実行

/* 1行ずつ取得 = 配列(whileでループ)*/
$result = $stmt->fetch(PDO::FETCH_ASSOC);
/* 全データを配列に変換 = 多次元配列(foreachでループ)*/
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
↓
/* 配列なので、foreachを使おう */
foreach ($stmt as $result){
  echo $result['添字']
}
■結果を表示

print_r($result);
4つとも変数の内容を出力するための関数です。(echoは関数ではない)

print_r ($result);       /* 配列が出力される */
ただ、実際のhtml内にデータを入れ込むには、tableなのか、div, pタグなのか、 とにかく要素(ラッパー)の中にデータを入れていかなければなりません。
▼用語説明
stmt  (ステートメント:文章 の略語)
PDO::FETCH_ASSOC  (カラム名で添字を付けた配列を返す)

データベースのデータをhtml構文でレイアウト


<div id="drag-area">
<?php
$sql = 'SELECT * FROM sortable';
$stmt = $dbh->query($sql)->fetchAll(PDO::FETCH_ASSOC);

/* while ($result = $stmt->fetch(PDO::FETCH_ASSOC)){ while文だとこのような書き方*/
/* 配列なので、foreach文を使ってみよう */
foreach ($stmt as $result){
  echo '  <div class="drag" data-num="'.$result['id'].'" style="left:'.$result['left_x'].'px; top:'.$result['top_y'].'px;">'.PHP_EOL;
  echo '    <p><span class="name">'.$result['id'].' '.$result['name'].'</span></p>'.PHP_EOL;
  echo '  </div>'.PHP_EOL;
}
?>
</div>
PHP_EOL(End Of Line)は、ソース上で改行されるPHPの定義済み定数です。ドットで繋いで文末に入れて使用します。 上記は、<div id="drag-area">内に、データベース内の情報を、↓このように埋め込みたい場合のやり方になります。

<div id="drag-area">
  <div class="drag" data-num="1" style="left:810px; top:56px;">
    <p><span class="name">1 新垣結衣</span></p>
  </div>
  <div class="drag" data-num="2" style="left:631px; top:302px;">
    <p><span class="name">2 北川景子</span></p>
  </div>
  <div class="drag" data-num="3" style="left:788px; top:326px;">
    <p><span class="name">3 佐々木希</span></p>
  </div>
  ︙
</div>
ここでの「1」「新垣結衣」「810」「56」の部分が、データベースのレコードに格納されているデータになります。 たくさんレコード(行)があれば、その行が終わるまで、テンプレートに沿って データだけが違う、たくさんのdivが自動でそれが何百行でも生成されます。

以下のdivの中でデータが変わる箇所【 】に変数を入れます。
<div class="drag" data-num="【ID】" style="left:【x座標】px; top:【y座標】px;">
 <p><span class="name">【ID】 【名前】</span></p>
<div>

'data-num="1"'
↓
'data-num="' .$result['id']. '"'
文字列は' '(シングルクオーテーション)に入れ、変数は .(ドット)で繋いでいきます。
DBカラム名中のデータPDO出力
id1$result['id']
name新垣結衣$result['name']
left_x810$result['left_x']
top_y56$result['top_y']

そうするとこのような表示になると思います。

ここまでのindex.php

<?php
error_reporting(-1);

/* データベース設定 */
define('DB_DNS', 'mysql:host=localhost; dbname=cri_sortable; charset=utf8');
define('DB_USER', 'root');
define('DB_PASSWORD', 'root');

/* データベースへ接続 */
try {
  $dbh = new PDO(DB_DNS, DB_USER, DB_PASSWORD);
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
} catch (PDOException $e){
    echo $e->getMessage();
    exit;
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>8001-cri-sortable</title>
  <link href="css/style.css" rel="stylesheet">
</head>
<body>
<div id="wrapper">

<div id="drag-area">
<?php
$sql = 'SELECT * FROM sortable';
$stmt = $dbh->query($sql)->fetchAll(PDO::FETCH_ASSOC);
foreach ($stmt as $result){
  echo '  <div class="drag" data-num="'.$result['id'].'" style="left:'.$result['left_x'].'px; top:'.$result['top_y'].'px;">'.PHP_EOL;
  echo '    <p><span class="name">'.$result['id'].' '.$result['name'].'</span></p>'.PHP_EOL;
  echo '  </div>'.PHP_EOL;
}
?>
</div>

</div>
</body>
</html>