CakePHPという言葉を聞いた事があるでしょうか?
こちらのページを読んでみようと思っていらっしゃる方は多分どんなものかわかっていらっしゃるかもしれません。
当記事ではCakePHPについて入門編から
ちょっとしたアプリが開発できるくらいまでをカバーし解説してゆきたいと思います。
興味を持たれた方はよろしくお付き合いくださいませ。
Contents
フレームワークはどこが便利?
以前から日本で流行っていたPHPのウェブフレームワークであるCakePHPですが、どのような点が便利なのでしょうか。
実際のところフレームワークがあるとWEBの開発がはかどります。
WEBの開発は決まった処理というものがあります。ウェブでよく行われる「GETクエリーを取り出してそれをキーにデータを取り出す」等、といった定型の処理をかくのに簡単にかけたらそれ以上のことはありません。
フレームワークは決まりきった定型の処理を簡単に処理するために様々な機能を提供してくれます。
プログラムを作るのに初心者の方や入門者の方が書くと冗長であったり、セキュリティがいまいちだったり、分かりにくかったり、修正しにくかったりしてしまいます。
しかしフレームワークという大枠があることにより、初心者の方でも分かりやすく修正しやすく様々な欠点を作りにくいプログラム開発がしやすくなります。
それはプログラムの作成のプロが大枠を作ってくれているからで、フレームワークという部品を使うことによりこのような様々なメリットを得ることができます。
CakePHPはMVCフレームワーク
MVCフレームワークという言葉を聞いたことがあるでしょうか?WEBはページを描画するのが主です。
ページを描画するための機能を役割ごとに分担して3つに分け設計するという考え方を用いたのがMVCです。
MVCはそれぞれ Model , View ,Controllerの略で以下のような役割があります
Model | データベースのデータ形式などデータ管理を司ります |
View | ページの描画テンプレートなどがこれにあたり、画面の描画を担当します |
Controller | プログラム全体の制御などを担当します。 |
このように3つに分けることによりプログラムが分かりやすく扱いやすくなります。
CakePHPは設定より規約
CakePHPでは設定より規約を重視します。
あらかじめ名前命名規則などのルールを設けることにより設定を最小限にしてページアクセスの際に呼び出されるファイルなどを決定していきます。
これにより煩わしい設定を減らすことができるのでルールさえ分かっていればかなり分かりやすいプログラミングが可能となります。
開発に必要なもの
何はなくともWEBサーバープログラムとPHPが必要です。
必要に応じてSQLサーバもあるとよいでしょう。
開発のためにはすべてオールインワンで揃えられるxamppというオールインワンサーバ環境が世の中にはあるのでそれを使うとそろえるのが楽です。
こちらの講座ではxamppを使い環境を用意して開発を始めることにしましょう。
またCakeでプロジェクトを作っていくにあたり、Composerと呼ばれるパッケージ管理ソフトが必要になってきます。
composerは初回インストールの時に使うほかバージョンアップなどにも使えるそうですが、初回にインストールを行う方法を今回はご紹介します。
サーバとブラウザの関係について
ところでサーバとブラウザの関係については大まかにご存じでしょうか。
WEBの仕組みはまずクライアントであるブラウザがサーバにリクエストと呼ばれる要求を投げかけることから始まります。
以下のような流れで処理が行われていきます。
またhtmlとはホームページの骨格ファイルでホームページ記述言語で書かれているテキストファイルです。
このような事も何らかの形でWEBに携わる人間としては覚えておいたほうが良いことでしょう。
各種インストール
まずはオールインワンサーバ環境であるXamppのインストールから始めましょう。
Xamppのインストール
以下のアドレスよりxamppが入手できます。ダウンロードよりWindowsを選んでください。
https://www.apachefriends.org/index.html
ダウンロードしてできたファイルをダブルクリックすると以下のような画面が現れる場合があります。
これはウイルス対策ソフトウェアを使っている場合に動作が遅くなったり不具合が発生する場合があるよという警告です。
問題ない場合も結構あるので問題が起こらない以上はそのまま続けて構いません。
It seems you have an antivirus running. In some cases, this may slow down or interfere the installation of the software. Please visit the following link to learn more about this.
http://apachefriends.org/en/faq-xampp-windows.html#antivirus
Continue with installation?
次に次のようなメッセージが続けて出る場合があります。
これはユーザアカウント制御機能により、書き込み権限などが制限されプログラムに問題がでる可能性を示しています。C:\Program Filesのフォルダを避けるとよいとの事なので今回はこちらのフォルダに入れていきます。
C:\xampp\
Warning
—————————
Important! Because an activated User Account Control (UAC) on your system some functions of XAMPP are possibly restricted. With UAC please avoid to install XAMPP to C:\Program Files (missing write permisssions). Or deactivate UAC with msconfig after this setup.
次にインストールするコンポーネントを選びます。
上記のようにほとんどはデフォルトでインストールされますので特に希望がなければデフォルトのままでかまいません。
インストールするパスを入力します。当連載ではC:\xampp\にインストールしている前提で話を進めていきますので特に希望がなければ、こちらに合わせると混乱しないで済むことでしょう。
インストールする言語を選びます。ドイツ語が選べますが、Englishが無難でしょう。
Bitnamiは宣伝のようです(?)チェックを外しましょう。
準備が完了したようです。ネクストを押しましょう。
インストールが行われます。
インストール後ソフトウェア(サーバー)が起動されると以下のメッセージが表示される事があります。
以下の画面ではファイアーウォールの設定を行うことにより、接続のブロックを回避できます。
設定を行わないと接続がブロックされてしまう時があります。
基本はプライベートネットワークにチェックをつけてアクセスを許可すればよいでしょう。
セットアップが完了すると以下の画面がでます。
PHP動作確認
インストールが完了したところで一度PHPがきちんと動くか確認しておきましょう。
Windowsキー+Rを同時押しして(Windowsキーを長くおしてその後にRキーを押す)で出てくる画面、「ファイル名を指定して実行」画面で、入力欄にcmdと打ちます。
黒い画面が出ますのでphp -vと入力してください。これでPHPのバージョンが出てくれば問題ありません。PHPは無事動いています。
ここでPHPが動いていない方も多いでしょう。
デフォルトでは動かない可能性が高いです。
その場合環境変数のPATHを設定する事でPHPにパスが通り実行できるようになりますのでご安心ください。
Windowsキー + Breakキーを同時に押します。
すると以下のような画面が出ますので
「システムの詳細設定」→「環境変数」→「Pathをクリック」→「編集」により一番下のスペースに
C:\xampp\php
の行を追加します。
これでOKを連打して押していき画面を閉じていきます。
もう一度「cmd」の黒い画面を開きphp -vと入力しましょう。
バージョンは表示されましたか?表示されればオーケーです。
表示されなければ何かが間違っていますので手順をもう一度確認してみましょう。
Composerのインストール
次に以下のサイトよりcomposerをダウンロードします。
Downloadよりダウンロード画面を開き Composer-Setup.exeをダウンロードします。
ダウンロードしたファイルをダブルクリックして開きインストールを進めてください。
ALL USERを選択
ディベロッパーモードは無しでNEXT
PHPのパスを設定
Proxyは特に必要なければ空欄で設定
Installボタンを押す
重要とありますが一応Finish後コンピュータを再起動(リブート)しておきましょう
Finishで終えましょう。その後コンピュータの再起動するとよいでしょう。
CakePHPのインストール
インストールが完了したらいよいよcomposerでCakePHPの新規プロジェクトをスタートさせます。
そのためには以下のコマンドで新規Cakeプログラムををダウンロードしてセットアップします。
cd C:\xampp\htdocs
composer create-project --prefer-dist cakephp/app myapp
これを行うとほとんどの人は以下のメッセージがでて止まるでしょう。
– cakephp/cakephp[4.2.0, …, 4.2.2] require ext-intl * -> it is missing from your system. Install or enable PHP’s intl extension.
– Root composer.json requires cakephp/cakephp ~4.2.0 -> satisfiable by cakephp/cakephp[4.2.0, 4.2.1, 4.2.2].To enable extensions, verify that they are enabled in your .ini files:
– C:\xampp\php\php.ini
You can also run `php –ini` inside terminal to see which files are used by PHP in CLI mode.
これを回避するにはメッセージにある通りC:\xampp\php\php.iniを編集してintl拡張を有効にする必要があります。
C:\xampp\php\php.iniをメモ帳やテキストエディタなどで開いてください。
次に「Ctrl+F」でintlを検索すると以下の行が見つかります。
;extension=intl
この行の先頭の;を削除して保存します。この行がない場合は
;;;;;;;;;;;;;;;;;;;;;;
; Dynamic Extensions ;
;;;;;;;;;;;;;;;;;;;;;;
の少し下部のextensionが並んでいる付近に
extension=intl
を追加してください。そして保存します。
cd C:\xampp\htdocs
composer create-project --prefer-dist cakephp/app myapp
こちらを再度実行すると以下のメッセージが表示されることがあります。
これは一度このコマンドを実行して停止しているためにセットアップフォルダ(C:\xampp\htdocs\myapp)ができてしまっているためです。このフォルダを削除してもう一度実行します。
Project directory “C:\xampp\htdocs\myapp” is not empty.
気を取り直して
cd C:\xampp\htdocs
composer create-project --prefer-dist cakephp/app myapp
を再度実行すると今度は無事長々と処理が走ると思います。
この処理はダウンロードなどで、セットアップが完了すると停止します。
これで準備は整いました。実際にサーバーを起動してみましょう。
サーバーの起動
C:\xampp\xampp-control.exeをダブルクリックか何かの方法で起動します。
出てくる画面でApacheの横のStartボタンを押します。
するとApacheが起動します。
ここでポートがすでに使われているというエラーが起こることがありますが、WindowsのWEBサーバであるIISや、通話ソフトのスカイプ等が(httpのポート)80番ポートを使用している事が原因です。
これらのプログラムを停止させましょう。そして再度Startを実行すれば無事Apacheが80番ポートで待ち受けしてくれます。
CakePHPの動作確認
でCakeを起動するとプログラムが走り結果がブラウザに返されます。
これは動作確認用のページになります。
こちらの画面で様々な必要部品がきちんと動作しているか確認できるので一つ一つ問題ないかみていきましょう。
いよいよCakeのクッキング
Cakeの作成準備はできました。ではCakeプログラムを作ってみましょう。Cakeに最低限必要なものはコントローラです。Viewはなくてもとりあえず動かしてみる事はできます。ですがコントローラはないといけません。
まず最初に
myapp/src/Controller/ArticlesController.php
にArticlesControllerを置いてみましょう。
<?php
//パス:myapp/src/Controller/ArticlesController.php
namespace App\Controller;
use App\Controller\AppController;
class ArticlesController extends AppController
{
public $autoRender = false;
public function index()
{
echo "<html><body>hello world.</body></html>";
}
}
これで
http://localhost/myapp/articles/index
でこのコントローラのアクションを呼び出すことができます。
これがCakePHPの重要な点で「設定よりも規約」というルールに従って作られているからこそなせる業です。
コントローラを置く場所(ファイルパスや命名規則)が決まっていてさらには
http://loalhost/アプリへのパス/コントローラ名/アクション名
というルールからコントローラを決定しそのアクションまでアクセスできるのです。
上記のArticlesController.phpのコードは一般化すると以下のような記述方式になっています。
<?php
namespace App\Controller;
use App\Controller\AppController;
class コントローラ名Controller extends AppController
{
public $autoRender = false;
public function アクション名()
{
echo "<html><body>hello world.</body></html>";
}
}
このルールに従うことによりこのアドレスにアクセスしたときはこのアクションと決定する事ができるという訳です。
CakePHPの基本理念にかかわる大切な内容です。ぜひ覚えておいてください。
なお $autoRender はViewの自動描画をオフにするための設定です。
これをしないとViewテンプレートが存在しないというエラーが出てしまいます。
これを避けるためにこの設定を行っています。
Viewテンプレートを用意した場合は全く不要の設定です。
なおコントローラ名やアクション名にも命名規則があり、コントローラ名は先頭を大文字、残りを小文字とし、複数の単語からなる場合は各単語の先頭を大文字にするというルールがあります。
MyNewPageControllerのような感じです。
アクションは先頭を小文字、後の単語の先頭を大文字のローワーキャメルケース(キャメルバック)と呼ばれる記法を使います。myPageViewのような感じです。
テンプレートを使ってみよう
テンプレートは大きく分けてレイアウトテンプレートとビューテンプレートがあります。
レイアウトテンプレートからビューテンプレートを呼び出して使うようになっていて、ビューテンプレートはアクション毎に一つ用意します。
ビューテンプレートはアクションによって切り替わる部分と覚えておきましょう。
レイアウトテンプレートは大枠になっています。
ではコードです。
<?php
//パス:myapp/src/Controller/ArticlesController.php
namespace App\Controller;
use App\Controller\AppController;
class ArticlesController extends AppController
{
public function index()
{
$this->set("title","Hello World.");
$this->set("message","最初のテンプレートプログラムです。");
}
}
//パス:myapp\templates\Articles\index.php
<h1 style="padding: 0.4em 0.5em;color: #494949;background: #f4f4f4;border-left: solid 5px #7db4e6;border-bottom: solid 3px #d7d7d7;">
<?= $title ?>
</h1>
<p>
<?= $message ?>
</p>
注目すべきは二か所、$this->set()メソッドはコントローラからビューテンプレートに変数や文字などを渡してビューで使えるようにするための指定です。
これでビューに変数を渡すことにより初めてビューでその変数が使えるようになります。
またindex.phpテンプレートで<?= $title ?>としているところはphpのショートタグで$titleをそのまま出力するという意味です。
これは<?php echo $message; ?>とも書けます。
これらのコードを以下のアドレスで実行するとコントローラからビューへデータが渡り以下のような画面になります。
それほど難しいところはないかと思います。
http://localhost/myapp/articles/index
なお上記のアドレスはデフォルトのindexアクションを用いていますので省略して以下のようにも書けます
http://localhost/myapp/articles
またウェブページのソースコードを見てみると以下のようになっています。下に続く
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>
CakePHP: the rapid development php framework:
Articles </title>
<link href="/myapp/favicon.ico" type="image/x-icon" rel="icon"/><link href="/myapp/favicon.ico" type="image/x-icon" rel="shortcut icon"/>
<link href="https://fonts.googleapis.com/css?family=Raleway:400,700" rel="stylesheet">
<link rel="stylesheet" href="/myapp/css/normalize.min.css"/>
<link rel="stylesheet" href="/myapp/css/milligram.min.css"/>
<link rel="stylesheet" href="/myapp/css/cake.css"/>
</head>
<body>
<nav class="top-nav">
<div class="top-nav-title">
<a href="/myapp/"><span>Cake</span>PHP</a>
</div>
<div class="top-nav-links">
<a target="_blank" rel="noopener" href="https://book.cakephp.org/4/">Documentation</a>
<a target="_blank" rel="noopener" href="https://api.cakephp.org/">API</a>
</div>
</nav>
<main class="main">
<div class="container">
<h1 style="padding: 0.4em 0.5em;color: #494949;background: #f4f4f4;border-left: solid 5px #7db4e6;border-bottom: solid 3px #d7d7d7;">
Hello World.
</h1>
<p>
最初のテンプレートプログラムです。
</p>
</div>
</main>
<footer>
</footer>
<script id="__debug_kit" data-id="5510a5c3-b006-47f5-8d48-871a52cffda5" data-url="http://localhost/myapp/" src="/myapp/debug_kit/js/toolbar.js?1608506846"></script></body>
</html>
なぜか長くなっていますし、書いていないはずのhtmlタグや、headタグ、bodyタグなどが見られます。
これはデフォルトのレイアウトテンプレートが適応されているからです。
よくみると以下のようにindex.phpで指定しているビューテンプレート部分が埋め込まれている事がわかります。(下記のように)
<div class="container">
<h1 style="padding: 0.4em 0.5em;color: #494949;background: #f4f4f4;border-left: solid 5px #7db4e6;border-bottom: solid 3px #d7d7d7;">
Hello World.
</h1>
<p>
最初のテンプレートプログラムです。
</p>
</div>
レイアウトテンプレートはどこにあるのでしょうか。答えは
C:\xampp\htdocs\myapp\templates\layout\default.php
です。中身をみてみましょう。
各メソッドなどコードになっている違いはありますが、以下のように概ね同じ構造になっているのがわかるかと思います。
このようにレイアウトテンプレートから<?= $this->fetch(‘content’) ?>によりビューテンプレートが呼び出されて全体のhtmlが生成されます。このほかエレメントと呼ばれる小さい部品を呼び出す事もできます。
<?php
$cakeDescription = 'CakePHP: the rapid development php framework';
?>
<!DOCTYPE html>
<html>
<head>
<?= $this->Html->charset() ?>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>
<?= $cakeDescription ?>:
<?= $this->fetch('title') ?>
</title>
<?= $this->Html->meta('icon') ?>
<link href="https://fonts.googleapis.com/css?family=Raleway:400,700" rel="stylesheet">
<?= $this->Html->css(['normalize.min', 'milligram.min', 'cake']) ?>
<?= $this->fetch('meta') ?>
<?= $this->fetch('css') ?>
<?= $this->fetch('script') ?>
</head>
<body>
<nav class="top-nav">
<div class="top-nav-title">
<a href="<?= $this->Url->build('/') ?>"><span>Cake</span>PHP</a>
</div>
<div class="top-nav-links">
<a target="_blank" rel="noopener" href="https://book.cakephp.org/4/">Documentation</a>
<a target="_blank" rel="noopener" href="https://api.cakephp.org/">API</a>
</div>
</nav>
<main class="main">
<div class="container">
<?= $this->Flash->render() ?>
<?= $this->fetch('content') ?>
</div>
</main>
<footer>
</footer>
</body>
</html>
ビューやレイアウトに関する簡単な説明はこれで終わりにします。
次はいよいよユーザからの入力の処理に取り掛かりたいと思います。
GETクエリー(GETメソッドによるデータの受け渡し)
HTTPではユーザーがデータを入力する方式としてGETとPOSTと呼ばれるものが存在します。
POSTは標準入力にGETはアドレスの一部としてサーバーに渡される違いがあるのですが、GETは比較的短いデータに向いています。
アドレスの一部としてデータを入力する事から比較的分かりやすく扱いやすいという利点があります。
このGETによる入力をパラメータとして検索エンジンの挙動を変えたり、ウェブサイトの動作を様々に変えたりすることができます。
なんだかいまいちわからない方も実際に使ってみましょう。
ああこういう事かと納得できると思います。先ほどのテンプレートとコントローラを改造していきます。
<?php
//パス:myapp/src/Controller/ArticlesController.php
namespace App\Controller;
use App\Controller\AppController;
class ArticlesController extends AppController
{
public function index()
{
//GETクエリーでデータを取得し変数に入れる
$id = $this->request->getQuery("UserID");
$pass = $this->request->getQuery("Password");
//ビューテンプレートにデータを渡す
$this->set("title","Hello World.");
$this->set("id",$id);
$this->set("pass",$pass);
}
}
//パス:myapp\templates\Articles\index.php
<h1 style="padding: 0.4em 0.5em;color: #494949;background: #f4f4f4;border-left: solid 5px #7db4e6;border-bottom: solid 3px #d7d7d7;">
<?= $title ?>
</h1>
<p>
<?= "あなたのIDは". $id . "ですね<br>" ?>
<?= "あなたのパスは" . $pass . "のようです" ?>
</p>
以下のアドレスでアクセスします。
Password=とUserID=が見てとれます。上記のコードの
$this->request->getQuery("UserID");
などは=の後の文字列を取り出せるという機構です。
GETクエリーではクエリー部とファイルへのパス部を?でまず区切り、その後、各クエリーを&で区切り、クエリーのキーと値は=で分けます。キーが先にきて後に値という感じですね。
例を挙げると今回のアドレス↓
http://localhost/myapp/Articles/index?UserID=Alexa&Password=newPassword
以下のようになりましたね。
ユーザからの入力をプログラムで受け取って、そのまま表示させる例です。
ただし後でも説明しますがユーザの入力をそのまま表示させるというのは本当はプログラムの欠陥(脆弱性)になるのでよくありません。
あくまでシンプルに解説するためのコードなので、そのことは頭の片隅においておいてください。
なおこの連載ではこれらのユーザ入力の無害化(サニタイズ)は省略する事があります。
実際のウェブアプリ作成ではユーザの入力を処理する場合には、必ず入力チェック、つまりサニタイズを行い、処理に有害な文字が含まれていないかの確認を心がけてください。
ユーザの入力は信用してはならず常に汚染されていると考えるのがセーフティだそうです。
とはいえ、これで一応ユーザからの入力を受けとることができました。色々な場面で役立つので覚えておいてください。
フォームでユーザ入力を受け取ろう
ウェブアプリといえば入力フォームですね。
ここからは入力フォームを用いてユーザ入力を受け取る方法をご紹介していきます。
<?php
//パス:myapp/src/Controller/ArticlesController.php
namespace App\Controller;
use App\Controller\AppController;
class ArticlesController extends AppController
{
public function index()
{
//ビューテンプレートにデータを渡す
$this->set("title","Hello World.");
}
public function form()
{
$email = $this->request->getData("email");
$name = $this->request->getData("name");
$nickname = $this->request->getData("nickname");
$PersonData = [
"name" => $name,
"email"=> $email,
"nickname" => $nickname
];
$this->set("PersonData",$PersonData);
$this->set("title","フォームサンプル");
}
}
//パス:myapp\templates\Articles\index.php
<h1 style="padding: 0.4em 0.5em;color: #494949;background: #f4f4f4;border-left: solid 5px #7db4e6;border-bottom: solid 3px #d7d7d7;">
<?= $title ?>
</h1>
<p>
<form method="post" action="/myapp/articles/form">
<input type="text" name="name">
<input type="text" name="email">
<input type="text" name="nickname">
<input type="hidden" name="_csrfToken" autocomplete="off" value="<?= $this->request->getAttribute('csrfToken') ?>">
<input type="submit" name="submitButton">
</form>
</p>
//パス:myapp\templates\Articles\form.php
<h1 style="padding: 0.4em 0.5em;color: #494949;background: #f4f4f4;border-left: solid 5px #7db4e6;border-bottom: solid 3px #d7d7d7;">
<?= $title ?>
</h1>
<p>
<?= $PersonData["name"] . "さんこんにちは。" . "emailは" . $PersonData["email"] . "ですね。" . $PersonData["nickname"] ."さんと呼ばせていただきます。"; ?>
</p>
http://localhost/myapp/Articles/index
まずindex.phpのビューテンプレートをみてみましょう。フォームの出力命令である
<form method="post" action="/myapp/articles/form">
<input type="text" name="name">
<input type="text" name="email">
<input type="text" name="nickname">
<input type="hidden" name="_csrfToken" autocomplete="off" value="<?= $this->request->getAttribute('csrfToken') ?>">
<input type="submit" name="submitButton">
</form>
が見て取れます。これはHTMLの該当箇所にフォームを出力してくださいという命令です。
“action=”がパス+articlesコントローラ+formアクションとなっていますね。
これはフォームの送信先を示しています
それからinputタグがいくつかあります。nameというフィールドやemailフィールド,nicknameフィールド等があります。
それぞれメールアドレスなどを入力する欄です。
見慣れないcsrfTokenというフィールドがありますがCSRFの脆弱性対策に必要なものなので、必要だからあるけれども、今は無視してかまいません。セキュリティ対策でこれがないとページがエラーを吐きます。
最後にボタンがあります。
このページから項目を入力して送信すると今度はarticlesコントローラのformアクションのform.phpテンプレートが呼び出されてそのまま入力がやまびこのように戻ってくるという感じでメッセージが出力されます。
どうやって実現しているかというとarticlesコントローラのformアクションに
$nickname = $this->request->getData("nickname");
などといった記述があります。送信されたデータはformタグの指定によりformアクションに渡されてformアクションでは、getData(“nickname”)メソッドがnicknameと名されたフィールドのデータを戻すので$nickname変数にこのデータが入ります。
それをビューに渡して表示させているだけです。
なお$this->requestはオブジェクトでユーザからのリクエストを集中管理して保存しておく変数です。
ここにリクエスト関係の様々な情報が保管されます。
ちょっと分かりにくいでしょうか?流れとしては以下のようになります
↓
フィールドにデータ入力
↓
フィールドをformアクションに送信
↓
formアクションでデータを受け取る
↓
formアクション用のビューテンプレートに
入力されたデータを渡す
↓
ビューテンプレートは
渡されたデータを元に表示(やまびこメッセージ)
ところでここではユーザの入力をそのまま表示していました。ということはどんな文字もそのままやまびこのように戻ってくるということです。ここで名前に「<span style=”font-size:100px;”>太郎</span>」と入力するとどうなるでしょうか
以下のようになったと思います。
文字が大きくなっていますね。これはhtmlのタグが有効になってしまっているからでユーザが入力したタグがフォントサイズを変えるだけのものであればまだよいのですが、Scriptタグなどを入力されるとスクリプトが実行されXSS(クロスサイトスクリプティング)というセキュリティの欠陥になります。
前述もしましたがユーザの入力はこのように予期せぬセキュリティ上の欠陥につながりますのでそのまま使用してはいけないのです。ではこの場合はどうすればよいのでしょうか?この場合であればユーザの入力からタグを取り除けばよいことになります。
それって簡単なの?という疑問がのこることでしょう。PHPやCakeにはそのような時のための機能が備わっています。
それを使えば割と簡単にタグを取り除くことができますよ。
タグをタグじゃない安全な形式に変換することをエスケープするともいいます。
タグのエスケープの方法は以下の通りです。
h($Hensuu)
hメソッドでくくるだけですね。簡単です。
ではform.phpテンプレートを書き換えてみましょう。
//パス:myapp\templates\Articles\form.php
<h1 style="padding: 0.4em 0.5em;color: #494949;background: #f4f4f4;border-left: solid 5px #7db4e6;border-bottom: solid 3px #d7d7d7;">
<?= $title ?>
</h1>
<p>
<?= h($PersonData["name"]) . "さんこんにちは。" . "emailは" . h($PersonData["email"]) . "ですね。" . h($PersonData["nickname"]) ."さんと呼ばせていただきます。"; ?>
</p>
再度アクセスすると今度はどうでしょうか?タグのかっこがそのまま表示されていますね
またhtmlのソースコードはブラウザでソースコードを表示したい画面を右クリックして「ソースを表示」で見れますがこの方法でhtmlのソースコードを見てみると、エスケープされた文字列は以下のようになっています。
<span style="font-size:100px;">太郎</span>
<が<になっています。>は>ですね。
このように変換(エスケープ)することでタグとしての効果をなくしただの文字として表示されるようになります。
フォームヘルパーを使おう
前回はフォームをタグで手入力しましたが様々な機能が足りていません。
例えば入力したデータを保持してデータを表示する機能、前回の方法ではフォームがクリアされてしまうため何かの時にちょっと不便ですが、この保持機能を自分でつけると色々と書くことが増えて大変です。
フォームを作るうえでフォームヘルパーを使うと何かと便利なので使ってみましょう。
フォームヘルパーはビューテンプレート内で以下のようなメソッドを使うことで使用できます。
$this->Form->メソッド(引数);
これらのメソッドはタグを出力するためのものなので実際の利用は
<?= $this->Form->メソッド(引数); ?>
のように、メソッドの戻り値をphpの出力としてそのまま書き出せばよいです
ではこれらのフォームヘルパーを使っていきましょう。
//パス:myapp\templates\Articles\index.php
<h1 style="padding: 0.4em 0.5em;color: #494949;background: #f4f4f4;border-left: solid 5px #7db4e6;border-bottom: solid 3px #d7d7d7;">
<?= $title ?>
</h1>
<div><pre><?php print_r($PersonData) ?></pre></div>
<p>
<?= $this->Form->create(null,["type" => "post","url" => ["controller" => "articles","action" => "index"]]); ?>
<?= $this->Form->control('PersonForm.name', ['type' => 'text', 'label' => 'NAME:']); ?>
<?= $this->Form->control('PersonForm.age', ['type' => 'text', 'label' => 'AGE:']); ?>
<?= $this->Form->control('PersonForm.nickname', ['type' => 'text', 'label' => 'NICKNAME:']); ?>
<?= $this->Form->submit('送信ボタン'); ?>
<?= $this->Form->end(); ?>
</p>
フォームの各inputタグはcontrolで出力できます。
$this->Form->control('フィールド名', オプション);
となっておりオプションには様々な設定を書き込むことができます。なおオプションはタグの属性などで連想配列形式で設定します。
$this->Form->create(null,["type" => "post","url" => ["controller" => "articles","action" => "index"]]);
フォーム開始タグは以上のように指定します。第一引数はモデルですが、まだモデルに関しては触れていないのでnullを設定しておきます。
第二引数の連想配列のキーTypeには、getかpost,urlにはcontrollerやアクションなどを指定します。
ここにアクションとコントローラを書くことにより、設置する場所が変わっても正しくアドレスを認識して正しく使えるので、設定はめんどくさくなりますが、URLを直書きするよりもこの形式で書いておいたほうがよいでしょう。
それからprint_rですがPHPの便利出力機能で配列の中身をすべて見やすい形式で出力してくれるというメソッドです。
今回はPersonDataの中身をまとめて出力します。
コントローラも編集しておきます。
<?php
//パス:myapp/src/Controller/ArticlesController.php
namespace App\Controller;
use App\Controller\AppController;
class ArticlesController extends AppController
{
public function index()
{
//ビューテンプレートにデータを渡す
$this->set("title","Hello World.");
if($this->request->isPost()){
$this->set("PersonData",$this->request->getData("PersonForm"));
}else{
$this->set("PersonData",[]);
}
}
}
indexアクション上の
$this->request->isPost()
ですが、リクエストがポストメソッドによるものかどうかを判定するメソッドでポストメソッドだった場合にtrueを返します。
ifでtrueかfalseかを判定してtrueだった場合にポストの中身のPersonFormを取得しています。
今回はフォーム作成時に名前の付け方をPersonForm.nameのようにしていたので、PersonFormが連想配列になっています。
フォームの中身(name,age,nickname等)がまとめて入るので便利ですね。
実際に動かしてみましょう。
http://localhost/myapp/articles
以上のようになりましたか?
ちゃんと送信されたデータが保持されている事が分かります。このようにデータの保持やformのアクションの正しいパスへの設定など色々なことを自動でやってくれるのでフォームヘルパーは便利なのです。
チェックボックスとラジオボタン
では基本的なフォームヘルパーの使い方が見えてきたところで、テキスト入力以外のコントロールの使い方も見ていきましょう。テキスト入力さえ分かれば結構いろいろなアプリは作れますが、これらはより複雑なものを作りたいときに役立ちます。
入力のフィールドにはラジオボタンやチェックボックスなどがほかに使えます。
//パス:myapp\templates\Articles\index.php
<h1 style="padding: 0.4em 0.5em;color: #494949;background: #f4f4f4;border-left: solid 5px #7db4e6;border-bottom: solid 3px #d7d7d7;">
<?= $title ?>
</h1>
<div><pre><?php print_r($PersonData) ?></pre></div>
<p>
<?php
$options = [
'dansei' => '男性',
'jyosei' => '女性'
];
?>
<?= $this->Form->create(null,["type" => "post","url" => ["controller" => "articles","action" => "index"]]); ?>
<?= $this->Form->control('PersonForm.seibetu', ['type' => 'radio', 'label' => '性別:','options' => $options]); ?>
<?= $this->Form->control('PersonForm.chk', ['type' => 'checkbox', 'label' => 'チェックボックス:']); ?>
<?= $this->Form->submit('送信ボタン'); ?>
<?= $this->Form->end(); ?>
</p>
コントローラーに変更はありませんが一応便宜上記載しておきます。
<?php
//パス:myapp/src/Controller/ArticlesController.php
namespace App\Controller;
use App\Controller\AppController;
class ArticlesController extends AppController
{
public function index()
{
//ビューテンプレートにデータを渡す
$this->set("title","Hello World.");
if($this->request->isPost()){
$this->set("PersonData",$this->request->getData("PersonForm"));
}else{
$this->set("PersonData",[]);
}
}
}
以上のように記述します。解説をしていきますと
$options = [
'dansei' => '男性',
'jyosei' => '女性'
];
で男性か女性か選ぶためのオプションを入力しておきます。
$this->Form->control('PersonForm.seibetu', ['type' => 'radio', 'label' => '性別:','options' => $options]);
で使いますが、typeなどを指定する、オプションのタグ属性の項目箇所にまとめて設定します。すると男女を選べるラジオボタンが出現して男性、または女性を選ぶとその時の選んだデータとしてdanseiまたはjyoseiが送信されます。
それからチェックボックスの方ですがtypeをcheckboxにするだけですね。データはチェックが入っている時に1、チェックなしの時には0が送信されます。
http://localhost/myapp/articles
リストボックスを作ろう
コントローラーに変更はありませんが一応便宜上記載しておきます。
<?php
//パス:myapp/src/Controller/ArticlesController.php
namespace App\Controller;
use App\Controller\AppController;
class ArticlesController extends AppController
{
public function index()
{
//ビューテンプレートにデータを渡す
$this->set("title","Hello World.");
if($this->request->isPost()){
$this->set("PersonData",$this->request->getData("PersonForm"));
}else{
$this->set("PersonData",[]);
}
}
}
//パス:myapp\templates\Articles\index.php
<h1 style="padding: 0.4em 0.5em;color: #494949;background: #f4f4f4;border-left: solid 5px #7db4e6;border-bottom: solid 3px #d7d7d7;">
<?= $title ?>
</h1>
<div><pre><?php print_r($PersonData) ?></pre></div>
<p>
<?php
$options = [
'tiba' => '千葉',
'saitama' => '埼玉',
'totigi' => '栃木',
'gunma' => '群馬',
'toukyou' => '東京'
];
?>
<?= $this->Form->create(null,["type" => "post","url" => ["controller" => "articles","action" => "index"]]); ?>
<?= $this->Form->control('PersonForm.Shussin', ['type' => 'select', 'label' => '都道府県:','options' => $options]); ?>
<?= $this->Form->submit('送信ボタン'); ?>
<?= $this->Form->end(); ?>
</p>
リストボックスはオプションの属性の個所に、キーがoptionsで、そのデータとして連想配列のリストを持つ変数$optionsを指定することにより設定できます。
この際 値=>ラベルのような関係になることに注意してください。
「ラベル」を選択すると「値」がフォームデータとして送信されます。
ほかは難しいところはないと思います。実際に動かして確認してみましょう。
http://localhost/myapp/articles
いかがでしょうか?このようにリストではあらかじめ決めておいたリストからデータを選択させる事ができます。
複数選択をする事もできます。
オプションの属性に”multiple” => trueを設定するとできるようになります。
これを指定しただけでは全体が表示されないのでいくつか値が表示されるようにするためにsizeも併せて指定します。sizeだけでうまくいかないときはstyle属性も指定してください。
style=>”height:150px;”のようにします。multipleにcheckboxを指定するとリストがチェックボックスとして表示されます。併せて覚えておくと便利ですね。
//パス:myapp\templates\Articles\index.php
<h1 style="padding: 0.4em 0.5em;color: #494949;background: #f4f4f4;border-left: solid 5px #7db4e6;border-bottom: solid 3px #d7d7d7;">
<?= $title ?>
</h1>
<div><pre><?php print_r($PersonData) ?></pre></div>
<p>
<?php
$options = [
'tiba' => '千葉',
'saitama' => '埼玉',
'totigi' => '栃木',
'gunma' => '群馬',
'toukyou' => '東京'
];
?>
<?= $this->Form->create(null,["type" => "post","url" => ["controller" => "articles","action" => "index"]]); ?>
<?= $this->Form->control('PersonForm.reki', ['type' => 'select', 'label' => '都道府県:', "multiple"=> true,'size' => 3 ,"style" => "height:150px;", 'options' => $options]); ?>
<?= $this->Form->submit('送信ボタン'); ?>
<?= $this->Form->end(); ?>
</p>
このデータは使用するときはforeachを使うとよいでしょう。foreachを使った例は以下のようになります。
$a = $this->request->getData("PersonForm.reki");
foreach ($a as $key => $value){
//データにしたい処理
//print $key . $value;
}
//パス:myapp\templates\Articles\index.php
<h1 style="padding: 0.4em 0.5em;color: #494949;background: #f4f4f4;border-left: solid 5px #7db4e6;border-bottom: solid 3px #d7d7d7;">
<?= $title ?>
</h1>
<div><pre><?php
print_r($PersonData) ;
if($this->request->isPost()){
$a = $this->request->getData("PersonForm.reki");
foreach ($a as $key => $value){
//データにしたい処理
//print $key . $value;
}
}
?>
</pre></div>
<p>
<?php
$options = [
'tiba' => '千葉',
'saitama' => '埼玉',
'totigi' => '栃木',
'gunma' => '群馬',
'toukyou' => '東京'
];
?>
<?= $this->Form->create(null,["type" => "post","url" => ["controller" => "articles","action" => "index"]]); ?>
<?= $this->Form->control('PersonForm.reki', ['type' => 'select', 'label' => '都道府県:', "multiple"=> true,'size' => 3 ,"style" => "height:150px;", 'options' => $options]); ?>
<?= $this->Form->submit('送信ボタン'); ?>
<?= $this->Form->end(); ?>
</p>
foaeachは何かにつけて使うので覚えておくと便利です。特にデータにまんべんなく処理をしたい場合によく使います。
さてここまで早足で色々なコントロールを見てきましたが、ついてこれてますでしょうか。
なかなか初めてのものを使いこなすのは難しいと思いますが、もし理解が難しいようであればテキスト入力ボックスだけは覚えておいてください。
後のものは応用ですので、必要ない方はすぐに理解できなくてもかまいません。
必要にかられて使うものですので必要になるまではボヤっとした理解でもよいです。
ただこういうものは使っているうちに覚えてくることもあるので理解を深めたい方は試行錯誤して使ってみるのもよいしょう。
きっと身に付きますよ。
レイアウトを使いこなそう
レイアウトは以前少し触れましたがここではレイアウトについて取り上げてみたいと思います。
ウェブページは基本デザインが統一されているものです。同じウェブサイトなのにデザインが統一されていないとユーザは違うウェブサイトに移動してしまったのかなと疑問を持つことになってしまいます。
そこでウェブのデザインは統一したいわけですが、統一したデザインを作るのにもってこいなのがレイアウトです。
レイアウトはコンテンツ部分とベースとなる部分を分けてデザインできる仕組みでこれを使うことにより簡単にコンテンツ部分とそれ以外を分けたウェブサイトを構築できます。
すでにデフォルトレイアウトを使っていますが自分でレイアウトを作って使ってみましょう。
レイアウトは myapp\templates\layoutに配置します。
<!-- myapp\templates\layout\userlayout.php -->
<!DOCTYPE html>
<html dir="ltr" lang="ja">
<head>
<?= $this->Html->charset(); ?>
<title>ホームページ</title>
<?= $this->Html->css("styleMain"); ?>
</head>
<body>
<!-- ヘッダー -->
<div id="header">
<div class="inner">
<!-- ロゴ -->
<h1>CakePHP講座 レイアウトテスト</h1>
<!-- / ロゴ -->
<!-- トップナビゲーション -->
<table style="width:100%;">
<tr><td class="Menu">ホーム</td><td class="Menu">お店</td><td class="Menu">営業時間</td><td class="Menu">アクセス</td><td class="Menu">会社概要</td></tr>
</table>
<!-- トップナビゲーション -->
</div>
</div>
<?= $this->fetch("content"); ?>
<table style="width:100%;">
<tr><td class="foot">Copy Right 2020 CakePHP 講座 All rights reserved.</td></tr>
</table>
</body>
</html>
/** myapp\webroot\css\styleMain.css **/
.Menu{
width:20%;background-color:#2222ff;
text-align:center;
color:#FFFFFF;
}
.foot{
width:100%;background-color:#2222ff;
text-align:center;
color:#FFFFFF;
}
body {
background-color: #eeeeff;
text-align: center;
}
h1 {
font-size: 25pt;
font-family: 'Luckiest Guy';
color: #fff;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-shadow: 0px -6px 0 #212121,
0px -6px 0 #212121,
0px 6px 0 #212121,
0px 6px 0 #212121,
-6px 0px 0 #212121,
6px 0px 0 #212121,
-6px 0px 0 #212121,
6px 0px 0 #212121,
-6px -6px 0 #212121,
6px -6px 0 #212121,
-6px 6px 0 #212121,
6px 6px 0 #212121,
-6px 18px 0 #212121,
0px 18px 0 #212121,
6px 18px 0 #212121,
0 19px 1px rgba(0,0,0,.1),
0 0 6px rgba(0,0,0,.1),
0 6px 3px rgba(0,0,0,.3),
0 12px 6px rgba(0,0,0,.2),
0 18px 18px rgba(0,0,0,.25),
0 24px 24px rgba(0,0,0,.2),
0 36px 36px rgba(0,0,0,.15);
}
<?php
//パス:myapp/src/Controller/ArticlesController.php
namespace App\Controller;
use App\Controller\AppController;
class ArticlesController extends AppController
{
public function initialize(): void {
$this->viewBuilder()->setLayout("userlayout");
}
public function index()
{
//ビューテンプレートにデータを渡す
$this->set("title","Hello World.");
if($this->request->isPost()){
$this->set("PersonData",$this->request->getData("PersonForm"));
}else{
$this->set("PersonData",[]);
}
}
}
以下はUTF8のエンコードで書かれているという指定をhtmlタグに出力するコードです。
$this->Html->charset();
以下はWEBROOTのフォルダのstyleMain.cssのファイルをCSSファイルとして使うという指定です。
$this->Html->css("styleMain");
コンテンツ部分を読み込む指定です。
これらの指定はタグに変換されますのでechoや<?=などで出力してください。
コンテンツ部分はmyapp\templates\コントローラ\のアクションに対応するビューがそれにあたります。
$this->fetch("content");
public function initialize(): void {
$this->viewBuilder()->setLayout("userlayout");
}
initializeがありますが、これはコントローラーのアクションメソッドが呼ばれる前に必ず最初に一回実行されるメソッドで、共通する処理などを記述しておくことができます。
主な用途はコントローラの初期化処理などです。コントローラのどのアクションを実行してもその前に呼び出されます。
これらのファイルを設置し実行すると
以下のようにヘッダーメニューやフッターなどのベース部分の中にコンテンツが挿入されていることが分かります。
エレメントを使おう
レイアウトではさらにパーツを細分化したエレメントという機能を使うことができます。
パーツをエレメントで分割すればパーツの再利用や組み立てが便利になります。
どんなものか早速使ってみましょう。
<!DOCTYPE html>
<html dir="ltr" lang="ja">
<head>
<?= $this->Html->charset(); ?>
<title>ホームページ</title>
<?= $this->Html->css("styleMain"); ?>
</head>
<body>
<!-- ヘッダー -->
<div id="header">
<div class="inner">
<h1>CakePHP講座 レイアウトテスト</h1>
<?= $this->element("header",["var" => "Header Text"]) ?>
</div>
</div>
<?= $this->fetch("content"); ?>
<?= $this->element("footer",["rights" => "Cake講座"]) ?>
</body>
</html>
<!-- \myapp\templates\element\header.php -->
<!-- トップナビゲーション -->
<table style="width:100%;">
<tr><td class="Menu">ホーム</td><td class="Menu">お店</td><td class="Menu">営業時間</td><td class="Menu">アクセス</td><td class="Menu">会社概要</td></tr>
</table>
<!-- トップナビゲーション -->
<!-- \myapp\templates\element\footer.php -->
<table style="width:100%;">
<tr><td class="foot">Copy Right 2020 <?= $rights ?> All rights reserved.</td></tr>
</table>
エレメントは$this->elementメソッドで呼び出します。
$this->element("footer",["rights" => "Cake講座"])
第一引数はエレメントの名前で拡張子をとったものです。
第二引数はエレメントに渡すデータで連想配列形式で指定することによりキーが変数名に、値が変数の内容になります。
http://localhost/myapp/articles
実行するとどうでしょうか?見た目は変わりませんがちゃんとファイル分割できています。
いくつかファイルを用意して切り替えてみるのも面白いでしょう。
編集する箇所が分かりやすく一回の編集でまとめて複数の個所にまたがった編集ができるのでサイトが大きくなるにつれこれらの威力は大きくなってきます。
たとえばレイアウトに直書きしているとそのレイアウト全部を編集しなければなりませんが、参照設定にしておけば編集個所は一か所で済みます。
大規模サイトには欠かせないですね
そしてエレメントに渡す変数ですが、こうやってレイアウトビューから値を渡していくことができます。
応用ではコントローラからレイアウトビューへレイアウトからエレメントへといったデータの受け渡しも可能です。
順に書いていくだけなので割愛します
データベースを利用した方がいいよ
ここからはデータベースを使っていくよ
データベースを使ってみよう(準備編)
前回まででコントローラとビューの使い方については少しずつ分かってきました。残るはMつまりモデルの部分が分からないままになっています。ビューやコントローラに関しては少し分かりやすいですが、モデルに関してはデータベースが関わってくるので少し難しくなります。
データベースには様々なものがありますがCakeで扱うデータベースは、リレーショナルデータベースと呼ばれるものでSQLと呼ばれるデータを取り出したり、処理したりするための言語を使って操作するものです。SQLデータベースはSQLサーバを起動して使用するものなので、SQLサーバの起動が必要となります。このサーバーにSQL言語による命令を発行してデータを操作するのです。SQLが発行されるとその結果がクライアントに返されデータの処理が可能となります。SQLはプログラミング言語と相性がよく構造化されているため、データの扱いが簡単なものとなります。そうSQLはプログラミングに適しているのです。
モデルの役割とは
通常SQLデータベースではSQL(ストラクチャードクエリーランゲージ)を発行してデータを操作します。ところがCakeではSQLを直接扱わず、データを取り出したいという命令を出すとSQLに自動で変換してデータと取り出し結果をオブジェクトとして扱えるようになります。結果のオブジェクトはコントローラなどで処理し扱えます。このようにモデルはPHP言語で書かれた命令からSQLコマンドに変換するPHPプログラムとデータベースの橋渡し役SQLとPHPの同時翻訳をしてくれる役割を果たしているといっていいでしょう。
Cakeで扱えるデータベースとは
MySQL | 広く一般に使われているデータベースサーバでLAMP(Linux,Apache,MySQL,PHPで出来たシステムの略)という言葉も出来るほど普及していいます。XAMMPに含まれるMySQLはMaria DBというMySQL互換ソフトです。 |
PostgreSQL | 日本で人気の高いデータベースソフトです。Linuxで広く使われています。 |
SQLite | サーバータイプではなくファイルに直接アクセスして利用するタイプのデータベースシステムです |
Microsoft SQL Server | マイクロスフ製のデータベースサーバーです。.netなどのプログラムから多く利用されています |
オラクルデータベース | オラクルのデータベースも使えますが、別途プラグインが必要となります。 |
データベースの選定は大事
データベースといってもその種類により方言のようにSQLは微妙に異なってきます。また仕様も違うために様々な制限も異なってきます。データベースを選ぶときは本格システムを作るときなどは慎重に行いましょう。個人でデータベースを使った掲示板などを作るときはそれほど慎重になる必要はないでしょう。CakePHPではデータベースの差異はCakeが吸収してくれるので比較的データベースの切り替えは簡単になっています。SQLを直接発行することもCakePHPでは可能ですがこのような使用方法をしない限りにおいては切り替えが簡単にできる場合もあります。このような事情もあるのでここではとりあえずMySQLを使って作っていくことにします。XAMMPであらかじめ用意されているのがMySQLコンパチブル(互換)だからです。
ここからはMySQLの設定方法などについて解説していきます。
MySQLを用意しよう。
MySQLは一般に広く使われているためネットで調べた時に情報量も多く便利なためMySQLを使っていきましょう。
MySQLの起動はXAMMPのコントロールパネルから行えます。これはApacheの起動の時と同じものです。
起動した画面のMySQLの横のボタンのスタートを押しましょう。MySQLのラベルが緑いろになれば起動は成功しています。このボタンを押した際にWidnowsファイアーウォールの制御画面が出たらこのアプリの通信を許可してください。これで起動の仕方の解説は終わりです。
データベースの構造について
さてMySQLの準備ができた所でデータベースの構造やその各要素の名称などを確認しておきましょう。
データベース | データベースの一番大きな入れ物です。データベース名でデータベースを管理してその中にテーブルやレコードなどのデータを保管していきます。この中にありとあらゆるデータが保存されていきます。 |
テーブル | データベースの中にテーブルという単位でさらにデータが細分化されて保管されていきます。テーブルは保管するデータの名前とその型をまとめたセットとなります。例えばnameというデータはTEXT形式で,ageはint型でというように保管するデータとその型を定義して決めておきます。これらの一つ一つの項目はカラム(もしくはフィールド)と呼ばれます。こうやってどんなデータを保管するか定義したものがテーブルなのです。 |
レコード | 定義したデータ型の実際のデータがレコードです。nameやageなど定義されたデータ型のデータが1行1データ(1レコード)として実際のデータがたまっていきます。レコードには定義したデータがそれぞれ入力されすべて同じ形式でテーブルに蓄積していきます。 |
phpMyAdminを使おう
皆様データベースを扱った事がありますでしょうか?データベースを扱ったことのない方も結構いらっしゃると思います。SQLデータを扱うSQLサーバですが、いきなりSQLもとっつきにくいと思います。データを扱うのに便利なphpMyAdminと呼ばれるウェブアプリがXAMMPには用意されていますので、こちらを使うとデータの操作が簡単になります。もちろんSQLを使ったデータの操作も可能ですが、グラフィカルユーザインタフェイスによるデータの追加や削除などデータ操作が簡単に行えるこのツールは何かとよく使う便利ツールです。ここからはこちらのツールの簡単な使い方を勉強していきたいと思います。
XAMMPでApacheを起動して以下のアドレスにアクセスすることにより、phpMyAdminが使えます。
起動すると以下のような画面になります。
左側上部のエリアから説明していきます。
こちらの画面はmysqlの中に存在するデータベースを選べます。階層構造になっていてデータベースを選んだ中にあるのはテーブルです。初めから存在するデータベースはMySQLの設定データなどでむやみにいじってはいけないデータです。
右側のエリアはメニューがありメニューに応じて内容が変わります。
右側下部のエリアは選んだメニューにより大きく変わる部分です。一つ一ついじってみて理解を深めるといいでしょう。一応ここからは必要に応じて説明をしていくことにします。
データベースを作ろう
の画像の最上部に新規作成ボタンがあります。
データベース名の個所に新規作成するデータベース名を入力します。右側は優先されるデータのエンコード設定です。ここで選んだ文字エンコードをデータベースのデフォルトエンコーディングにします。正しく設定しないと文字化けなどしますので、ここではCakePHPで使うUTF8を選ぶためにutf8_unicode_ciを選んでおきましょう。最後に作成ボタンを押せばデータベースが作成されます。データベースはテーブルやレコードの入れ物でしたね。思い出しましたか?
productsテーブルを作る
それではテーブルを作っていきましょう。テーブル名の個所にproductsと入力してカラム数を5とします。その後実行ボタンを押します。CakePHPで使うテーブル名の注意点としては英語の複数形を使うという点です。今回は製品の情報を扱うテーブルという事で複数形のproductsにしました。これは命名規則などで規定されていてこのような名前にすると都合がいいためです。
カラムの作成-各入力項目
作成実行ボタンを押すとフィールドを設定する画面が現れます。この画面はテーブルのフィールドのタイプや名前などを設定する画面です。設定項目が非常に多いため横スクロールしないと入りきりませんが、必要に応じて横スクロールして設定しましょう。用意されている画面の説明は以下の通りです。
名前 | フィールドの識別名です。分かりやすい名前を付けます。 |
データ型 | データのタイプを選びます。扱えるデータは多岐にわたります。 |
長さ | テキストなどの場合の最大文字数、データ長を設定します。このサイズまでのデータが入力できます。 |
デフォルト値 | データが空で作成された場合のデフォルト値です。作成時にデータの指定がなかった場合にこの値が設定されます。 |
照合順序 | エンコーディング方式を指定します。 |
属性 | バイナリデータやアンサイン(プラスマイナスのない値)などの設定です |
NULL | NULL値を許可するかどうかの設定です。 |
インデックス | プライマリキーなどを設定できます。 |
A_I | オートインデックスの略で設定するとデータ作成時に自動的に重複しない番号を割り振ります |
コメント | カラムに説明をつけておきたい場合に設定します。 |
コメント欄より右側はそれほど使うことはありませんので覚えなくて大丈夫です。また最低限必要なのは名前とデータ型くらいです。ほかは必ずしも設定しなくても大丈夫です。
実際にカラムを作ろう
1つ目のフィールド
名前 | product_idとします |
データ型 | intとします |
インデックス | PRIMARYとします |
A_I | チェックを入れます。 |
2つ目のフィールド
名前 | product_name とします |
データ型 | VARCHARとします |
長さ | 200とします |
3つ目のフィールド
名前 | model_no とします |
データ型 | VARCHARとします |
長さ | 200とします |
4つ目のフィールド
名前 | cost とします |
データ型 | INTとします |
5つ目のフィールド
名前 | discount とします |
データ型 | FLOATとします |
A_Iを設定すると自動でPRIMARYキーが設定されるはずです。もし設定されない場合は手動でインデックスよりPRIMARYキーを設定してください。
プライマリキーという見慣れない言葉が出てきました。プライマリーキーとはなんでしょうか。データベースでデータを取り出すときに確実に一つのレコードを取り出すためにはどうしたらいいでしょうか。実はプライマリーキーがレコードを一意に決定するためのIDになるのです。プライマリーキーは絶対に重複して二つ以上のデータが記録されることはありません。必ず一意の重複しないデータが記録されます。これによりプライマリーキーでデータを取り出せば、必ず一つの特定のレコードが取り出せて混乱を避けられるという訳です。ほかのデータは人物データベースであれば名前などが重複する事もあります。同姓同名は許可しないと実際のデータを入れるときに困りますからね。そんなわけで重複したデータを許可した場合、山田で取り出したら2件以上出てくることもあるわけです。それではレコードを特定できませんからプライマリーキーというものが必要になる訳です。
またA_Iはオートインクリメントの事でこの設定は自動で重複しない番号を割り振ってくれる事により管理を楽にしてくれる機能です。プライマリーキーの設定と非常に相性がよく同時に設定する事が多いのです。
テーブルできた!
テーブルを作成するとメニューの構造が表示された状態になります。このメニューの「構造」はあとからデータベースの構造を変更したくなった場合に、構造を根本的に変えられるという機能です。テーブルの構造を変えるとプログラムが動かなくなる可能性があるので簡単には変えられず、よく検証が必要ですが、そのような事も出来る機能があると覚えておくとよいでしょう。プログラムを作ってから仕様変更したくなることは割とあります。そのような時に便利ですね。
レコードデータを作っていこう
作成したproductsテーブルにはまだレコードがありません。サンプルのためのレコードを追加していきましょう。レコードの追加は上部メニューの挿入タブから行えます。productsテーブルを選択したまま挿入を押しましょう。出てくる画面ではレコードをいくつか同時に挿入する事もできます。
レコードの挿入している図
レコードを挿入すると今回発行されたSQLが表示されます。
これをコピーして編集しSQLタブからデータを挿入する事もできます。
同様の操作を繰り返し5件程度レコードを挿入しておくとよいでしょう。
表示タブで作ったレコードを確認
サンプルレコードを作ったら上部のメニューから表示をクリックしてください。
作ったレコードを確認する事ができます。
レコードはきちんと追加されているでしょうか。
ちゃんと存在すればレコードの追加は完了です。
CakePHPのデータベース設定
それでは作成したデータベースを利用するための設定をしていきましょう。
CakePHPからデータベースを使う設定というものがあります。
データベースの使用にはパスワードや接続先、ユーザ名などを設定する必要があります。
データベースに誰でもアクセスして書き換えたり閲覧されたりしては困りますからね。必要な設定なのです。
CakePHPからデータベースを使用するためにはmyappの中のconfigフォルダを見てみてください。
この中にapp.phpとapp.defalt.phpというファイルがあります。
これらのファイルは設定関連ファイルでapp.phpは設定ファイル、app.defalt.phpは設定テンプレートファイルになります。
設定のテンプレートが存在するapp.defalt.phpは実際には使いません。
このファイルはなるべく編集しないようにしてデフォルト状態を保ってください。
設定が分からなくなったときにコピーしなおして最初から設定できます。
これらのファイルの中身、設定項目ははとてつもなく長い連想配列です。
連想配列の構造を間違えて壊してしまうと問題が発生しプログラムが動かなくなります。
そのためデフォルト状態を記載したファイルというのは役立つ場面があります。
app.phpは実際に設定を反映させるために書き換えるファイルです。こちらを正しく編集していきましょう。
ではデータベースの設定の個所を書き換えましょう。
データベースの個所はDatasourcesの中身です。
コメントはなくすとこんな感じの個所があります。
'Datasources' => [
'default' => [
'className' => Connection::class,
'driver' => Mysql::class,
'persistent' => false,
'timezone' => 'UTC',
'flags' => [],
'cacheMetadata' => true,
'log' => false,
'quoteIdentifiers' => false,
],
'test' => [
'className' => Connection::class,
'driver' => Mysql::class,
'persistent' => false,
'timezone' => 'UTC',
//'encoding' => 'utf8mb4',
'flags' => [],
'cacheMetadata' => true,
'quoteIdentifiers' => false,
'log' => false,
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
],
],
各項目を解説してゆきます。まずdefaultとtestという大きな見出しがありますが、これは本番環境とtest環境で使い分けるための設定で使い方次第では環境を分けられます。その中のdefaultを見ていきますが、さらに以下の項目があります。
'className' => Connection::class,
データベースの接続に用いるクラスの指定です。これは設定変更しません。
'driver' => Mysql::class,
ドライバーと呼ばれるプログラムの指定でデータベースにアクセスするために必要な基本ソフトです。
デバイスドライバーなどから連想されるように、これがあることによりデータベースにすんなりアクセスできます。
なんというか必要なソフトという訳です。ここではMySQLを使いますのでMysql::classで問題ありません。
'persistent' => false,
これは持続的接続に関する設定です。falseのままで結構です。
'timezone' => 'UTC',
タイムゾーンに関する設定です。UTC世界標準時が設定されています。
これは必要に応じて変更すると時間に関する記述が楽になるかもしれません。
ただし数か所の設定を変える必要がありますので必要になった際はよく調べてください。
とりあえず今はUTCのまま使用することにします。
'flags' => [],
その他の各種設定をまとめて指定するものです。今は空のままにしておきます。
'cacheMetadata' => true,
メタデータのキャッシュを有効にするかどうかの設定です。デフォルトのままで結構です。
'log' => false,
ログ出力の設定です。falseのままで結構です。
'quoteIdentifiers' => false,
あなたがテーブルやカラム名に予約語や特殊文字を使用している場合は true に設定します。特殊文字なんかをバッククオートで囲ってクエリーとして使えるようにするための設定ですが、そもそもおかしな文字や予約語などはカラム名に使うべきではないでしょう。処理速度にかかわるそうなのでfalseでよい思います。
'encoding' => 'utf8mb4',
データベースのテキストエンコード設定です。utf8かutf8mb4でよいでしょう。
以下は重要な設定です。主にこの個所を変更します。なお後述しますが、これらの設定はapp.phpにない場合があります。app_local.phpに移動されていますのでそちらで設定しましょう。(書き方次第ではapp.php内で指定することもできます)
'host' => 'localhost',
データベースのホスト名です。こちらのホストに接続してデータを取得します。
'username' => 'cakephp',
mysqlデータベースのユーザ名です。存在するユーザを指定し、パスワードも適切なものに変えましょう。
'password' => 'AngelF00dC4k3~',
mysqlデータベースのアカウントパスワードです。認証のために必要となります。
'database' => 'cake_cms',
接続先のデータベース名です。主に扱うデータベース名を指定します。データベース名は前項で最初に作っていたものを指定します。もちろん他のデータベースを作っていてそれを使いたかったらそれでもかまいません。
なおCakePHP3.xではapp.phpの編集だけでよかったのですがCakePHP4.xではapp_local.phpというファイルが存在しこちらにデータベースの設定などを記述するようです。項目の詳細については一緒なのでこちらにパスワードなどを記入しましょう。app_local.phpを削除しapp.phpによる設定もできますが、Saltなどを指定する必要があります。(Saltはapp.phpに設定されていないため)素直にapp_local.phpを使うのがよいでしょう。
なおxammpのmysqlデフォルト状態ではユーザー名:root,パスワードが空文字列,db名は作成したDB名となっています。
driver | mysqlを使用のため変更なし |
username | root |
password | 空文字列 ” |
database | DB名(mydb) |
とするとよいでしょう。
ファイルの変更が完了したらデータベースにアクセスが可能かどうか確認して置いたほうがよいでしょう。cakephのデフォルトページに確認できる機能がありますのでそちらから確認しておきましょう。http://localhost/myapp/にアクセスするとDatabaseの箇所に青いマークがついていれば正常にアクセス出来ています。文字は以下のようになっていれば正常です
それではCakephpのモデル部分の作成を行ってみましょう。データベースを実際に使用してみるのです。
モデルについて
その前にモデルについてお話ししましょう。データベースへのアクセス部分を担当するのはMVCのMというのはお話したと思います。つまりモデルが担当するのですが、その実態は二つのクラスです。このクラスをそれぞれデータ毎に定義してあげることで、モデルとして機能します。
その二つのクラスとはなんでしょうか。それぞれみていきましょう。
テーブル | データベースのテーブルを扱うクラスです。 |
エンティティ | エンティティデータベースから取り出したいレコードを扱います。 |
これらは必ずセットで作成します。モデル関係のファイルはプロジェクトフォルダの中のsrcフォルダにmodelというフォルダがありその中に保存されます。以下の二つのフォルダがあるのが分かるかと思います。
Entity | エンティティのファイルを保管します。 |
Table | テーブルのファイルを保管します。 |
Behaviorという名前のフォルダもありますが、ここでは使いませんので省略します。
モデルを作成しよう(テーブルクラス)
ここからがモデルの作成となります。まずはテーブルクラスから作っていきましょうTableフォルダの中にProductsTable.phpという名前のテキストファイルを作成し中身を以下のようにしてください。
<?php
//ProductsTable.php
namespace App\Model\Table;
use Cake\ORM\Table;
class ProductsTable extends Table
{
public function initialize(array $config): void
{
}
}
中身はクラスの宣言だけの空っぽ状態ですが、これで大丈夫です。クラスとして存在する事が大切なので気にせず行きましょう。
中身を解説していきます。
名前空間 | namespace App\Model\Table;となっていますが、これはTableクラスではお決まりとなっています。必ず指定しましょう。 |
use文 | cakephpのtableクラスを指定しています。これも決まりです。おまじないのようにかいておきましょう。 |
Tableクラスの継承 | 必ずTableクラスを継承してクラスを作ります。extends Tableという宣言がそうです。これもTableクラスを宣言する際には必須なので決まりです。おまじないのように書きましょう。 |
クラス名 | ここで名称をカスタマイズする必要があります。これから使用するテーブルの名称を書いておきましょう。今回はProductsTableになります。 |
ファイル名称 | クラス名.phpとします。 |
Cakephpでは設定よりも規約が大事にされるとお話しました。クラス名やファイル名はとても大切なので、これを守らないとまともに動きません。もし動かない時などはファイル名やクラス名が正しく指定されているかを確認してください。
テーブルクラスの規則について記述してきました。間違わないように設定してください。
モデルを作成しよう(エンティティクラス)
続きましてエンティティクラスを作っていきます。\src\model内にあるEntityフォルダにProduct.phpという名前のファイルを作ります。中身は以下としてください
<?php
//Product.php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Product extends Entity
{
}
名前空間は決まりうちです。App\Model\Entity;という名前空間に必ず配置します。
Entityクラスの継承 | Entityクラスを継承しているというのがポイントです。継承しないときちんと動きません。 |
クラス名 | クラス名称はテーブル名の複数形を指定します。複数形がsがつくものではないものも内部で対応しているようです。 |
ファイル名 | クラス名.phpとします。 |
複数形単数形などは日本人には分かりにくいですよね。ネットで辞書を調べるなどして対応するか言い換えで単数、複数の分かりやすいものを選ぶとよいでしょう。
それではこれらを使って実際にデータベースにアクセスしていきましょう。
コントローラとビューの作成
以下のコントローラーをsrcの中のControllerの中に作ってください。
<?php
use Cake\ORM\TableRegistry;
class ProductsController extends AppController
{
public function view($id)
{
$prodTable = TableRegistry::getTableLocator()->get('Products');
$data = $prodTable->find("all");
$this->set("data",$data);
}
}
あとはテンプレートを作っていきます。
アプリのルートのtemplatesの中にProductsフォルダを作りview.phpを作ります。
中身は以下の通りです。
<p>テーブルのデータです</p>
<table>
<?php foreach($data as $item) :?>
<tr>
<td><?php echo h($item->product_id); ?></td>
<td><?php echo h($item->product_name); ?></td>
<td><?php echo h($item->model_no); ?></td>
<td><?php echo h($item->cost); ?></td>
<td><?php echo h($item->discount); ?></td>
</tr>
<?php endforeach; ?>
</table>
ファイルが設置できたら次の場所にアクセスしてみましょう。
http://localhost/myapp/products/view
テーブルに保存したデータが出力される事が分かると思います。
データベース操作コード類の解説
それでは解説していきたいと思います。
Productsテーブルを使ってProductを取り出す
では今作成したProductsControllerを見ていきます。viewメソッドでProductsテーブルから全エンティティを取り出す処理をしています。
$prodTable = TableRegistry::getTableLocator()->get('Products');
$data = $prodTable->find("all");
テーブルを呼び出すためにTableRegistry::getTableLocator()->get(‘Products’);を行い、メンバメソッドであるfindでエンティティを取り出しています。
findメソッドの解説
今回はfindメソッドというメソッドでデータを取り出しますがfindメソッドをall引数で呼び出すとそのテーブルのエンティティが丸々取り出せます。
全部を返したい場合には一番簡単な方法です。この使い方はこれからも何度か出現します。覚えておくとよいでしょう。
データを取り出したらViewに渡します。
$this->set("data",$data);
がそうです。これでビューでこのエンティティを使用できます。
戻り値はQueryオブジェクト
findで返されるのはQueryオブジェクトと呼ばれるものです。
これはデータベースからのデータを受け取りたい時に使うものです。
findを呼び出すとクエリーが返り、そこからさらに検索したエンティティを取り出せるという事を覚えておくとよいと思います。
この講座でもQueryを使った検索方法などを紹介する予定です。お楽しみに。
$prodTable = TableRegistry::getTableLocator()->get('Products');
$data = $prodTable->find("all");
view.phpの詳細
続いてはview.phpの詳細についてです。viewアクション側でdataという名前の変数をビューに送りました。
そのクエリーオブジェクトを使用してデータを表示させていきます。
<?php foreach($data as $item) :?>
データからデータを一つ$itemとして取り出します。
取り出した$itemにはデータベースで定義した$item->product_id $item->product_name $item->model_no $item->cost $item->discountなどのフィールドがありフィールドごとに取り出せるようになっています。
これをforeachで繰り返し取り出していくのであるデータ分のデータが表示されます。
ここから取り出せるデータは何かというとProductエンティティクラスです。
エンティティクラスを取り出す事により必要な値を抽出して取り出す事ができるというわけです。
繰り返し部分の
<td><?php echo h($item->product_id); ?></td>
<td><?php echo h($item->product_name); ?></td>
<td><?php echo h($item->model_no); ?></td>
<td><?php echo h($item->cost); ?></td>
<td><?php echo h($item->discount); ?></td>
ですがh関数によりhtmlspcialcharをエスケープして安全に取り出している他は難しいところはないと思います。
echoでデータを出力しTDタグのデータとしています。TDタグはhtmlのテーブルタグのデータを表すタグで各データをセルとしたいときに使うものです。
エンティティではフィールド名がそのままプロパティになっています。
データベースのテーブルに定義したproduct_id~discountといったフィールドデータがそのままアクセスできる形となっています。
このようにして取り出すと覚えて置くとよいでしょう。
テーブルクラスの設定
データベースにアクセスできてうれしいですね。ここでモデル部分であるテーブルクラスとエンティティについて少し補足をしていきます。テーブルクラスから見ていきましょう。テーブルクラスでは設定が行えます。ProductsTable.phpを開いて以下のように編集してみましょう
<?php
//ProductsTable.php
namespace App\Model\Table;
use Cake\ORM\Table;
class ProductsTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable("products");
$this->setPrimaryKey("product_id");
}
}
初期化にはinitializeメソッド
ここではinitializeメソッドを定義しています。
この関数はクラスの初期化をするためのものです。このクラスのインスタンスを作成した時に、initializeメソッドが呼び出され内容が初期化されます。
設定などをここで指定して行くわけです。
この関数には引数の配列が一つ指定されます。これはCakePHPのアプリケーションで使う各種設定です。
これらが配列にまとめられて引数として渡されます。今回は使っていませんが、アプリケーションの設定などを利用したい場合にはこちらから取り出せます。
このinitializeメソッドではparent::initialize($config);で親クラスのinitializeが呼ばれています。
親クラスであるTableクラスに用意されている初期化処理も一通り実行されます。忘れずに必ず実行しておきましょう。
その後にテーブルクラスに関する基本設定が実行されます。
テーブル名
$this->setTable("products");
ここではデータベースのテーブル名を指定します。
CakePHPでは基本的にテーブルクラスですと同名のデーターベーステーブルにアクセスしてくれますが、setTableを使うと名前が同じじゃなくともアクセスすることが可能です。
プライマリーキーの設定
プライマリーキーはデータベースのレコードを特定するために必要な一意のidです。
通常idという名前が使われますが違う名前の項目をidとして使う場合にはこちらの設定をしておくとよいでしょう。
$this->setPrimaryKey("product_id");
基本的には設定しなくてもOK
これらの設定項目は命名規則にしたがって利用している場合には自動的に設定されていくものですから、動かないという場合以外は設定しなくても大丈夫だったりします。
エンティティのaccessible
続いてはエンティティに用意される基本設定を見ていきます。Product.phpを開き以下のようにProductクラスを修正してください
<?php
//Product.php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Product extends Entity
{
protected $_accessible = [
"product_name" => true,
"model_no" => true,
"cost" => true,
"discount" => true
];
}
accessibleとは
クラスの中にaccessibleを用意していますがこれは一括代入と呼ばれる機能を利用するための設定です。
フォームなどで値を設定してエンティティを作成する場合に送られたフォームデータを一括でエンティティに設定する事ができれば便利です。
これを行うための設定がaccessibleなのです。
accessibleにはエンティティの項目名と真偽値がまとめて保管されます。これによりその項目が一括代入に対応するかという設定を行うのです。
今回product_name,model_no,cost,discountがtrueになっています。
これはこれらの項目が一括代入に対応しているということを表しています。
このProductクラスにはproduct_idという項目もありますが、これは自動インクリメントに対応しているので自動的に値が設定されますのでフォームなどに値を指定する必要はないのです。
ですからaccessibleには記述していないのです。一括代入に関してはのちにまた説明したいと思います。
$dataの内容を見る
コントローラではProductsTableからエンティティをまとめて取り出すのにfindメソッドを使いました。
このメソッドで取り出したオブジェクトをループで回して使っていましたが、このオブジェクトにはエンティティ以外にも色々な情報がまとめられています。
どんなものが保管されているのでしょうか。少し見ていきましょう。
これにはphpのprint_r機能を使うとよいでしょう。view.phpのソースコードを修正し以下のようにします。
<table>
<?php foreach($data as $item) :?>
<tr>
<td><?php echo h($item->product_id); ?></td>
<td><?php echo h($item->product_name); ?></td>
<td><?php echo h($item->model_no); ?></td>
<td><?php echo h($item->cost); ?></td>
<td><?php echo h($item->discount); ?></td>
</tr>
<?php endforeach; ?>
</table>
<pre>
<?php
print_r($data);
?>
</pre>
修正したら
http://localhost/myapp/products/view
にアクセスします。
以下のようになったと思います。
Cake\ORM\Query Object
(
[(help)] => This is a Query object, to get the results execute or iterate it.
[sql] => SELECT Products.product_id AS Products__product_id, Products.product_name AS Products__product_name, Products.model_no AS Products__model_no, Products.cost AS Products__cost, Products.discount AS Products__discount FROM products Products
[params] => Array
(
)
[defaultTypes] => Array
(
[Products__product_id] => integer
[Products.product_id] => integer
[product_id] => integer
[Products__product_name] => string
[Products.product_name] => string
[product_name] => string
[Products__model_no] => string
[Products.model_no] => string
[model_no] => string
[Products__cost] => integer
[Products.cost] => integer
[cost] => integer
[Products__discount] => float
[Products.discount] => float
[discount] => float
)
[decorators] => 0
[executed] => 1
[hydrate] => 1
[buffered] => 1
[formatters] => 0
[mapReducers] => 0
[contain] => Array
(
)
[matching] => Array
(
)
[extraOptions] => Array
(
)
[repository] => App\Model\Table\ProductsTable Object
(
[registryAlias] => Products
[table] => products
[alias] => Products
[entityClass] => App\Model\Entity\Product
[associations] => Array
(
)
[behaviors] => Array
(
)
[defaultConnection] => default
[connectionName] => default
複雑ですが眺める程度で結構です。
これはQueryオブジェクトの内容でして、Queryはテーブルクラスのfindメソッドでデータベースからレコードを取り出そうとする時に利用されるクラスです。
このクラスには取り出すテーブルに関する情報などがまとめられています。
見ていっても取り出しているレコードなどの情報は一見見えません。
これはforeachで取り出さないと得られないのです。
表示されるのはテーブルの設定情報などになります。
findでどんな物が取り出されているのか軽く見ておくとよいでしょう。
sql | findで実行されたsqlコマンドです |
params | 実行時パラメータです |
defaultTypes | 用意されている項目とそのタイプです |
registryAlias | 登録されているエンティティクラス名です |
talbe | 登録されているテーブル名です |
alias | テーブルの別名です |
entityClass | 登録されているエンティティクラスです |
associations | 複数テーブルを連結処理するための機能です。 |
まあ軽くみておくだけで結構です。
CRUD
データベースのアクセスの仕方がなんとなくわかってきたところで色々なアクセスの仕方を学んで行きましょう。データベースのアクセスでは基本的なもので4つの機能が求められます。
Create | 新しいエンティティを作り保存する |
Read | エンティティを読み込み表示する |
Update | エンティティのデータを更新し保存する |
Delete | エンティティを削除する |
これらの基本機能が頭文字をとりCRUDと呼ばれます。基本的にはCRUDをマスターしてデータベースの基礎ができたと言えるので覚えて置くべきですが、すべてをマスターしなくても一応データベースは使えます。このサイトでは一つ一つ見ていきたいと思います。
viewアクションの修正
まずはReadに当たるエンティティの取得です。
これは前述したifindなどで行えるほか一つのデータだけを取り出すgetメソッドなどが使えます。
getメソッドの使い方を見ていきましょう。
ProductsControllerのviewメソッドを以下のように書き換えます。
<?php
namespace App\Controller;
use Cake\Controller\Controller;
use Cake\ORM\TableRegistry;
class ProductsController extends AppController
{
public function view()
{
$prodTable = TableRegistry::getTableLocator()->get('Products');
$id = $this->request->getQuery("id");
$data = $prodTable->get($id);
$this->set("data",$data);
}
}
書き換え後
http://localhost/myapp/products/view?id=1
のようにアクセスします。するとIDの値が1のレコードデータが取り出せます。
id=1の部分を別の2や3にすればそのレコードが取り出せます
GETクエリーで取り出す
GETクエリーとはデータをユーザからシステムに渡すときに使われる手段です。
?AAA=BBBのようにURLの後ろにつけるとそれをプログラムから取り出せるようになっています。
$id = $this->request->getQuery("id");
この例では?id=xxxのデータを
$kakaku =$this->request->getQuery("price");
などであれば
?price=100
などとしてデータを取り出せます。
getメソッドでデータを取り出す
$idにクエリーを取り出せたらそれを使ってgetメソッドを呼び出します。
$data = $prodTable->get($id);
引数にはプライマリーキーを渡します。
これでプライマリーキーを渡すことで一つデータを取り出すことができます。
返される値はエンティティオブジェクトです。配列などではありません。
プライマリーキーが重複するデータはありませんので、プライマリーキーを使用してデータを取り出した場合常にデータは一つあるいはデータなしかのどちらかです。
つまりGETで取り出せるのは一つのエンティティです。
このエンティティは今後のエンティティの更新や削除などで使いますので取り出しの基本を覚えておきましょう。
エンティティの作成、保存、CRUDのC
エンティティの作成・保存方法について見ていきます。
エンティティは新たに作成し保存することでデータベースのレコードとして保存することが可能です。
新しいレコードを作成したい場合には新しいエンティティのインスタンス(オブジェクト)を作成して保存のメソッドを呼び出すだけです。
このあたりかなり簡単仕様になっているので助かります。
しかしながら実際に保存するためにはデータ入力のフォームを作らないといけません。
そこらへんも見ていきましょう。
以下の二つのアクションを作ります。
add | エンティティのフォームを表示するためのアクションです |
create | フォームを送信する先です。フォーム内容を元にデータを実際に保管します。 |
ビューテンプレートの作成
アプリのルートのtemplatesの中のProductsフォルダにadd.phpを作ります。
<?= $this->Form->create($entity,["type"=>"post","name" => "Product","url"=>["controller"=>"Products","action" => "create"]]); ?>
<div>product_name</div>
<?= $this->Form->text("product_name"); ?>
<div>model_no</div>
<?= $this->Form->text("model_no"); ?>
<div>cost</div>
<?= $this->Form->text("cost"); ?>
<div>discount</div>
<?= $this->Form->text("discount"); ?>
<?= $this->Form->submit("送信用"); ?>
<?= $this->Form->end(); ?>
ここで
<?= $this->Form->create($entity,["type"=>"post","url"=>["controller"=>"Products","action" => "create"]]); ?>
が指定されていますがこれはフォームヘルパーという機能を使ってFormを生成するコードです。
第一引数に$entityがありますがこれを指定することによりエンティティの値が自動的にフィールドに入力されます。
とはいえ今回は新しいエンティティを作っていくので値が表示されたりはしませんが、
フォームヘルパーを使う場合には第一引数にエンティティを入力しておくと後々色々便利になりますので忘れないようにしましょう。
view.phpも直しておく
一つ忘れないでほしいのがtemplateフォルダのproductsファルダのview.phpです。
以前にview.phpを書き換えてしまいましたね。$dataの中身を表示させるようにしたと思います。
それを元に戻す作業が必要となってきます。
これをレコードの内容を表示するようにしておきたいと思います。
<!-- templates/Products/view.php -->
<p>テーブルのデータです</p>
<table>
<?php foreach($data as $item) :?>
<tr>
<td><?php echo h($item->product_id); ?></td>
<td><?php echo h($item->product_name); ?></td>
<td><?php echo h($item->model_no); ?></td>
<td><?php echo h($item->cost); ?></td>
<td><?php echo h($item->discount); ?></td>
</tr>
<?php endforeach; ?>
</table>
コントローラのview側も変えておきましょう。
<!-- src\Controller\ProductsController.php -->
一部略
public function view()
{
$prodTable = TableRegistry::getTableLocator()->get('Products');
$id = $this->request->getQuery("id");
//$data = $prodTable->get($id);
//;
$data = $prodTable->find("all");
$this->set("data",$data);
}
コントローラの実際の修正
では実際にコントローラーを修正していきましょう。
<?php
//src/Controller/ProductsController.php
namespace App\Controller;
use Cake\Controller\Controller;
use Cake\ORM\TableRegistry;
class ProductsController extends AppController
{
public function view()
{
$prodTable = TableRegistry::getTableLocator()->get('Products');
$id = $this->request->getQuery("id");
//$data = $prodTable->get($id);
//;
$data = $prodTable->find("all");
$this->set("data",$data);
}
public function add(){
$prodTable = TableRegistry::getTableLocator()->get('Products');
$entity = $prodTable->newEmptyEntity();
$this->set("entity",$entity);
}
public function create(){
if($this->request->is("post")){
$data = $this->request->getData();
$prodTable = TableRegistry::getTableLocator()->get('Products');
$entity = $prodTable->newEmptyEntity();
$entity = $prodTable->patchEntity($entity,$data);
$prodTable->save($entity);
}
return $this->redirect(["action" => "view"]);
}
}
修正が完了したら
http://localhost/myapp/products/add
にアクセスしてみましょう。
先ほど作成したadd.phpのフォームが出力されます。
このフォームにデータを入力して送信ボタンを押すと内容がcreateメソッドに送られて処理されてデータベースにデータが書き込まれます。
そしてViewメソッドにリダイレクトされてデータ一覧が表示されるという訳です。
送信されたレコードがきちんと追加されていれば成功です。
フォームの処理とデータ(フォームの値の取り出し)
ProductsController.phpで行っている処理を見ていきます
$data = $this->request->getData();
フォームデータをポストメソッドにより連想配列に取り出しています。
ポストメソッドはユーザとデータをやり取りするときに使える手段でhttpではGETメソッド、POSTメソッドの二種類がオーソドックスです。
なおGETメソッドはURLの末尾につく?id=1などといった表記を用いた入力方法でこちらもよく使われます。
ポストメソッドに関してはフォームでデータをやり取りするのが普通です。
新しいエンティティの生成
エンティティの生成は必ずメソッドを使って行います
$entity = $prodTable->newEmptyEntity();
このようにします。
このようにテーブル内のnewEmptyEntityを呼び出すことによって空のエンティティを作成できます。
エンティティにデータを流し込む
$entity = $prodTable->patchEntity($entity,$data);
$dataにポストメソッドで得たものを流し込めばSAVE準備のできたEntityが出来上がります。
一括代入について
accessibleの説明の際に一括代入について少し触れました。
一括代入は自動的にエンティティにデータを入れてくれる機能でこれを使うことでpatchEntityで必要なデータを入れることが出来ます。
エンティティのSAVE
エンティティは出来ましたがまだデータベースに保管されていません。
保管するにはsaveメソッドを呼び出します。
$prodTable->save($entity);
これによりエンティティを保存出来ます。
リダイレクトについて
リダイレクトとはリクエスト際にページが移動したなどの理由によりリクエストしたページから別のページにジャンプさせる機能です。
同じコントローラから同じコントローラの別アクションに飛ばしたい時は
return $this->redirect(["action" => "view"]);
のようにアクションを書くだけですみます。別のコントローラへ飛ばしたい時にはコントローラも書きます。
エンティティの更新処理 CRUDのU
続きましてはエンティティの更新を見てきます。
エンティティの更新は新規作成よりかは複雑です。エンティティの更新は以下のように考えられます。
- 更新するエンティティを取り出します
- フォームなどで修正情報を送ります。
- エンティティを取り出して修正を加え保存します。
このようになりますね。作って保存するだけの作業ではないのですね。
以下では流れを説明します。
- view.phpを修正してリンククリックで編集処理にはいれるようにします。
- editアクションを作成し現在の状態をあらかじめ代入しておきます。
- エンティティを書き換えてフォーム送信するとupdateメソッドに送りこのメソッドで更新SAVE処理を行います。
view.phpのデータクリックで現在のデータが表示された状態でedit画面が開き、修正送信で書き換えが完了するという仕組みです。
最初はtemplates/Products/view.phpの中身を変更して行きましょう。商品名のところに編集ページへのリンクを付与します
<!-- templates/Products/view.php -->
<p>テーブルのデータです</p>
<table>
<?php foreach($data as $item) :?>
<tr>
<td><?php echo h($item->product_id); ?></td>
<td><a href="<?php echo $this->url->build(["controller" => "Products","action" => "edit" ]) . "?id=" . $item->product_id; ?>"><?php echo h($item->product_name); ?></a></td>
<td><?php echo h($item->model_no); ?></td>
<td><?php echo h($item->cost); ?></td>
<td><?php echo h($item->discount); ?></td>
</tr>
<?php endforeach; ?>
</table>
<pre>
</pre>
$this->url->build
はURLを生成するためのメソッドで以下のように使用します。
$urltext = $this->url->build(["controller" => "コントローラ名","action" => "アクション名" ])
こうする事でコントローラのアクションへのURLが生成されます。
こちらの便利なところは本番環境とテスト環境のパスが異なる場合でも同じコードで追尾してurlが生成できることです。
edit.phpの編集
editアクション用のビューテンプレートを作っていきましょう。
templates/Products内にedit.phpというファイルを作成し以下のようなコードにしましょう。
<!-- templates/Products/view.php -->
<?= $this->Form->create($entity,["type"=>"post","name" => "Product","url"=>["controller"=>"Products","action" => "update"]]); ?>
<div>product_name</div>
<?= $this->Form->hidden("product_id"); ?>
<?= $this->Form->text("product_name"); ?>
<div>model_no</div>
<?= $this->Form->text("model_no"); ?>
<div>cost</div>
<?= $this->Form->text("cost"); ?>
<div>discount</div>
<?= $this->Form->text("discount"); ?>
<?= $this->Form->submit("送信用"); ?>
<?= $this->Form->end(); ?>
軽く見て分かるようにほとんどaddと一緒なのです。
違うところはFormのAction指定がupdateアクションになっている事です。
これにより送信先がupdateになります。
そしてupdateで保存処理を行います。
フォームはフォームヘルパーで生成し、Entityを使用してフォームを初期化しています。
フォームの中には各フィールドがあり、アクセスしたら自動的に現在記録されているデータが表示された状態でフォームが表示されます。
ほかに違うところがあるのですが、product_idが出力されています。
これはhiddenフィールドなので画面上には存在しませんが、実際にソースコードを読んでみるとちゃんと存在します。
このhiddenフィールドを使用してデータベースのデータのidを指定します。
更新の際には作成の際と異なり更新対象を確定させるためにIDが必要となります。
作成の際にはautoincrement機能により自動的にidが割り振られるのでいりませんでした。
ですが今回は必要です。
ProductsControllerを再編集
それではまたProductsControllerを編集追加していきましょう。
ProductsControllerに以下のメソッドを追加していきましょう。
updateメソッド
<?php
//src/Controller/ProductsController.php
namespace App\Controller;
use Cake\Controller\Controller;
use Cake\ORM\TableRegistry;
class ProductsController extends AppController
{
public function view()
{
$prodTable = TableRegistry::getTableLocator()->get('Products');
$id = $this->request->getQuery("id");
//$data = $prodTable->get($id);
//;
$data = $prodTable->find("all");
$this->set("data",$data);
}
public function add(){
$prodTable = TableRegistry::getTableLocator()->get('Products');
$entity = $prodTable->newEmptyEntity();
$this->set("entity",$entity);
}
public function edit(){
$id = $this->request->getQuery("id");
$prodTable = TableRegistry::getTableLocator()->get('Products');
$entity = $prodTable->get($id)
$this->set("entity",$entity);
}
public function create(){
if($this->request->is("post")){
$data = $this->request->getData();
$prodTable = TableRegistry::getTableLocator()->get('Products');
$entity = $prodTable->newEmptyEntity();
$entity = $prodTable->patchEntity($entity,$data);
$prodTable->save($entity);
}
return $this->redirect(["action" => "view"]);
}
public function update(){
if($this->request->is("post")){
$prodTable = TableRegistry::getTableLocator()->get('Products');
$data = $this->request->getData();
$entity = $prodTable->get($data["id"])
$entity = $prodTable->patchEntity($entity,$data);
$prodTable->save($entity);
}
return $this->redirect(["action" => "view"]);
}
{
}
追記が終えたら
http://localhost/myapp/products/view
にアクセスしましょう。商品リストの商品名のところをクリックするとEDITページに飛べます。
その際に各フィールドは既存データで埋められているはずです。ここから内容を修正し送信するとどうでしょう。
内容が書き換えられて保存されているはずです。どうでしょう。
だいぶウェブアプリらしくなってきたのではないでしょうか
editアクションの処理として
ではアクションの内容を見ていきましょう。
まずeditアクションはどうなっているでしょうか。
editアクションではクエリーパラメータを使って編集するエンティティ番号を渡しそのエンティティを取り出し
$entityに設定してビューテンプレートからページを生成する処理をしています。
例えば、viewで表示されるリストでid=1のリンクをクリックしたとします。
このリンクは以下のようなタグとして生成されているのがソースを見ればわかります。
<a href="/myapp/products/edit?id=1">おいしいごはん</a>
editの後にGETクエリーパラメータとしてidの値が付け加えられていることがわかるでしょう。
このidを取り出して、それを使い実際のエンティティを抽出してビューテンプレートに渡すという作業を行うことができます。
$id = $this->request->getQuery("id");
$prodTable = TableRegistry::getTableLocator()->get('Products');
$entity = $prodTable->get($id);
$this->set("entity",$entity);
こちらがeditアクションの処理になります。getQueryで値を取得して$prodTable->get($id);でそのクエリー番号に対応するエンティティを取り出しています。
そしてsetでentityの名前でエンティティをビューテンプレートに渡しています。
edit.phpでは、このentityをForm->createの第一引数に指定しています。
これにより$entityのオブジェクトの値がそれぞれのフィールドに設定されるのです。
updateアクションによるエンティティの更新
ここで作ったフォームの送信先がupdateアクションになります。
ここでは送信したフォームの内容を元にしてエンティティの更新処理を行います。
どのような処理なのでしょうか。順番に見ていきましょう。
ポストメソッドで送信されたフォームの値を取り出す
$data = $this->request->getData();
以前httpのユーザ入力を受け取る手段としてPOSTメソッドと,GETメソッドがあることは軽く説明したと思います。
POSTメソッドでデータを受け取るにはgetDataとします。これによりユーザがフォームフィールドに入力したデータを取り出すことができます。
データを変数に入れています。
エンティティの取り出し
$entity = $prodTable->get($data["product_id"]);
この後に行うべきはユーザから送信されたid値を使って、編集したいエンティティを取り出す作業です。
これはテーブルクラスのgetメソッドを使って行うことができます。$data[“product_id”]の値を使ってgetすれば指定のidのエンティティを取り出せます。
フォームの値でエンティティを更新する
次ですがフォームにより送られてきた値を使ってエンティティの内容を更新する作業が必要となります。
この作業はテーブルクラスのpatchEntityというメソッドで行えます。このメソッドは第一引数のエンティティを第二引数の値で更新します。
第一引数にgetで取り出したエンティティ、第二引数にフォームにより送信されたデータを指定する事で取り出したエンティティの内容をフォームで送られた値に更新上書きすることができるってことです。
エンティティの保存
$prodTable->save($entity);
そしておしまいにテーブルクラスのsaveを使ってエンティティを保存します。これによりエンティティの更新がデータベーステーブルに送られてレコードの内容が変更され保存されます。
エンティティの更新はpatchEntityでエンティティを更新し、saveで保存するという流れで行うことができます。新規作成の時とは少々異なりますね。
CRUDのD、エンティティの削除
残すところはエンティティの削除となります。どのような流れで実装するか考えておく必要はありますね。例えば削除するときに確認メッセージが出ないと誤って削除して取り返しがつかないこととなるかもしません。これを防ぐために削除してよろしいですかなどときく必要があるかもしれません。
基本的な処理はエンティティの更新を参考にするとよいと思います。
ここでは削除エンティティの表示用にdelete、削除の実際の実行にdestroyという二つのアクションを用意します。全体の流れは以下のようになります。
- index.phpを修正します。データベースの一覧リストに削除のリンクを追加してクリックするとそのエンティティの削除ページ(deleteアクション)にジャンプするようにします
- deleteアクションでは削除するエンティティの内容を表示して削除ボタンを用意します。
- ボタンをクリックすると削除の処理を行うアクション(destroyアクション)に送信されエンティティの削除処理を行います。
更新処理と同様にviewアクションのページから削除する項目のリンクをクリックして削除の処理を進めていくというやり方がよいと思われます。
こうすることで間違って違うエンティティを削除してしまうことを防ぐことができます。
ではviewアクションのview.phpから修正していきましょう。templates/Products/view.php を編集します。
<!-- templates/Products/view.php -->
<p>テーブルのデータです</p>
<table>
<?php foreach($data as $item) :?>
<tr>
<td><?php echo h($item->product_id); ?></td>
<td><a href="<?php echo $this->url->build(["controller" => "Products","action" => "edit" ]) . "?id=" . $item->product_id; ?>"><?php echo h($item->product_name); ?></a></td>
<td><?php echo h($item->model_no); ?></td>
<td><?php echo h($item->cost); ?></td>
<td><?php echo h($item->discount); ?></td>
<td><a href="<?= $this->Url->build(["controller" => "Products","action" => "delete"]); ?>id=<?php echo $item->product_id; ?>">delete</a></td>
</tr>
<?php endforeach; ?>
</table>
<pre>
</pre>
これでdeleteリンクが追加されてリンクが押せるようになった。けれどもこのリンクはまだ機能しないので注意が必要です。
これから機能の部分を作っていきます。
delete.phpのビューテンプレートを作成する
ではdeleteアクションのビューテンプレートを作成していきます。
templates/Products/delete.phpを作成し以下の内容にします。
エンコーディングはUTF-8にしてください。
<p>以下のデータを削除しますか?</p>
<table>
<tr>
<td><?php echo h($item->product_id); ?></td>
<td><?php echo h($item->product_name); ?></td>
<td><?php echo h($item->model_no); ?></td>
<td><?php echo h($item->cost); ?></td>
<td><?php echo h($item->discount); ?></td>
</tr>
</table>
<?php
echo $this->Form->create($item,["type"=>"post","url" => ["controller" => "products","action" => "destroy"]]);
echo $this->Form->hidden("product_id");
echo $this->Form->submit("削除する");
echo $this->Form->end();
?>
<pre>
</pre>
今回は、<?php echo h($item->product_id); ?>というようにエンティティの値を表示して確認できるようにしています。
またそれとは別にecho $this->Form->hidden(“product_id”);というように非表示フィールドを一つ用意したフォームを作成しています。
またボタンの表示も行っており、ボタンを押すと削除するエンティティのIDがdestroyアクションに送られてそのデータを元に削除処理を行うという訳になります。
productsControllerの修正
それではコントローラにアクションを追加していきます。
//src\Controller\ProductsController.php
//一部省略・・・・・・・・
public function delete(){
$prodTable = TableRegistry::getTableLocator()->get('Products');
$id = $this->request->getQuery("id");
$item = $prodTable->get($id);
$this->set("item",$item);
}
public function destroy(){
if($this->request->is("post")){
$prodTable = TableRegistry::getTableLocator()->get('Products');
$data = $this->request->getData();
$entity = $prodTable->get($data["product_id"]);
$prodTable->delete($entity);
}
return $this->redirect(["action" => "view"]);
}
修正が完了したら
http://localhost/myapp/products/view
にアクセスしてデータ一覧の右側に表示されたdeleteを押します。
するとそのエンティティの内容が画面にレコードとして表示されますので「削除する」ボタンを押すとエンティティが削除されます。
それではアクションの動きを見ていきましょう。最初はdeleteアクションになります。
viewでdeleteリンクをクリックするとdeleteアクションに遷移します。viewで示されたdeleteリンクはどのような仕組みで移動先を指定しているのでしょう。
例えばid=1の場合リンクは以下のようになります。
<a href="/CakeGakushu/products/delete?id=2">delete</a>
これを見るとdeleteアクションに指定の後に?がつきその後にid=1がついているのがわかります。
この値をdeleteアクションで受け取ってそのIDのエンティティを取り出しビューテンプレートに渡しているわけなのですね。
$prodTable = TableRegistry::getTableLocator()->get('Products');
$id = $this->request->getQuery("id");
$item = $prodTable->get($id);
$this->set("item",$item);
コントローラのdeleteアクションはこのようになっています。これはエンティティの更新を行うeditアクションとそっくりです。順番が違うだけで一緒ですね。書く時の表記ゆれがあるだけで順番は同じでも構いません。今回はこうなってしまいました深く考えないで結構です。
destroyでエンティティの削除を行う
続けましてフォームの送信先になるdestroyアクションの処理になります。ここでエンティティの削除が行われます。
フォームで送信されたデータを取り出す
$data = $this->request->getData();
すでに出てきていますのでわかりますでしょうか。ユーザからポストされたデータを変数に代入しています。
指定されたIDを使いエンティティを取り出す
$entity = $prodTable->get($data["product_id"]);
フォームのデータを使ってテーブルオブジェクトのgetメソッドを使ってそのIDのエンティティを取り出しています。
取り出したエンティティをデータベースから削除
$prodTable->delete($entity);
テーブルオブジェクトのdeleteメソッドでエンティティを削除します。
このメソッドは第一引数で指定したエンティティを削除するものになります。
これによって該当のエンティティに対応するデータベーステーブル内のレコードが削除されます。
あとがき
以上データベースの操作CRUDを完成させました。
皆さんこれを応用していけばいろいろなものが作れますのでいろいろ試してみてくださいませ。
長くなりましたが解説は以上でひとまず終了となります。皆様お疲れさまでした。
どうぞよいプログラミング生活をお送りくださいませ。
ではまたお会いしましょう。失礼いたします。
↓
サーバが要求されたファイルを探し出す
↓
ファイルによっては内部でプログラム処理を行う
↓
サーバーが処理内容のhtmlを生成する
↓
生成したhtmlをブラウザに送り返す
↓
ブラウザがhtmlの描画処理を行う
↓
必要に応じて画像などhtmlにより構成されたファイルもリクエスト
↓
サーバーが画像などを送り返す
↓
画像などもレンダリング(描画)
↓
ページが表示される