読者です 読者をやめる 読者になる 読者になる

CakePHP基礎の基礎

こんにちは。 アスネット開発部の名倉です。

今回は急遽学習することになったCakePHPについて、得た情報を「CakePHP基礎の基礎」という形で記事にしたいと思います。

◆目次

CakePHPとは?

PHP用のWebアプリケーションフレームワークです。MVCモデルを採用しています。

この「Cake」という名前には「a piece of cake」(朝飯前、すごく簡単)という意味が込められているのだそうです。
本題とは関係ありませんが、美味しそうな名前ですよね。そう思ったのは私だけではないはず…。

概要ついでにMVCフレームワークについても少し触れておきましょう。
MVC」とはプログラムを構成する要素の頭文字をとったものです。

f:id:asnet:20160408142740p:plain

  • Model

ビジネスロジックを扱います。 DBへの問い合わせやデータの登録/削除はModelに実装します。 その他にも、複雑な計算などはモデルで実装し、処理結果をControllerへ渡します。

  • View

ユーザが見る「見た目」の部分に該当します。 HtmlやJavaScriptはViewに実装します。

  • Controller

ControllerはModelとViewを繋ぎます。 ユーザからの入力を受け、必要なModelに適宜入力値を渡し処理を実行させ、処理結果を受け取ってViewに反映して表示する、言うなれば司令塔の役割を担っています。

ソースコードを見てみよう

実際のソースコードMVCに分けて見ていきましょう。 サンプルとして、書籍データの一覧表示と新規追加する機能を紹介します。

Controller
<?php

public class SamplesController extends AppController{                    
                    
    //使用するモデルを宣言               
    public $uses = array('Book');             
                    
    //Viewで使用するヘルパーを宣言             
    public $helpers = array('Form');              
                    
    //書籍一覧を取得、表示する             
    public function search(){              
        //Bookテーブルのデータを取得する          
        $books = $this->Book->search();         
                    
        $this->set('books', $books);         
                    
        $this->render('Samples/search');          
    }               
                    
                    
    //add.ctpからPOSTされたデータをBooksテーブルに登録する               
    //add.ctpについては後述             
    public function add(){             
                    
        //postされた場合のみ処理を行う         
        if($this->request->is('post')){         
            //postされたデータを取得する        
            $data = $this->request->data;        
                    
            try{      
                $saveResult = $this->Book->save($data['Book']);    
                if(is_array($saveresult)){    
                    //登録成功メッセージを設定する
                    $message = sprintf(' %s を追加しました。', 
                    $data['Book']['Title']);
                    $this->Session->setFlash($message);
                    
                    //Searchにリダイレクトする
                    $this->redirect(array(
                    
                    
                    ));
                    
                }   
            }catch(Exception ex){      
                //例外処理(サンプルなので省略)    
            }       
            //エラーがある場合     
            //入力内容を復元して画面表示      
            $this->request->data = $data;        
        }           
    }

Controllerは必ずAppControllerを継承する必要があります。 Controllerで扱うモデルは明示する必要があります。 上記の場合は

//使用するモデルを宣言
public $uses = array('Book');

ここでBookモデルを使うことを宣言しています。 この書き方ではController内全メソッドにおいてBookモデルを使用できるようになりますが、 1メソッドでしか扱わないモデルは、メソッドの中に

$this->loadModel('Book');

のように書くことでモデルを使用できるようになります。

続いてViewヘルパーです。

//Viewで使用するヘルパーを宣言
public $helpers = array('Form');

このように書くことでFormヘルパーをView側で使用できるようになります。 なお、サンプルなのでこのように書いていますが、デフォルトで Formヘルパー Htmlヘルパー Sessionヘルパー の3つは使用できるようになっていますので、これらは明示する必要はありません。 自作したヘルパーを使いたいときなどに記述してください。

次にメソッドの説明を少々。 searchメソッドは書籍一覧を取得、表示するメソッドです。 まずはBookモデルのsearchメソッド(処理内容は後述します)を呼び出し、書籍一覧を取得します。

//Bookテーブルのデータを取得する
$books = $this->Book->search();
$this->set('books', $books);

この1行はView側で取得値を利用するための記述です。 これにより、Controller側で$booksに入っている値をView側の変数「$books」に渡すことができます。 第1パラメータ'books'がViewで使用する変数名、第2パラメータ$booksがControllerから渡す値に該当します。

$this->render('Samples/search');

表示するViewのファイル名を指定します。この記述により表示する画面では「apps/View/Samples/search.ctp」を使用します。 因みに、例として挙げるため明記していますが、書かないことも可能です。 書かない場合は「apps/View/(Controller名)/(アクション名).ctp」というルールでファイルを探し、使用します。 searchメソッドであれば「apps/View/Samples/search.ctp」となり、 実は、明記した場合と明記しない場合に使用するファイルは同じだったりします。

addメソッドは書籍登録画面で入力されたデータの登録を行います。 なお、addメソッドの処理が行われるのはPOST通信が発生したときのみ、という条件をつけています。 画面初期表示時は特に処理はありません。 POST通信が発生した時の処理について順番に説明すると、

①POSTされた入力データを受け取る

$data = $this->request->data;

$dataには['Book'][Title']などのデータが入ります。

keyとなる値はctpファイルのフォームに記述した

<?php
echo $this->Form->input('Book.Title', array(
 'type' => 'text'
 'lavel' => 'タイトル'
);
?>

これの第1パラメータ'Book.Title'をピリオドで区切った値となります。 因みに、$this->request->data;で受け取れるのはPOSTされたデータです。 GET送信の場合は$this->request->query;で取得可能で、他にもJSON等の受け取り方も別に存在しますがここでは割愛します。

②データの保存

$saveResult = $this->Book->save($data['Book']);

saveメソッドはCakePHPの標準のメソッドです。 挿入も更新も行えるメソッドとなっており、どちらを行うかはテーブルの主キーを登録するデータに持っていれば更新、無ければ挿入、となりますが、 主キーがデータにあってもsaveメソッドで主キーが一致するデータのselectが行われ、存在しなければ挿入が発生します。

戻り値は以下のようになります。 登録成功:配列に登録したデータが格納されている 登録失敗: true(Modelにセットしたデータが正しい形でない) false(バリデーションエラー等が原因)

そのため、登録成否の条件判定はis_arrayメソッドを用いて配列か否かをチェックしています。 成功したときはsearchメソッドにリダイレクトさせ、画面表示時に登録完了メッセージを表示させます。 失敗する場合には $this->request->data = $data; これによりフォームの第1パラメータに一致するkeyを持つデータが入力フォームに入った状態で画面を再表示することが出来ます。

Model
<?php         
            
public class Book extends AppModel{          
            
    //Booksテーブルと他テーブルとの関係性を宣言      
    public $hasMany = array('Manage');        
            
    //削除されていないデータを出版年で並べかえて取得する      
    public function search(){      
        $books = $this->find('all',array(  
             'fields' => array('ID', 'Title', 'Writer', 'PublishYear', 'Price', 'Introduction')
             'conditions' => array('DeleteFlag' => 0),
             'order' => 'PublishYear DESC'
             'recursive' => -1
        )); 
            
        return $books;   
    }       
            
}

Modelは必ずAppModelを継承する必要があります。

public $hasMany = array('Manage');

はコメントにも書きましたが、Modelで扱うテーブルと他テーブルの関係を表します。 Books : Manage = 1 : n のため、hasMany(Booksから見て、複数のManageデータを持っている)に設定しています。 1:1、n : 1、n : nは別の書き方で表します。

searchメソッドは書籍一覧で有効なデータを取得します。 第1パラメータ'all'は条件に合致するデータを全件取得します。 他に

  • 'first':条件に合致するデータの上位1件のみ取得

  • 'list':1つのkeyに対して1つの値を持った配列の一覧を取得します。keyと値に対応する項目を明示しない場合、IDとNameまたはTitle等の項目から一覧を作成します。

等があります。

第2パラメータは取得条件を設定します。

  • 'fields' => array('ID', 'Title', 'Writer', 'PublishYear', 'Price', 'Introduction') 取得項目select。明示しない場合はテーブルの全ての項目となります。

  • 'conditions' => array('DeleteFlag' => 0), 取得条件where。条件は配列で指定します。'DeleteFlag' => 0はDeleteFlag = 0という条件になります。

  • 'order' => 'PublishYear DESC' 並び替え条件order by。

  • 'recursive' => -1 テーブルと親子関係にあるテーブルのデータを取得するための項目。-1は親子関係のデータは取得しません。詳細は省略しますが、値が1で直接関係があるテーブル、更に値が大きくなると間接的に関係のあるテーブルからもデータが取得されます。

View

search.ctp

<h2> 書籍一覧 </h2>          
            
<table>           
    <tr>      
        <td>ID</td> 
        <td>タイトル</td>   
        <td>著者</td> 
        <td>紹介文</td>  
        <td>出版年</td>  
        <td>金額</td> 
    </tr>     
            
    <?php foreach($books as $book) : ?>       
            
        <tr>  
            <td><?php echo $book['Book']['ID']; ?></td>
            <td><?php echo $book['Book']['Title']; ?></td>
            <td><?php echo $book['Book']['Writer']; ?></td>
            <td><?php echo $book['Book']['PublishYear']; ?></td>
            <td><?php echo $book['Book']['Price']; ?></td>
            <td><?php echo $book['Book']['Introduction']; ?></td>
        </tr> 
            
    <?php endforeach; ?>      
            
</table>          

こちらは書籍一覧を表示する画面です。 変数に格納した配列のkey値を指定してkeyに対応した値(Value)を表示しています。 cakephpの機能を利用するときは必ず「echo」を行った上で値の出力やフォームの作成を行います。

このctpファイルだけで表示出来ないよね?と過った方、お察しの通りです。 このsearch.ctpで記述した内容はタグの中に表示する要素に該当します。 cakephpではデフォルトで「app/View/Layouts/default.ctp」というファイルがあり、 ユーザが明示的にレイアウトを指定しない場合はこのdefault.ctpのレイアウトを使用して その中のコンテンツとしてsearch.ctpに記載した内容を表示します。(以下の図のようなイメージです)

f:id:asnet:20160408133708p:plain

続いては、書籍情報を登録する画面です。 こちらもレイアウトにdefault.ctpを利用しています。

add.ctp

<?php echo $this->Form->create('Book', array('type' => 'post')); ?>      
    <?php    
    echo $this->Form->input('Book.Title', array( 
         'type' => 'text'
         'lavel' => 'タイトル'
    );  
    ?>   
        
    <?php    
    echo $this->Form->input('Book.Writer', array(    
         'type' => 'text'
         'lavel' => '著者'
    );  
    ?>   
        
    <?php    
    echo $this->Form->input('Book.Introduction', array(  
         'type' => 'text'
         'lavel' => '紹介文'
    );  
    ?>   
        
    <?php    
    echo $this->Form->input('PublishYear', array(    
         'type' => 'text'
         'lavel' => '出版年'
    );  
    ?>   
        
    <?php    
    echo $this->Form->input('Book.Price', array( 
         'type' => 'text'
         'lavel' => '金額'
    );  
    ?>   
        
<?php echo $this->Form->end('登録する'); ?>

フォームの開始、終了を指定するための記述は以下の部分です。

<?php echo $this->Form->create('Book', array('type' => 'post')); ?>
<?php echo $this->Form->end('登録する'); ?>

createメソッドの第2パラメータ

'type'=>'post'

は送信方式を明示しています。 データ送信が発生したときにPOST送信を行います。明示しない場合はGET送信をおこないます。 第1パラメータは登録の対象となるモデルの設定を行っています。 サンプルの場合は'Book'モデルに対応するデータを扱うため第1パラメータに'Book'を設定しています。 endメソッドにのパラメータに設定した'登録する'という文字列は送信を発生させる(submitする)ボタンに表示するテキストになります。 設定することでフォームの最下部にボタンが作られます。 パラメータを渡さなければボタンは作られず、任意の場所にボタンを作ることも可能です。

次に入力フォームを構成する記述です。

<?php 
echo $this->Form->input('Book.Title', array( 
     'type' => 'text'
     'lavel' => 'タイトル'
);  
?>   

終わりに

いかがでしたでしょうか? cakephp、ルールに則ったソースで開発できれば、ソースコードボリュームを減らすことができ、Javaを見慣れた私にとってはなかなかに便利さを感じています。 本当はもっと沢山学習したことがあるのですが、記事の長さが途轍もないことになりそうなので大雑把な紹介とさせて頂きました。 参考までに私の学習に用いた書籍、公式ドキュメントのURLもご紹介します。 学習の第一歩としてご活用いただければ幸いです。

【書籍】 CakePHP2 実践入門 (WEB+DB PRESS plus) 技術評論社,安藤祐介, 岸田健一郎, 新原雅司, 市川快, 渡辺一宏, 鈴木則夫,2012

【URL】 http://book.cakephp.org/2.0/ja/index.html#

github.com