Zend Framework入門

第2章 Zend Frameworkの基本

前へ | 目次へ |次へ  | Yamada-Lab

2.3 データベース利用の基本

■データベース抽象化レイヤ

 Zend FrameworkではMVC(Model - View - Controller)モデルというアーキテクチャが採用されています。ここでは、データの管理を担当するモデル(Model)の作成法について述べます。

 データ管理にあたって、データはファイルシステムに保存したり、データベースに保存したりしますが、ここではデータの管理が容易でかつ検索など高度な管理が可能なデータベースを利用する方法について述べます。

 PHPはプログラミング言語の中でも、フリーウェアのMySQLやPostgreSQLあるいは商用のOracleやSQL Servaeなど多くのデータベースに対応してることで有名ですが、Zend Frameworkでもこれらのデータベースを利用可能です。さらにZend Frameworkでは、これらの個々のデータベースを意識することなく汎用的なコードを記述することが可能なデータベース抽象化レイヤを提供しています。データベース抽象化レイヤはZend_Dbコントコンポーネントによって提供されます。

 たとえば、PHPでは、MySQLデータベースに接続する場合はmysql_conect()関数を使い、PostgrSQLデータベースに接続する場合は、pg_connect()関数を使い分ける必要がありますが、Zend_Dbではいずれの場合も汎用的なgetConnectionメソッドを使えば後の個々のデータベースへの接続処理はZend_Dbが内部で処理してくれます。

                データベース抽象化レイヤ(Zend_Db)

 したがって、アプリケーション開発者は、データベース名やパスワードなど最初の接続情報だけを記述すれば、あとは個々のデータベースを意識する必要がなくなります。別の言い方をすれば、Zend_Dbを使えば、どのようなデータベースにも対応可能な汎用的なアプリケーションプログラムを作成することができます。

■php.iniの設定

 これらのアダプタを利用するためには、DBMS用のPDOドライバを利用できるようにしておく必要があります。ここではデータベースとしてMySQLを使うこととし、MySQL用アダプタを利用する場合は、php.iniの中の以下2行を有効にします。

extension=php_pdo.dll
extension=php_pdo_mysql.dll

■サンプルデータベース

 ここでは、データベースとしてdb_zend、テーブルとして以下の電話帳テーブルtbl_telを使います。

フィールド名 データ型 概要
tel_id INT auto_increment

電話番号ID(主キー、自動連番)

name

VARCHAR(64)

氏名
tel_number VARCHAR(24) 電話番号
updated DATETIME 更新日

 電話帳テーブルtbl_telの生成用SQLは以下のようになります。

CREATE TABLE tbl_tel (
tel_id int(11) NOT NULL auto_increment,
name varchar(64) ,
tel_number varchar(24) ,
updated datetime ,
PRIMARY KEY (tel_id)
) ENGINE=InnoDB;

INSERT INTO tbl_tel (tel_id, name, tel_number, updated) VALUES
(1, '青木 和夫', '03-1234-5678', '2009-02-20 00:00:00'),
(2, '田中 次郎', '045-678-1234', '2009-02-27 00:00:00'),
(3, '山田太郎', '048-765-4321', '2009-01-03 00:00:00');

■データベースへの接続

 Zend Frameworkでデータベースを利用するにはまずデータベースに接続する必要があります。データベースに接続するためのコードの例を次に示します。とりあえずは、MVCモデルでなく、単なるPHPプログラムconnect.phpで実行してみます。

c:\Apache2.2\htdocs\zf\db\01\connect.php

<?php
//Zend_Dbコンポーネントの呼び出し
require_once 'Zend/Db.php';

// データベースへの接続情報を設定
$db_info = array('host' => 'localhost',        // データベースのホスト名
         'username' => 'webapl',       // データベースのユーザ名
         'password' => 'pass1234',      // ユーザノパスワード
         'dbname' => 'db_zend');       // データベース名

try {
  //接続情報にもとづいて、アダプタクラスを生成
  $db = Zend_Db::factory('Pdo_Mysql',$db_info);

  //データベースへの実際の接続を確立
  $db->getConnection();
  print('データベースへの接続に成功しました。');
} catch (Zend_Exception $e) {
  //例外発生時のエラーメッセージを表示
  die($e->getMessage());
}

//データベースへの接続を切断
$db->closeConnection();

 Zend_Dbコンポーネントで、データベースへ接続するには、factoryメソッドを使います。factoryメソッドの構文は次のとおりです。

Zend_Db::factory('アダプタ名l',接続情報の連想配列)

 上記リストでは、MySQLアダプタPdo_Mysqlを使用し、接続情報を連想配列$db_infoに格納しています。

$db = Zend_Db::factory('Pdo_Mysql',$db_info);

 データベースへの接続情報は、以下のように連想配列に設定します。

連想配列 = array('host' => 'ホスト名',
           'username' => ユーザ名',
           'password' => 'パスワード',
           'dbname' => 'データベース名');

 Zend_Dbでは、一般にクエリを発行する時点で、自動的にデータベースに接続されます。ここでは、クエリを発行しないので、明示的にgetConnectionメソッドで、データベースに接続しています。

 Zend Frameworkでは、クラスで発生した例外処理は、すべてZend_Exceptionで把握できます。その構文は次のとおりです。

try {
  //処理
} catch (Zend_Exception $e) {
  //例外発生時の処理
}

 connect.phpには直接URL「http://localhost:8080/zf/db/01/connect.php」でアクセスします。正常に接続されれば以下のように表示されます。

 たとえば、パスワードが正しくなかったりして、正常に接続されないと、以下のようにエラーメッセージが表示されます。

■データベース接続情報の外部ファイル化

 データベース接続情報を個々のアプリケーションスクリプト内に記述すると、保守性やセキュリティ上問題があります。そこで、データベース接続情報を外部ファイル化して1箇所におき、各アプリケーションから共通に利用する方法があります。そのサンプルリストを以下に示します。

c:\Apache2.2\htdocs\zf\db\01\connect_ini.php

<?php
//Zend_Configコンポーネントの呼び出し
require_once 'Zend/Config/Ini.php';
//Zend_Dbコンポーネントの呼び出し
require_once 'Zend/Db.php';

try {
  //外部ファイルzend.iniをロードしZend_Configオブジェクトを生成
  $config = new Zend_Config_Ini('../../../../zendapps/db/zend.ini','db_zend');
  //Zend_Configオブジェクトを使って、アダプタクラスを生成
  $db = Zend_Db::factory($config);

  //データベースへの実際の接続を確立
  $db->getConnection();
  print('データベースへの接続に成功しました。');
} catch (Zend_Exception $e) {
  //例外発生時のエラーメッセージを表示
  die($e->getMessage());
}

//データベースへの接続を切断
$db->closeConnection();

c:\Apache2.2\zendapps\db\zend.ini

[db_zend]
adapter = Pdo_Mysql
params.host = localhost
params.dbname = db_zend
params.username = webapl
params.password = pass1234

 この例では、データベースへの接続情報をzend.iniというZend Frameworkに対応した外部ファイルに格納しています。設定ファイル「.ini」ファイルの記法は次のとおりです。

・設定情報は「設定名=値」の形式で定義
・設定名は「.」(ピリオド)で区切り、階層化することが可能
・セクション名は「[○○]」(ブランケット)で囲む
・コメント行は先頭を「;」(セミコロン)ではじめる

 設定ファイルzend.iniファイルの配置ディレクトリは任意ですが、ここではアプリケーションディレクトリ「c:\Apache2.2\zendapps\db\」に配置しています。

 設定ファイル(「.ini」ファイルを呼び出すには、まず、Zend/Config/Ini.phpコンポーネントを呼び出します。その上で、以下の構文により Zend_Config_Iniオブジェクトを生成します。

オブジェクト = new Zend_Config_Ini('設定ファイルへのパス','読み込むセクション名')

 Zend_Config_Iniオブジェクトを引数とした場合のfactoryメソッドの構文は、次のとおりです。factoryメソッドの第2引数がない場合、factoryメソッドは引数をオブジェクトとみなします。

Zend_Db::factory(オブジェクト)

 ここでは、次のようにしています。

$config = new Zend_Config_Ini('../../../../zendapps/db/zend.ini','db_zend');
$db = Zend_Db::factory($config);

 connect_ini.phpファイルへのアクセス結果は、次のようになります。

 設定ファイル内の設定名を階層化した例を次に示します。ここでは、「db」という階層を設け、その配下に設定名を定義します。

c:\Apache2.2\zendapps\db\zend_db.ini

[db_zend]
db.adapter = Pdo_Mysql
db.params.host = localhost
db.params.dbname = db_zend
db.params.username = webapl
db.params.password = pass1234

 Zend_Configオブジェクトを使って、アダプタクラスを生成するコードは次のように「$config->db」とします。

$db = Zend_Db::factory($config->db);

■データベース接続コードの外部化

 データベース接続のコードをさらに、クラスとして外部化し、簡潔にデータベースに接続できるようにします。

c:\Apache2.2\htdocs\zf\db\01\DbManager.class.php

<?php
//Zend_Configコンポーネントの呼び出し
require_once 'Zend/Config/Ini.php';
//Zend_Dbコンポーネントの呼び出し
require_once 'Zend/Db.php';

class DbManager {
  public static function getConnection() {
    $db = NULL;
    try {
      //外部ファイルzend.iniをロードしZend_Configオブジェクトを生成
      $config = new Zend_Config_Ini('../../../../zendapps/db/zend.ini','db_zend');
      //Zend_Configオブジェクトを使って、アダプタクラスを生成
      $db = Zend_Db::factory($config->db);

      //データベース接続時の文字コードをutf8に設定
      $db->query('SET CHARACTER SET utf8');
    } catch (Zend_Exception $e) {
      //例外発生時のエラーメッセージを表示
      die($e->getMessage());
    }
    return $db;
  }
}

 データベースにクエリを発行する時、自動的にデータベースに接続処理を行います。また、データベース接続時の文字コード設定は文字化けを防ぐために、データベース接続確立直後に一度行う必要があります。そこで、ここでは、getConnectionメソッドのかわりに、文字コード設定のクエリを発行し、同時にデータベースへの接続も行うこととしています。クエリ発行ウィザードのqueryメソッドの構文は次のとおりです。$dbはアダプタクラスの例です。

$db->query('クエリ')

 DbManagerクラスを使ってデータベースへ接続するには、DbManager.class.phpをインクルードし、次のコードを記述します。

$db = DbManager::getConnection();

 たとえば、以下のスクリプトでDbManagerクラスの使用法を確認してみます。

c:\Apache2.2\htdocs\zf\db\01\connect_class.php

<?php
//
require_once 'DbManager.class.php';

$db = DbManager::getConnection();

 connect_class.phpへのアクセス結果は、次のようになります。エラーもメッセージも何も表示されなければ正常にデータベースに接続されています。

■レコード検索

 データベースに接続ができたら、次にテーブルのデータ処理の例を示します。まずテーブルのデータ処理のもっとも一般的なSELECTクエリにyる全レコードデータの取得のselect_allアプリケーションの例を以下に示します。

c:\Apache2.2\htdocs\zf\db\01\select_all.php

<?php
//データベース接続クラス(ユーザ定義)のロード
require_once 'DbManager.class.php';
//データベースに接続
$db = DbManager::getConnection();
//クエリ(SQL文)の定義
$sql = 'select * from tbl_tel;';
//クエリの発行と結果取得
$result = $db->fetchAll($sql);
//取得結果の表示
foreach ($result as $row) {
  //1レコード分を表示
  echo $row['name'] .':' . $row['tel_number'] . '<br/>';
}

 Zend_Dbではレコードデータの取得にはqueryメソッドも使えますが、単にレコードデータの取得だけであればfetchAllメソッドが利用できます。

SELECT文で、データベースからレコードを取得した結果は、仮想的なテーブルとしてメモリ上に一時的に保持されます。この仮想的なテーブルを「結果セット」といいます。この結果セットから指定のレコードを取り出すことをフェッチするといいます。fetchAllメソッドは、SELECT文のクエリを発行し結果セットを取得するとともに、結果セットの中のすべてのレコードをフェッチしてくれます。

 fetchAllメソッドの構文例は次のとおりです。$resultは各レコードと各フィールドの2次元連想配列、$dbはアダプタクラス、$sqlはクエリ(SQL文)です。

$result = $db->fetchAll($sql);

 select_all.phpにアクセスした結果は以下のとおりです。

■MVCモデル

 上記select_allアプリケーションをMVCモデルでコード化した例を以下に示します。ここでは、ディレクトリ構造を以下のようにしています。

c:\Apache2.2\htdocs\zf\db\02
  .htaccess          
  index.php   
       
c:\Apache2.2\zendapps\db
  zend.ini
  \02
    \controllers        
      IndexController.php   
    \models   
      DbManager.class.php
      telModel.php     
    \views           
      \scripts        
        \index       
          index.phtml   

公開ディレクトリ
Rewriteエンジン制御ファイル
フロントコントローラファイル

アプリケーションディレクトリ
設定ファイル
アプリケーションルートディレクトリ
コントローラディレクトリ
デフォルトアクションコントローラファイル
モデル用ディレクトリ
データベース接続用クラス
電話帳モデル
ビュー関連ディレクトリ
ビュースクリプトディレクトリ
デフォルトアクションコントローラ用ディレクトリ
デフォルトアクション用ビュースクリプト

 select_allアプリケーション用の公開ディレクトリは、「ドキュメントルートディレクトリ\db\02\」としています。.htaccessファイルは次のとおりです。

c:\Apache2.2\htdocs\zf\db\02\.htaccess

RewriteEngine on
RewriteBase /zf/db/02
RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php

 フロントコントローラファイルindex.phpは次のとおりです。

c:\Apache2.2\htdocs\zf\db\02\index.php

<?php
//フロントコントローラ用のコンポーネントの呼び出し
require_once 'Zend/Controller/Front.php';
/**フロントコントローラのインスタンスの取得
 *コントローラのフォルダ指定
 *ディスパッチ
 */
Zend_Controller_Front::run('../../../../zendapps/db/02/controllers');

 コントローラ(C)部分のデフォルトアクションコントローラファイルIndexController.phpは次のとおりです。

c:\Apache2.2\zendapps\zf\db\02\controllers\IndexController.php

<?php
// コンポーネントのロード
require_once 'Zend/Controller/Action.php';
// 電話帳モデルのロード
require_once '../../../../zendapps/db/02/models/telModel.php';
// デフォルトアクションコントローラクラスを生成
class IndexController extends Zend_Controller_Action {
  // 電話帳モデル用インスタンスのためのメンバー変数の定義
  private $_tel;
  // デフォルトアクションの定義
  public function indexAction() {
    // 電話帳モデル用インスタンスの生成
    $this->_tel = new telModel();
    // 電話帳データを取得
    $result = $this->_tel->selectTel();
    // ビュー変数resultに取得データ$resultを設定
    $this->view->result = $result;
  }
}

 アクションコントローラでは、モデル(M)との連携に関して、以下のコードを記述します。

・電話帳モデルファイルtelModel.phpをロードする。kkk
  (例)require_once '../../../../zendapps/db/02/models/telModel.php';
・電話帳モデル用インスタンスのためのメンバー変数の定義する。
  (例)private $_tel;
・電話帳モデル用インスタンスを生成する。
  (例)$this->_tel = new telModel();
・電話帳モデル内で定義したメソッドを使って、データベース処理を行う。
  (例)$result = $this->_tel->selectTel();

 モデル(M)部分の電話帳モデルtelModel.phpは次のとおりです。Zend_Dbコンポーネントのロードは、先に作成したデータベース接続用クラスDbManage.class.phpで行っているので、ここでは省略します。

c:\Apache2.2\zendapps\zf\db\02\models\telModel.php

<?php
//データベース接続クラス(ユーザ定義)のロード
require_once 'DbManager.class.php';
// 電話帳モデルをクラスとして定義
class telModel {
  // データベースアダプタ用メンバー変数を定義
  private $_db;
  // 全レコード抽出メソッドの定義
  public function selectTel() {
    // データベースへの接続
    $this->_db = DbManager::getConnection();
    // 全レコード抽出クエリの作成
    $sql = 'select * from tbl_tel;';
    //クエリの発行と結果取得
    $result = $this->_db->fetchAll($sql);
    return $result;
  }
}

 データベースによるデータ管理のコード群をクラスとして定義します。これがモデル(M)になります。データベースの検索、入力、更新、削除などをおのおのモデル内のメソッドとして定義します。ここでは、電話帳モデルをtelModelとします。

 ビュー(V)の部分は、各コントローラの各アクションごとにビュー形式「.phtml」ファイルで定義します。

c:\Apache2.2\zendapps\zf\db\02\views\script\index\index.phtml

<html>
<head>
<title>db_02</title>
</head>
<body>
検索結果<br/>
<?php
foreach ($this->result as $rows){
 print $rows['name'] . ':' . $rows['tel_number'] . '<br/>';
}
?>
</body>
</html>

 URL「http://localhost:8080/zf/db/02/」でselect_allアプリケーションにアクセスした結果は、次のとおりです。

■アプリケーションディレクトリ定数

 フロントコントローラファイルの中での、アクションコントローラディレクトリの指定

Zend_Controller_Front::run('../../../../zendapps/db/02/controllers')

とか、アクションコントローラ内でのモデルのロード時のモデル用ディレクトリの指定

require_once '../../../../zendapps/db/02/models/telModel.php';

などで、アプリケーションディレクトリのパスを何回も参照します。そこで、このように多用する値をユーザ定義定数として定義し、使用するように変更します。そこで、フロントコントローラファイル(index.php)内で、以下のようにアプリケーションディレクトリのパスAPP_DIRを定義します。

define('APP_DIR','../../../../zendapps/db/02/');

 定数APP_DIRを使った、select_allアプリケーションの例を以下に示します。アプリケーションルートディレクトリは「ドキュメントルートディレクトリ\db\03」とします。フロントコントローラファイル(index.php)は次のようになります。

c:\Apache2.2\htdocs\zf\db\03\index.php

<?php
//アプリケーションルートパスの定義
define('APP_DIR','../../../../zendapps/db/03/');
//フロントコントローラ用のコンポーネントをロード
require_once 'Zend/Controller/Front.php';
//アクションコントローラ用のコンポーネントをロード
require_once 'Zend/Controller/Action.php';

/**フロントコントローラのインスタンスの取得
 *コントローラのフォルダ指定
 *ディスパッチ
 */
Zend_Controller_Front::run(APP_DIR . 'controllers');

 なお、アクションコントローラ内等でロードする各種コンポーネントも、フロントコントローラ内でまとめてロードするようにしています。

 デフォルトアクションコントローラファイルIndexController.phpは定数APP_DIRを使用して次のようにします。念のため、定数APP_DIRの値をダンプしてみます。

c:\Apache2.2\zendapps\zf\db\03\controllers\IndexController.php

<?php
// コンポーネントのロード
require_once 'Zend/Controller/Action.php';
// 電話帳モデルのロード
var_dump(APP_DIR);
require_once APP_DIR . 'models/telModel.php';
// デフォルトアクションコントローラクラスを生成
class IndexController extends Zend_Controller_Action {
  // 電話帳モデル用インスタンスのためのメンバー変数の定義
  private $_tel;
  // デフォルトアクションの定義
  public function indexAction() {
    // 電話帳モデル用インスタンスの生成
    $this->_tel = new telModel();
    // 電話帳データを取得
    $result = $this->_tel->selectTel();
    // ビュー変数rsに取得データ$resultを設定
    $this->view->result = $result;
  }
}

 定数APP_DIRを使ったselect_allアプリケーションへのアクセス結果は次のとおりです。

■「?」パラメータ

 Webアプリケーションでは、クライアントから入力されたデータをパラメータとして、動的にクエリを構成し、データベースを検索したり入力したりすることがよくあります。Zend_Dbでは、このような動的に変化するパラメータを扱う方法の一つに「?」パラメータがあります。以下、クライアントから入力した氏名の一部をキーに電話帳を検索するselect_nameアプリケーションを示します。

c:\Apache2.2\zendapps\zf\db\04\controllers\IndexController.php

<?php
// 電話帳モデルのロード
require_once APP_DIR . 'models/telModel.php';
// デフォルトアクションコントローラクラスを生成
class IndexController extends Zend_Controller_Action {
  // 電話帳モデル用オブジェクト(インスタンス)のためのメンバー変数の定義
  private $_tel;

  // 初期化メソッドの定義
  public function init() {
    // 電話帳モデル用オブジェクト(インスタンス)の生成
    $this->_tel = new telModel();
  }

  // デフォルト(index/index)アクションの定義
  public function indexAction() {
    // 検索キーワード入力画面表示
    // (index/index.phtml)
  }

  // 検索(index/search)アクションの定義
  public function searchAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // POSTデータの受信
    $key =$request->getPost('key') ;
    // 電話帳データを取得
    $result = $this->_tel->searchNameTel($key);
    // ビュー変数の設定
    $this->view->result = $result;
    // 検索結果の表示
    // (index/search.phtml)
  }
}

 indexコントローラ内に、初期化initメソッドを定義し、電話帳モデル用オブジェクト(インスタンス)を生成する。

アクションコントローラ内では、以下の順で処理が実行されます。

@__construcメソッド      オブジェクト(インスタンス)が生成されたとき
                一度だけ実行される。
Ainitメソッド         アクションコントローラの初期化処理を行う。
BpreDispatchメソッド      アクション実行前に処理される
Cアクションメソッド      個別のアクション
DpostDispatchメソッド     アクション実行後に処理される

 indexコントローラのindexアクション(以下これをindex/indexアクションと記す)では、検索キーワード入力画面を表示する。対応するビュースクリプトはindex/index.phtmlとなる。

 index/searchアクションでは、クライアントからの検索キーをPOSTデータとして受信し、電話帳モデル(telModel)内のsearchNameTelメソッドで電話帳の全データを取得し、その結果を対応するビュースクリプトindex/search.phtmlに渡す。

c:\Apache2.2\zendapps\zf\db\04\models\telModel.php

<?php
//データベース接続クラス(ユーザ定義)のロード
require_once 'DbManager.class.php';

// 電話帳モデルをクラスとして定義
class telModel {
  // データベースアダプタ用メンバー変数を定義
  private $_db;

  // コンストラクタの定義
  public function __construct() {
    // データベースへの接続
    $this->_db = DbManager::getConnection();
  }

  // 検索メソッドの定義
  public function searchNameTel($key) {
    // 「?」パラメータを使ったクエリの作成
    $sql = 'select * from tbl_tel where name = ? ;';
    //クエリの発行と結果取得
    $result = $this->_db->fetchAll($sql,$key);
    return $result;
  }
}

 電話帳モデルtelModelでは、コンストラクタを定義し、データベースへの接続を行うこととします。検索メソッドsearchNameTelには引数$keyを定義し、index/indexアクションから検索キーを受け取るようにします。Zend_Dbではこのような変数を含むクエリを作成するには、変数部分を以下のように「?」とします。これを「?」パラメータと呼びます。

$sql = 'select * from tbl_tel where name = ? ;';

 「?」パラメータへの具体的な値の設定は、以下のようにfetchAllメソッドの第2引数で行います。

$result = $this->_db->fetchAll($sql,$key);

 クエリに2個以上の「?」パラメータがある場合は、第2引数は配列とします。なお、「?」パラメータが1個の場合も以下のように配列を使うことができます。

$result = $this->_db->fetchAll($sql,array($key));

 各アクションに対応して、以下のビュースクリプトを作成します。

c:\Apache2.2\zendapps\zf\db\04\views\script\index\index.phtml

<html>
<head>
<title>select_name</title>
</head>
<body>
<!-- index/searchアクションにPOSTデータを送信-->
<form method="POST" action="/zf/db/05/index/search">
氏名:<input type="text" name="key" size="10">
<input type="submit" value="検索">
</form>
</body>
</html>

c:\Apache2.2\zendapps\zf\db\04\views\script\index\search.phtml

<html>
<head>
<title>select_name</title>
</head>
<body>
検索結果<br/>
<?php
foreach ($this->result as $rows){
  print $rows['name'] . ':' . $rows['tel_number'] . '<br/>';
}
?>
</body>
</html>

 MVCモデルにおける相互関連は次のようになります。

アクションコントローラ/アクション(C) モデル(M) ビュー(V)
(->:フォアワード)
概要
index/index なし index/index.phtml 検索キー入力画面表示
index/search searcNameTel index/search.phtml 電話帳検索結果表示

■名前つきパラメータ

 プレイスホルダとして、「?」パラメータのかわりに、名前つきパラメータを使うこともできます。名前つきパラメータは以下のように記述します。

「:」(コロン)+パラメータ名

(例) :key

 上記select_nameアプリケーションを、名前つきパラメータで記述するには、電話帳モデルtelModelの、下記クエリ部分のみ書き換えます。

c:\Apache2.2\zendapps\zf\db\05\models\telModel.php

<?php
//データベース接続クラス(ユーザ定義)のロード
require_once 'DbManager.class.php';

// 電話帳モデルをクラスとして定義
class telModel {
  // データベースアダプタ用メンバー変数を定義
  private $_db;

  // コンストラクタの定義
  public function __construct() {
    // データベースへの接続
    $this->_db = DbManager::getConnection();
  }

  // 検索メソッドの定義
  public function searchNameTel($key) {
    // 名前つきパラメータを使ったクエリの作成
    $sql = 'select * from tbl_tel where name = :key ;';
    //クエリの発行と結果取得(名前つきパラメータは連想配列で渡す)
    $result = $this->_db->fetchAll($sql,array(':key' => $key));
    return $result;
  }
}

 クエリを発行する場合、fetchAllメソッドの第2引数には、連想配列を使います。

名前つきパラメータに値を渡すときの連想配列のキー値は「array(':key' => $key)」のようにパラメータ名の前に「:」を付加しますが、「:」を付加しなくて「array(:key' => $key)」のように記述しても動作します。

■複数の単語を含むアクション名

 氏名の一部が一致している電話帳レコードを検索する部分一致検索(あいまい検索)を行うselect_likeアプリケーションの例を次に示します。

c:\Apache2.2\zendapps\zf\db\06\controllers\IndexController.php

<?php
// 電話帳モデルのロード
require_once APP_DIR . 'models/telModel.php';
// デフォルトアクションコントローラクラスを生成
class IndexController extends Zend_Controller_Action {
  // 電話帳モデル用オブジェクト(インスタンス)のためのメンバー変数の定義
  private $_tel;

  // 初期化メソッドの定義
  public function init() {
    // 電話帳モデル用オブジェクト(インスタンス)の生成
    $this->_tel = new telModel();
  }

  // デフォルト(index/index)アクションの定義
  public function indexAction() {
    // 検索キーワード入力画面表示
    // (index/index.phtml)
  }

  // 検索(index/search)アクションの定義
  public function searchAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // POSTデータの受信
    $key =$request->getPost('key') ;
    // 電話帳データを取得
    $result = $this->_tel->searchNameTel($key);
    // ビュー変数の設定
    $this->view->result = $result;
    // 検索結果の表示
    // (index/search.phtml)
  }

  // 検索(index/search-like)アクションの定義
  public function searchLikeAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // POSTデータの受信
    $key =$request->getPost('key') ;
    // あいまい検索により電話帳データを取得
    $result = $this->_tel->searchNameLikeTel($key);
    // ビュー変数の設定
    $this->view->reslut = $result;
    // 検索結果の表示
    // (index/search-like.phtml)
  }

}

 indexコントローラにあいまい検索のためのsearchLikeActionメソッドを追加します。この中であいまい検索で電話帳データを取得するsearchNameLikeTel($key)メソッド(電話帳モデルtelModel内で定義)を使っています。searchLikeActionメソッドは「search」と「Like」という2つの単語をアクション名に含んでいます。

 このようにアクションコントローラ内のアクション名(メソッド名)に複数の単語を含む場合、Zend Frameworkでは対応するビュースクリプト名とHTMLフォームにおけるactionのURIの文字列は次のように変換する必要があります。このルールはアクションコントローラ名(クラス名)にも同じように適用されます。

・アクション名のすべての文字列は小文字にする。
・CameCase記法の単語の区切りに「-」(ハイフン)を挿入する。「.」(ピリオド)でもよい。

 例を以下に示します。

コントローラ/アクション名 ビュースクリプト名/アクションのURI
searchLikeAction search-like
search.like
MyIndexContorller my-index
my.index
updateTelNameAction update-tel-name
update.tel.name

 したがって、IndexControllerコントローラのsearchLikeActionに対応するビュースクリプトのディレクトリおよびファイル名は「index/search-like.phtml」となり、HTMLにおけるフォームのactionのURLの文字列は「〜/index/search-like」となります。

 電話帳モデル(M)に、あいまい検索メソッドを追加します。

c:\Apache2.2\zendapps\zf\db\06\models\telModel.php

<?php
//データベース接続クラス(ユーザ定義)のロード
require_once 'DbManager.class.php';

// 電話帳モデルをクラスとして定義
class telModel {
  // データベースアダプタ用メンバー変数を定義
  private $_db;

  // コンストラクタの定義
  public function __construct() {
    // データベースへの接続
    $this->_db = DbManager::getConnection();
  }

  // 完全一致検索メソッドの定義
  public function searchNameTel($key) {
    // 「?」パラメータを使ったクエリの作成
    $sql = 'select * from tbl_tel where name = ? ;';
    //クエリの発行と結果取得
    $result = $this->_db->fetchAll($sql,$key);
    return $result;
  }

  // あいまい検索メソッドの定義
  public function searchNameLikeTel($key) {
    // あいまい検索の構文に設定
    $key = "%{$key}%";
    // 「?」パラメータを使ったクエリの作成
    $sql = 'select * from tbl_tel where name like ? ;';
    //クエリの発行と結果取得
    $result = $this->_db->fetchAll($sql,$key);
    return $result;
  }
}

 index/indexビュースクリプトに、あいまい検索用キー入力フォームを追加します。

c:\Apache2.2\zendapps\zf\db\06\views\script\index\index.phtml

<html>
<head>
<title>select_name</title>
</head>
<body>
<!-- index/searchアクションにPOSTデータを送信-->
<form method="POST" action="/zf/db/06/index/search">
氏名:<input type="text" name="key" size="10">
<input type="submit" value="検索">
</form>
<!-- index/search-likeアクションにPOSTデータを送信-->
<form method="POST" action="/zf/db/06/index/search-like">
氏名(部分):<input type="text" name="key" size="10">
<input type="submit" value="検索">
</form>
</body>
</html>

 POSTメソッドのactionのURIはindexControllerコントローラのsearchLikeActionアクションを起動するために次のようにします。

action="/zf/db/06/index/search-like"

 select_likeアプリケーションへのアクセス結果は次のようになります。

■表形式で表示

 上記select_likeアプリケーションの検索結果を表形式で表示するには、ビュースクリプトindex/search-like.phtmlを下記のようにします。

c:\Apache2.2\zendapps\zf\db\07\views\script\index\search-like.phtml

<html>
<head>
<title>select_name</title>
</head>
<body>
検索結果<br/>
<table border="1" cellspacing="0" cellpadding="0">
 <tr>
  <th>id</th>
  <th>氏名</th>
  <th>電話番号</th>
  <th>更新日</th>
 </tr>
<?php foreach ($this->result as $rows) { ?>
 <tr>
  <td><?php print $rows['tel_id']; ?></td>
  <td><?php print $rows['name']; ?></td>
  <td><?php print $rows['tel_number']; ?></td>
  <td><?php print $rows['updated']; ?></td>
 </tr>
<?php } ?>
</table>
</body>
</html>

 結果は、次のようになります。

■レコードの登録

 電話帳に新たにレコードを登録するinsert_telアプリケーションの例を以下に示します。

  デフォルトアクションコントローラ(C)に登録(index/insert)アクションを定義します。

c:\Apache2.2\zendapps\zf\db\08\controllers\IndexController.php

<?php
// 電話帳モデルのロード
require_once APP_DIR . 'models/telModel.php';
// デフォルトアクションコントローラクラスを生成
class IndexController extends Zend_Controller_Action {
  // 電話帳モデル用オブジェクト(インスタンス)のためのメンバー変数の定義
  private $_tel;

  // 初期化メソッドの定義
  public function init() {
    // 電話帳モデル用オブジェクト(インスタンス)の生成
    $this->_tel = new telModel();
  }

  // デフォルト(index/index)アクションの定義
  public function indexAction() {
    // 電話帳データ入力画面表示
    // (index/index.phtml)
  }

  // 登録(index/insert)アクションの定義
  public function insertAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // POSTデータの受信(アンエスケープ処理も行う)
    $var = array();
    $var['name'] = stripslashes($request->getPost('name'));
    $var['tel_number'] = stripslashes($request->getPost('tel_number')) ;

    // 現在の年月日、時刻を取得
    $var{':updated'] = date('Y-m-d H:i:s');

    // 電話帳データを登録
    $num= $this->_tel->insertTel($var);
    // 電話帳データを取得
    $result = $this->_tel->searchAllTel();
    // ビュー変数の設定
    $this->view->result = $result;
    // 検索結果の表示
    // (index/search.phtml)
  }
}

 POSTメソッドで受信したデータは、PHPのstripslashes関数でアンエスケープ処理をしておきます。また、データベースに登録するデータは名前つきパラメータの形式で、連想配列に格納します。データベースへの登録は、電話帳モデル内のinsertTelメソッドで行います。

$this->_tel->insertTel($var);

 電話帳モデル(M)に、登録メソッドを定義します。

c:\Apache2.2\zendapps\zf\db\08\models\telModel.php

<?php
//データベース接続クラス(ユーザ定義)のロード
require_once 'DbManager.class.php';

// 電話帳モデルをクラスとして定義
class telModel {
  // データベースアダプタ用メンバー変数を定義
  private $_db;

  // コンストラクタの定義
  public function __construct() {
    // データベースへの接続
    $this->_db = DbManager::getConnection();
  }
  // 登録メソッドの定義
  public function insertTel(&$tel) {
    // パラメータ付き登録クエリの作成
    $sql = 'insert into tbl_tel(name,tel_number,updated)'
      . 'value(:name,:tel_number,:updated);';

    // パラメータ付き登録クエリの発行
    $result = $this->_db->query($sql
           ,array(

                ':name' => $tel['name']
               ,':tel_number' => $tel['tel_number']
               ,':updated' => $tel['updated']
              )
           );

    $num = $result->rowCount();
    return $num;
  }
  // 全レコード検索メソッドの定義
  public function searchAllTel() {
    // クエリの作成(更新日の降順にソート)
    $sql = 'select * from tbl_tel order by updated desc;';
    //クエリの発行と結果取得
    $result = $this->_db->fetchAll($sql);
    return $result;
  }
}

 電話帳モデル(M)の中に、データ登録のためのinsertTelメソッドを定義します。INSERT文では、(プレイスホルダーに名前つきパラメータを使用しています。プレイスホルダーを使用すると、SQLインジェクション脆弱性対策のクオート処理を次のqueryメソッドの中で自動的に行ってくれます。

 SELECT文のクエリの場合は結果セットが生成されるで、fetchAllメソッドなどのフェッチ関連メソッドが使えますが、INSERT文などSELECT文以外のクエリの場合は、クエリの発行だけを行うqueryメソッドを使います。queryメソッドの構文例は次のとおりです。

$db->query($sql);
あるいは、
$result = $db->query($sql);

 $dbはアダプタクラス、$sqlはクエリ(SQL文)です。変更したレコード数などクエリの結果を取得するときは、結果状態$result変数を使います。

 全レコードの検索メソッドsearchAllTelでは、レコードをフィールドupdatede(更新日)の降順にソートすることとしています。

  index/indexビュースクリプトでは、電話帳データ登録用フォームを記述します。

c:\Apache2.2\zendapps\zf\db\08\views\script\index\index.phtml

<html>
<head>
<title>insert_tel</title>
</head>
<body>
<!-- index/insertアクションにPOSTデータを送信-->
<form method="POST" action="index/insert">
<table border="0">
 <tr>
  <th>氏名</th>
  <td><input type="text" name="name" size="10"></td>
 </tr>
 <tr>
  <th>電話番号</th>
  <td><input type="text" name=tel_number" size="10"></td>
 </tr>
 <tr>
  <th></th>
  <td><input type="submit" value="登録"></td>
    <input type="reset" value="リセット"></td>
 </tr>
</table>
</form>
</body>
</html>

 ビュースクリプトindex/insert.phtmlでは、登録後の確認用に電話帳の全レコード表示フォームを記述します。

c:\Apache2.2\zendapps\zf\db\08\views\script\index\insert.phtml

<html>
<head>
<title>insert_tel</title>
</head>
<body>
検索結果<br/>
<table border="1" cellspacing="0" cellpadding="0">
 <tr>
  <th>id</th>
  <th>氏名</th>
  <th>電話番号</th>
  <th>更新日</th>
 </tr>
 <?php foreach ($this->result as $rows) { ?>
 <tr>
  <td><?php print $rows['tel_id']; ?></td>
  <!-- ユーザ入力データの表示にはサニタイジングを行う -->
  <td><?php print $this->escape($rows['name']); ?></td>
  <td><?php print $this->escape($rows['tel_number']); ?></td>
  <td><?php print $rows['updated']; ?></td>
 </tr>
 <?php } ?>
</table>
</body>
</html>

 フィールドname(氏名)やフィールドtel_number(電話番号)のようにユーザ入力データの表示にはSQLインジェクション脆弱性の対策のためにサニタイジングを行います。

 insert_telアプリケーションのアクセス例を以下に示します。

 特殊な例として、氏名に「'」(シングルクオート)を含む文字列の登録、表示の例を以下に示します。

 「'」(シングルクオート)を含む文字列の登録、表示が正常に処理されていることが確認できます。

■データチェック

 Webアプリケーションでは、フォームから入力したデータのWebサーバ側での妥当性チェックはエラー防止やセキュリティ対策の面から極めて重要です。このようなデータの検証ツールを「バリデータ」といいます。Zend FrameworkではZend_Validateコンポーネントがバリデータ機能を提供します。

 Zend_Validateの使い方は次のとおりです。

@バリデータの個々のコンポーネントをロードする。
Aバリデータのインスタンスを生成する。
B必要に応じて、表示用エラーメッセージを設定する。
CisValidateメソッドでチェック処理を行う。

 デフォルトアクションコントローラ(C)に以下の太字のコードを追加します。登

c:\Apache2.2\zendapps\zf\db\09\controllers\IndexController.php

<?php
// 電話帳モデルのロード
require_once APP_DIR . 'models/telModel.php';
// デフォルトアクションコントローラクラスを生成
class IndexController extends Zend_Controller_Action {
  // 電話帳モデル用オブジェクト(インスタンス)のためのメンバー変数の定義
  private $_tel;

  // 初期化メソッドの定義
  public function init() {
    // 電話帳モデル用オブジェクト(インスタンス)の生成
    $this->_tel = new telModel();
  }

  // デフォルト(index/index)アクションの定義
  public function indexAction() {
    // 電話帳データ入力画面表示
    // (index/index.phtml)
  }

  // 登録(index/insert)アクションの定義
  public function insertAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // Zend_Validateコンポーネントのロード
    require_once 'Zend/Validate/NotEmpty.php';
    // バリデータのインスタンスを生成する
    $notEmpty = new Zend_Validate_NotEmpty;

    // POSTデータの受信(アンエスケープ処理も行う)
    $var['name'] = stripslashes($request->getPost('name')) ;
    $var['tel_number'] = stripslashes($request->getPost('tel_number')) ;

    // エラーメッセージの設定
    $varName = '氏名';
    $notEmpty->setMessage("{$varName}は必須入力です");

    // 空白チェック
    if(!$notEmpty->isValid($var['name'])) {
      foreach($notEmpty->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }


    // エラーメッセージの設定
    $_varName = '電話番号';
    $notEmpty->setMessage("{$varName}は必須入力です");

    // 空白チェック
    if(!$notEmpty->isValid($var['tel_number'])) {
      foreach($notEmpty->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }


    // 現在の年月日、時刻を取得
    $var['updated'] = date('Y-m-d H:i:s');
    if(count($errorMsg)) {
      // バリデートでエラーが発生した場合
      $this->view->errorMsg = $errorMsg;
    } else {
      // 電話帳データを登録
      $num = $this->_tel->insertTel($var);
    }
    // 電話帳データを取得
    $result = $this->_tel->searchALLTel();
    // ビュー変数の設定
    $this->view->result = $result;
    // 検索結果の表示
    // (index/search.phtml)
  }
}

 データチェック時のエラーメッセージを格納する配列$_errorMsgを定義し、初期化メソッドinit内で初期化します。

 登録アクションindex/insert(insertActionメソッド)内で、Zend_Validateコンポーネントの一つ変数が空白か否かをチェックするZend/Validate/notEmpty.phpをロードし、バリデータのインスタンス$notEmptyを生成します。構文例は次のとおりです。

require_once 'Zend/Validate/NotEmpty.php';
$notEmpty = new Zend_Validate_NotEmpty;

 エラーメッセージは既定では英語ですが、以下の構文で日本語に設定することも可能です。ここでは、エラーメッセージ内にチェック対象変数名$_varNameを埋め込んでいます。

$notEmpty->setMessage("{$_varName}は必須入力です");

 チェックは、以下の構文で、isvalidメソッドで行います。空白チェックの場合は、空白チェック用バリデータインスタンス$notEmptyを使います。

$notEmpty->isValid(変数)

 エラーが発生した場合のエラーメッセージは、次の構文でgetMessagesメソッドで取得します。取得結果は配列になります。

$notEmpty->getMessages()

 エラーメッセージをビュー変数にセットし、ビュースクリプトindex/insert,phtmlに渡します。

 ビュースクリプトindex/insert,phtmlにエラーメッセージ表示用コードを挿入します。エラーメッセージは赤字で表示しています。

c:\Apache2.2\zendapps\zf\db\09\views\script\index\insert.phtml

<html>
<head>
<title>insert_tel</title>
</head>
<body>
<?php
if(count($this->errorMsg)) {
  foreach($this->errorMsg as $msg) { ?>
    <span style="color:red"><?php print($msg) ?></span><br/>
<?php
  }
}?>

検索結果<br/>
<table border="1" cellspacing="0" cellpadding="0">
 <tr>
  <th>id</th>
  <th>氏名</th>
  <th>電話番号</th>
  <th>更新日</th>
 </tr>
 <?php foreach ($this->result as $rows) { ?>
 <tr>
  <td><?php print $rows['tel_id']; ?></td>
  <!-- ユーザ入力データの表示にはサニタイジングを行う -->
  <td><?php print $this->escape($rows['name']); ?></td>
  <td><?php print $this->escape($rows['tel_number']); ?></td>
  <td><?php print $rows['updated']; ?></td>
 </tr>
 <?php } ?>
</table>
</body>
</html>

 insert_telアプリケーションの入力画面を表示し、入力欄をすべて未入力のまま、「登録」ボタンをクリックすると、次のようにエラーメッセージが表示されます。

■フォアワード

 入力エラーがあった場合、実用的にはエラーメッセージを表示し、再度入力画面を表示します。そのためには、本アプリケーションではエラーがあった場合は、再度入力画面用スクリプトindex/index.phtmlと同じ画面表示する必要があります。

 Zend Frameworkには、このようなときに使えるフォワード(転送)という機能があります。フォワードはあるアクションを実行中に、別のアクションに処理を移します。かつそのときにリクエストデータを引き継ぐことができます。さらに、setParamメソッドでアクション間でユーザデータを共有することも可能です。

 insert_telアプリケーションでは、入力データにエラーがあった場合には、実行中のindex/insertアクションからindex/indexアクションにフォアワードするようにします。そのときに、リクエストデータを引き継ぎ、とエラーメッセージを共有することとします。

MVCモデルにおける相互関連は次のようになります。

アクションコントローラ/アクション(C) モデル(M) ビュー(V)
(->:フォアワード)
概要
index/index searchALLTel index/index.phtml 電話帳データ入力画面表示
index/insert insertTel

・入力データOK
 index/insert.phtml
・入力データエラー
 ->index/index

・入力データOK
 電話帳データ登録

 フォワードするために、デフォルトアクションコントローラ(C)に以下の太字のコードを追加します。

c:\Apache2.2\zendapps\zf\db\10\controllers\IndexController.php

<?php
// 電話帳モデルのロード
require_once APP_DIR . 'models/telModel.php';
// デフォルトアクションコントローラクラスを生成
class IndexController extends Zend_Controller_Action {
  // 電話帳モデル用オブジェクト(インスタンス)のためのメンバー変数の定義
  private $_tel;

  // 初期化メソッドの定義
  public function init() {
    // 電話帳モデル用オブジェクト(インスタンス)の生成
    $this->_tel = new telModel();
  }

  // デフォルト(index/index)アクションの定義
  public function indexAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // フォアワード後のエラーメッセージの受信
    $errorMsgAll = $request->getUserParam('errorMsgAll');
    // エラーメッセージをビュー変数に設定
    $this->view->errorMsgAll = $errorMsgAll;
    // 電話帳データ入力画面表示
    // (index/index.phtml)
  }

  // 登録(index/insert)アクションの定義
  public function insertAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // Zend_Validateコンポーネントのロード
    require_once 'Zend/Validate/NotEmpty.php';
    // バリデータのインスタンスを生成する
    $notEmpty = new Zend_Validate_NotEmpty;

    // POSTデータの受信(アンエスケープ処理も行う)
    $var['name'] = stripslashes($request->getPost('name')) ;
    $var['tel_number'] = stripslashes($request->getPost('tel_number')) ;

    // エラーメッセージの設定
    $varName = '氏名';
    $notEmpty->setMessage("{$varName}は必須入力です");

    // 空白チェック
    if(!$notEmpty->isValid($var['name'])) {
      foreach($notEmpty->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }

    // エラーメッセージの設定
    $_varName = '電話番号';
    $notEmpty->setMessage("{$_varName}は必須入力です");

    // 空白チェック
    if(!$notEmpty->isValid($var['tel_number'])) {
      foreach($notEmpty->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }

    // 現在の年月日、時刻を取得し、更新日時に設定
    $_var['updated'] = date('Y-m-d H:i:s');

    // エラーの有無のチェック
    if(count($errorMsg)) {
      // バリデートでエラーが発生した場合
      $errorMsgAll = implode('<br/>',$errorMsg);
      $request->setParam('errorMsgAll',$errorMsgAll);
      // indexアクションにフォアワード
      $this->_forward('index');
    } else {
      // 電話帳データを登録
      $num = $this->_tel->insertTel($_var);
      // 電話帳データを取得
      $result = $this->_tel->searchALLTel();
      // ビュー変数の設定
      $this->view->result = $result;
      // 検索結果の表示
      // (index/insert.phtml)
    }
  }
}

 フォアワードは_forwordメソッドで行います。構文は以下のとおりです。

$this->_forward(アクション名、コントローラ名、モジュール名);

 同一コントローラ内であれば、第2引数以降のコントローラ名、モジュール名は省略可能です。同一モジュール内の他のコントローラ/アクションにフォアワードする場合は、第3引数のモジュール名は省略可能です。

 また、index/insertアクションからindex/indexアクションにエラーメッセージを引き継ぎ(共有)表示することとしますが、エラーメッセージ$this->_errorMsgは配列なので、各要素を改行タグ「<br/>」で結合して、文字列に変換し、setParamメソッドを使い以下の構文でパラメータ設定します。

$request->setParam('パラメータ名',パラメータ値);

 引継ぎ先のindex/indexアクション内に、エラーメッセージを受信するコードを以下のように追加します。

$request = $this->getRequest();
$_errorMsgAll = $request->getUserParam('errorMsgAll');
$this->view->errorMsgAll = $_errorMsgAll;

 setParamメソッドで設定されたユーザパラメータは、getUserParamメソッドで以下の構文で取得できます。

$request->getUserParam(パラメータ名)

 ビュースクリプト(index/index.phtml)に、エラーメッセージ表示用スクリプトを挿入します。

c:\Apache2.2\zendapps\zf\db\10\views\script\index\index.phtml

<html>
<head>
<title>insert_tel</title>
</head>
<body>
<!-- index/insertアクションにPOSTデータを送信-->
<p style="color:red"><?php print($this->errorMsgAll); ?></p>
<form method="POST" action="<?php print BASE_DIR ?>index/insert">
<table border="0">
 <tr>
  <th>氏名</th>
  <td><input type="text" name="name" size="10"></td>
 </tr>
 <tr>
  <th>電話番号</th>
  <td><input type="text" name=tel_number" size="10"></td>
 </tr>
 <tr>
  <th></th>
  <td><input type="submit" value="登録"></td>
    <input type="reset" value="リセット"></td>
 </tr>
</table>
</form>
</body>
</html>

 それと、フォームのPOSTメソッドのアクションのURIは、ドキュメントルートのフルパスで指定します。ここの例では

<form metho="POST" action="index/insert">

ではなく、

<form metho="POST" action="/zf/db/10/index/insert">

とします。ここでは、公開ディレクトリのフルパスを定数BASE_DIRであらかじめフロントコントローラ内で定義し次のようにして使っています。

<form method="POST" action="<?php print BASE_DIR ?>index/insert">

 フロントコントローラファイル(index.php)では、公開ディレクトリのフルパス定数BASE_DIRの定義を挿入します。

c:\Apache2.2\htdocs\zf\db\10\index.php

<?php
//アプリケーションルートパスの定義
define('APP_DIR','../../../../zendapps/db/10/');
//公開ディレクトリのフルパスの定義
define('BASE_DIR','/zf/db/10/');
//フロントコントローラ用のコンポーネントをロード
require_once 'Zend/Controller/Front.php';
//アクションコントローラ用のコンポーネントをロード
require_once 'Zend/Controller/Action.php';

/**フロントコントローラのインスタンスの取得
 *コントローラのフォルダ指定
 *ディスパッチ
 */
Zend_Controller_Front::run(APP_DIR . 'controllers');

 入力画面で、氏名、電話番号の双方を未記入のまま「登録」ボタンをクリックすると、次のようにエラーメッセージが表示され、再度入力画面も表示されます。

■検証クラス

 上記insert_telアプリケーションでは、入力欄が空白か否かをチェックするZend_Validate_notEmptyクラスを使用しましたが、Zend Frameworkでは、そのほかにも、入力値が、英数字のみか、指定範囲内か、妥当なメールアドレスかなどをチェックする多くの検証クラスが提供されます。他の例として、文字列が正規表現に一致するかいなかをチェックするZend_Validate_Regexクラスの例を次に示します。

 具体的には、電話番号が数字と「-」のみから構成されているかをチェックします。この場合の正規表現は「'/^[0-9-]+$/'」となります。

c:\Apache2.2\zendapps\zf\db\11\controllers\IndexController.php

<?php
// 電話帳モデルのロード
require_once APP_DIR . 'models/telModel.php';
// デフォルトアクションコントローラクラスを生成
class IndexController extends Zend_Controller_Action {
  // 電話帳モデル用オブジェクト(インスタンス)のためのメンバー変数の定義
  private $_tel;

  // 初期化メソッドの定義
  public function init() {
    // 電話帳モデル用オブジェクト(インスタンス)の生成
    $this->_tel = new telModel();
  }

  // デフォルト(index/index)アクションの定義
  public function indexAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // フォアワード後のエラーメッセージの受信
    $errorMsgAll = $request->getUserParam('errorMsgAll');
    // エラーメッセージをビュー変数に設定
    $this->view->errorMsgAll = $errorMsgAll;

    // フォワード後のPOSTデータの受信(アンエスケープ処理も行う)
    $var['name'] = stripslashes($request->getPost('name')) ;
    $var['tel_number'] = stripslashes($request->getPost('tel_number')) ;
    // 受信データをビュー変数に設定
    $this->view->var = $var;

    // 電話帳データ入力画面表示
    // (index/index.phtml)
  }

  // 登録(index/insert)アクションの定義
  public function insertAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // Zend_Validateコンポーネントのロード
    require_once 'Zend/Validate/NotEmpty.php';
    require_once 'Zend/Validate/Regex.php';
    // バリデータのインスタンスを生成する
    $notEmpty = new Zend_Validate_NotEmpty;
    $regex = new Zend_Validate_Regex('/^[0-9-]+$/');

    // POSTデータの受信(アンエスケープ処理も行う)
    $var['name'] = stripslashes($request->getPost('name')) ;
    $var['tel_number'] = stripslashes($request->getPost('tel_number')) ;

    // エラーメッセージの設定
    $varName = '氏名';
    $notEmpty->setMessage("{$varName}は必須入力です");
    // 空白チェック
    if(!$notEmpty->isValid($var['name'])) {
      foreach($notEmpty->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }

    // エラーメッセージの設定
    $varName = '電話番号';
    $notEmpty->setMessage("{$varName}は必須入力です");
    // 空白チェック
    if(!$notEmpty->isValid($var['tel_number'])) {
      foreach($notEmpty->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }

    // エラーメッセージの設定
    $varName = '電話番号';
    $regex->setMessage("{$varName}には数字と「-」のみが使えます");

    
// 文字種チェック
    if(!$regex->isValid($_var['tel_number'])) {
      foreach($regex->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }


    // 現在の年月日、時刻を取得し、更新日時に設定
    $var[':updated'] = date('Y-m-d H:i:s');

    // エラーの有無のチェック
    if(count($errorMsg)) {
      // バリデートでエラーが発生した場合
      $errorMsgAll = implode('<br/>',$errorMsg);
      $request->setParam('errorMsgAll',$errorMsgAll);
      // indexアクションにフォアワード
      $this->_forward('index');
    } else {
      // 電話帳データを登録
      $num = $this->_tel->insertTel($var);
      // 電話帳データを取得
      $result = $this->_tel->searchALLTel();
      // ビュー変数の設定
      $this->view->result = $result;
      // 検索結果の表示
      // (index/search.phtml)
    }
  }
}

 index/insertアクション内で、正規表現のチェック用コンポーネントZend/Validate/Regex.phpをロードし、バリデータのインスタンス$regexを生成します。構文例は次のとおりです。インスタンス生成時に、引数にチェック用の正規表現を設定します。

require_once 'Zend/Validate/Regex.php';
$regex = new Zend_Validate_Regex('/^[0-9-]+$/');

 一方、index/indexアクション内では、フォワード後、電話番号データの入力画面に、入力済みのデータを再表示するために、フォワードされたリクエストデータ(ここではPOSTデータ)を受信し、ビュー変数に設定します。

 ビュースクリプト(index/index.phtml)に、入力済みのデータの再表示用スクリプトを挿入します。

c:\Apache2.2\zendapps\zf\db\11\views\script\index\index.phtml

<html>
<head>
<title>insert_tel</title>
</head>
<body>
<!-- index/insertアクションにPOSTデータを送信-->
<p style="color:red"><?php print($this->errorMsgAll); ?></p>
<form method="POST" action="<?php print BASE_DIR ?>index/insert">
<table border="0">
 <tr>
  <th>氏名</th>
  <td><input type="text" name="name" size="10"
        value="<?php print $this->var['name'] ?>"></td>
 </tr>
 <tr>
  <th>電話番号</th>
  <td><input type="text" name=tel_number" size="10"
        value="<?php print $this->var['tel_number'] ?>"></td>
 </tr>
 <tr>
  <th></th>
  <td><input type="submit" value="登録"></td>
    <input type="reset" value="リセット"></td>
 </tr>
</table>
</form>
</body>
</html>

 電話番号にたとえば、英字を混入して入力した場合のアクセス画面例を以下に示します。エラーメッセージとともに、すでに入力済みのデータが再表示されます。

■データの更新

 電話帳データを更新するupdate_telアプリケーションの例を示します。更新は新たに編集用アクションコントローラEditControllerを定義し、以下の手順で処理を進めることとします。

@全電話帳データを表示し、各レコードごとに更新リンクを設ける。(edit/indexアクション)
A更新リンクがクリックされた1レコードの電話帳データみをデータ更新用に表示する。(edit/editアクション)
B「更新」ボタンがクリックされると、当該レコードのみデータベースのデータを更新する。(edit/updateアクション)

 MVCモデルにおける相互関連は以下のようにします。

アクションコントローラ/アクション(C) モデル(M) ビュー(V)
(->:フォアワード)
概要
edit/index totalTel
searchPageTel
edit/index.phtml 開始画面表示
edit/edit searchIdTel edit/index.phtml 電話帳データ更新画面表示
edit/update updateTel ・入力データOK
 ->edit/index
・入力データエラー
 ->edit/edit
・入力データOK
 電話帳データ更新

c:\Apache2.2\zendapps\zf\db\11\EditController.php

<?php
// 電話帳モデルのロード
require_once APP_DIR . 'models/telModel.php';
// デフォルトアクションコントローラクラスを生成
class IndexController extends Zend_Controller_Action {
  // 電話帳モデル用オブジェクト(インスタンス)のためのメンバー変数の定義
  private $_tel;

  // 初期化メソッドの定義
  public function init() {
    // 電話帳モデル用オブジェクト(インスタンス)の生成
    $this->_tel = new telModel();
  }

  // デフォルト(edit/index)アクションの定義
  public function indexAction() {
    // 全電話帳データを取得
    $result = $this->_tel->searchALLTel();
    // ビュー変数の設定
    $this->view->result = $result;
    // 更新レコード選択画面の表示
    // (edit/index.phtml)
  }

  // 編集用画面表示(edit/edit)アクションの定義
  public function editAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // 更新対象レコードIDのQueryデータ受信
    $var['tel_id'] = stripslashes($request->getQuery('tel_id')) ;
    if($var['tel_id']) {
      // 新規に更新対象レコード取得
      $result = $this->_tel->searchIdTel($var['tel_id']);
      // 取得データをビュー変数に設定
      $this->view->var = array('tel_id' => $result[0]['tel_id']
                      ,'name' => $result[0]['name']
                      ,'tel_number' => $result[0]['tel_number']);
    } else {
      // フォアワード後のエラーメッセージの受信
      $errorMsgAll = $request->getUserParam('errorMsgAll');
      // エラーメッセージをビュー変数に設定
      $this->view->errorMsgAll = $errorMsgAll;

      // フォアワード後のPOSTデータの受信(アンエスケープ処理も行う)
      $var['tel_id'] = stripslashes($request->getPost('tel_id')) ;
      $var['name'] = stripslashes($request->getPost('name')) ;
      $var['tel_number'] = stripslashes($request->getPost('tel_number')) ;
      // 受信データをビュー変数に設定
      $this->view->var = $var;
    }
    // 電話帳データ更新画面表示
    // (edit/edit.phtml)
  }

  // 登録(edit/update)アクションの定義
  public function updateAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // Zend_Validateコンポーネントのロード
    require_once 'Zend/Validate/NotEmpty.php';
    require_once 'Zend/Validate/Regex.php';
    // バリデータのインスタンスを生成する
    $notEmpty = new Zend_Validate_NotEmpty;
    $regex = new Zend_Validate_Regex('/^[0-9-]+$/');

    // POSTデータの受信(アンエスケープ処理も行う)
    $var['tel_id'] = stripslashes($request->getPost('tel_id')) ;
    $var['name'] = stripslashes($request->getPost('name')) ;
    $var['tel_number'] = stripslashes($request->getPost('tel_number')) ;

    // エラーメッセージの設定
    $varName = '氏名';
    $notEmpty->setMessage("{$varName}は必須入力です");
    // 空白チェック
    if(!$notEmpty->isValid($var['name'])) {
      foreach($notEmpty->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }

    // エラーメッセージの設定
    $varName = '電話番号';
    $notEmpty->setMessage("{$varName}は必須入力です");
    // 空白チェック
    if(!$notEmpty->isValid($var['tel_number'])) {
      foreach($notEmpty->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }

    // エラーメッセージの設定
    $varName = '電話番号';
    $regex->setMessage("{$varName}には数字と「-」のみが使えます");
    // 文字種チェック
    if(!$regex->isValid($var['tel_number'])) {
      foreach($regex->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }

    // 現在の年月日、時刻を取得し、更新日時に設定
    $var['updated'] = date('Y-m-d H:i:s');

    // エラーの有無のチェック
    if(count($errorMsg)) {
      // バリデートでエラーが発生した場合
      $errorMsgAll = implode('<br/>',$errorMsg);
      $request->setParam('errorMsgAll',$errorMsgAll);
      // indexアクションにフォアワード
      $this->_forward('index');
    } else {
      // 電話帳データを更新
      $num = $this->_tel->updateTel($_var);
      // 電話帳データを取得
      $result = $this->_tel->searchALLTel();
      // ビュー変数の設定
      $this->view->result = $result;
      // 検索結果の表示
      // (edit/update.phtml)
    }
  }
}

 電話帳モデル(M)クラス内に指定IDレコード検索メソッドsearchIdTelの定義と指定IDレコード更新メソッドupdateTelの定義を追加します。

c:\Apache2.2\zendapps\zf\db\11\models\telModel.php

<?php
//データベース接続クラス(ユーザ定義)のロード
require_once 'DbManager.class.php';

// 電話帳モデルをクラスとして定義
class telModel {
  // データベースアダプタ用メンバー変数を定義
  private $_db;

  // コンストラクタの定義
  public function __construct() {
    // データベースへの接続
    $this->_db = DbManager::getConnection();
  }
  // 登録メソッドの定義
  public function insertTel(&$tel) {
    // パラメータ付き登録クエリの作成
    $sql = 'insert into tbl_tel(name,tel_number,updated)'
      . 'value(:name,:tel_number,:updated);';
    // パラメータ付き登録クエリの発行
    $result = $this->_db->query($sql
           ,array(
                ':name' => $tel['name']
               ,':tel_number' => $tel['tel_number']
               ,':updated' => $tel['updated']
              )
           );

    $num = $result->rowCount();
    return $num;
  }
  // 全レコード検索メソッドの定義
  public function searchAllTel() {
    // クエリの作成(更新日の降順にソート)
    $sql = 'select * from tbl_tel order by updated desc;';
    //クエリの発行と結果取得
    $result = $this->_db->fetchAll($sql);
    return $result;
  }
  // 指定IDレコード検索メソッドの定義
  public function searchIdTel($tel_id) {
    // クエリの作成
    $sql = 'select * from tbl_tel where tel_id = :tel_id;';
    //クエリの発行と結果取得
    $result = $this->_db->fetchAll($sql,array(':tel_id'=>$tel_id));
    return $result;
  }
  // 指定IDレコード更新メソッドの定義
  public function updateTel(&$tel) {
    
// クエリの作成
    $sql = 'update tbl_tel '
       . ' set name = :name'
       . ', tel_number = :tel_number'
       . ', updated = :updated'
       . ' where tel_id = :tel_id;';
    
//クエリの発行と結果取得
    $result = $this->_db->query($sql
           ,array(
                ':name' => $tel['name']
               ,':tel_number' => $tel['tel_number']
               ,':updated' => $tel['updated']
               ,':tel_id' => $tel['tel_id']
              )
           );
    $num = $result->rowCount();
    return $num;
  }

}

 ビュースクリプトedit/index.phtmlは、次のように各レコードごとに更新リンクを記述します。

c:\Apache2.2\zendapps\zf\db\11\views\script\index\index.phtml

<html>
<head>
<title>update_tel</title>
</head>
<body>
更新レコード行の「更新」リンクをクリックしてください<br/>
<table border="1" cellspacing="0" cellpadding="0">
 <tr>
  <th>更新</th>
  <th>id</th>
  <th>氏名</th>
  <th>電話番号</th>
  <th>更新日</th>
 </tr>
 <?php foreach ($this->result as $rows) { ?>
 <tr>
  <td><a href="<?php print BASE_DIR . 'edit/edit?tel_id='
            . $rows['tel_id']; ?>">更新</td>

  <td><?php print $rows['tel_id']; ?></td>
  <!-- ユーザ入力データの表示にはサニタイジングを行う -->
  <td><?php print $this->escape($rows['name']); ?></td>
  <td><?php print $this->escape($rows['tel_number']); ?></td>
  <td><?php print $rows['updated']; ?></td>
 </tr>
 <?php } ?>
</table>
</body>
</html>

 更新リンクのURIは、アクセス先の「コントローラ名/アクション名」を「edit/edit」とし、選択された更新対象レコードID(tel_id)の値を次のように付記します。

print BASE_DIR . 'edit/edit?tel_id=' . $rows['tel_id'];

 update_telアプリケーションの実行結果例は次のようになります。

■データの削除

 電話帳データ(レコード)を削除するdelete_telアプリケーションの例を示します。上記の編集用アクションコントローラEditControllerにedit/deleteアクションを定義することとします。以下の手順で処理を進めることとします。

@全電話帳データを表示し、各レコードごとに削除リンクを設ける。(edit/indexアクション)
A削除リンクがクリックされた1レコードの電話帳データみをデータ削除確認用に表示する。(edit/confirmアクション)
B「削除」ボタンがクリックされると、当該レコードのみデータベースのデータを削除する。(edit/deleteアクション)

 MVCモデルにおける相互関連は以下のようにします。

アクションコントローラ/アクション(C) モデル(M) ビュー(V)
(->:フォアワード)
概要
edit/index totalTel
searchPageTel
edit/index.phtml 開始画面表示
edit/edit searchIdTel edit/index.phtml 電話帳データ更新画面表示
edit/update updateTel ・入力データOK
 ->edit/index
・入力データエラー
 ->edit/edit
・入力データOK
 電話帳データ更新
edit/confirm searchIdTel edit/confirm.phtml 電話帳データ削除確認画面表示
edit/delete deleteTel ->edit/index 電話帳データ削除

c:\Apache2.2\zendapps\zf\db\12\controllers\EditController.php

<?php
// 電話帳モデルのロード
require_once APP_DIR . 'models/telModel.php';
// デフォルトアクションコントローラクラスを生成
class IndexController extends Zend_Controller_Action {
  // 電話帳モデル用オブジェクト(インスタンス)のためのメンバー変数の定義
  private $_tel;
  // 初期化メソッドの定義
  public function init() {
    // 電話帳モデル用オブジェクト(インスタンス)の生成
    $this->_tel = new telModel();
  }

  // デフォルト(edit/index)アクションの定義
  public function indexAction() {
    // 全電話帳データを取得
    $result = $this->_tel->searchALLTel();
    // ビュー変数の設定
    $this->view->result = $result;
    // 更新レコード選択画面の表示
    // (edit/index.phtml)
  }

  // 編集用画面表示(edit/edit)アクションの定義
  public function editAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // 更新対象レコードIDのQueryデータ受信
    $var['tel_id'] = stripslashes($request->getQuery('tel_id')) ;
    if($var['tel_id']) {
      // 新規に更新対象レコード取得
      $result = $this->_tel->searchIdTel($var['tel_id']);
      // 取得データをビュー変数に設定
      $this->view->var = array('tel_id' => $result[0]['tel_id']
                  ,'name' => $result[0]['name']
                  ,'tel_number' => $result[0]['tel_number']);
    } else {
      // フォアワード後のエラーメッセージの受信
      $errorMsgAll = $request->getUserParam('errorMsgAll');
      // エラーメッセージをビュー変数に設定
      $this->view->errorMsgAll = $errorMsgAll;

      // フォアワード後のPOSTデータの受信(アンエスケープ処理も行う)
      $var['tel_id'] = stripslashes($request->getPost('tel_id')) ;
      $var['name'] = stripslashes($request->getPost('name')) ;
      $var['tel_number'] = stripslashes($request->getPost('tel_number')) ;
      // 受信データをビュー変数に設定
      $this->view->var = $var;
    }
    // 電話帳データ更新画面表示
    // (edit/edit.phtml)
  }

  // 登録(edit/update)アクションの定義
  public function updateAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // Zend_Validateコンポーネントのロード
    require_once 'Zend/Validate/NotEmpty.php';
    require_once 'Zend/Validate/Regex.php';
    // バリデータのインスタンスを生成する
    $notEmpty = new Zend_Validate_NotEmpty;
    $regex = new Zend_Validate_Regex('/^[0-9-]+$/');

    // POSTデータの受信(アンエスケープ処理も行う)
    $var['tel_id'] = stripslashes($request->getPost('tel_id')) ;
    $var['name'] = stripslashes($request->getPost('name')) ;
    $var['tel_number'] = stripslashes($request->getPost('tel_number')) ;

    // エラーメッセージの設定
    $varName = '氏名';
    $notEmpty->setMessage("{$varName}は必須入力です");
    // 空白チェック
    if(!$notEmpty->isValid($var['name'])) {
      foreach($notEmpty->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }

    // エラーメッセージの設定
    $varName = '電話番号';
    $notEmpty->setMessage("{$varName}は必須入力です");
    // 空白チェック
    if(!$notEmpty->isValid($_var['tel_number'])) {
      foreach($notEmpty->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }

    // エラーメッセージの設定
    $varName = '電話番号';
    $regex->setMessage("{$_varName}には数字と「-」のみが使えます");
    // 文字種チェック
    if(!$regex->isValid($var['tel_number'])) {
      foreach($regex->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }

    // 現在の年月日、時刻を取得し、更新日時に設定
    $var['updated'] = date('Y-m-d H:i:s');

    // エラーの有無のチェック
    if(count($this->_errorMsg)) {
      // バリデートでエラーが発生した場合
      $errorMsgAll = implode('<br/>',$errorMsg);
      $request->setParam('errorMsgAll',$errorMsgAll);
      // indexアクションにフォアワード
      $this->_forward('index');
    } else {
      // 電話帳データを更新
      $num = $this->_tel->updateTel($var);
      // 電話帳データを取得
      $result = $this->_tel->searchALLTel();
      // ビュー変数の設定
      $this->view->result = $result;
      // 検索結果の表示
      // (edit/update.phtml)
    }
  }

  // 削除確認(edit/confirm)アクションの定義
  public function confirmAction() {
    
// リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    
// 削除対象レコードIDのQueryデータ受信
    $var['tel_id'] = stripslashes($request->getQuery('tel_id')) ;
    
// 新規に更新対象レコード取得
    $result = $this->_tel->searchIdTel($var['tel_id']);
    
// 取得データをビュー変数に設定
    $this->view->var = array('tel_id' => $result[0]['tel_id']
                 ,'name' => $result[0]['name']
                 ,'tel_number' => $result[0]['tel_number']);
    
// 削除確認画面の表示
    // (edit/confirm.phtml)
  }


  // 削除実行(edit/delete)アクションの定義
  public function deleteAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    
// POSTデータの受信(アンエスケープ処理も行う)
    $tel_id = stripslashes($request->getPost('tel_id')) ;
    
// 電話帳データを削除
    $num = $this->_tel->deleteTel($tel_id);
    // 削除レコード数メッセージをビュー変数に設定
    $this->view->msg = $num . "件のレコードを削除しました<br />";
    
// 全電話帳データを取得
    $result = $this->_tel->searchALLTel();
    
// 全電話帳データをビュー変数に設定
    $this->view->result = $result;
    
// 削除後全画面の表示
    // (edit/delete.phtml)
  }

}

 電話帳モデル(M)クラス内に指定IDレコード削除メソッドdeleteTelの定義を追加します。

c:\Apache2.2\zendapps\zf\db\12\models\telModel.php

<?php
//データベース接続クラス(ユーザ定義)のロード
require_once 'DbManager.class.php';

// 電話帳モデルをクラスとして定義
class telModel {
  // データベースアダプタ用メンバー変数を定義
  private $_db;

  // コンストラクタの定義
  public function __construct() {
    // データベースへの接続
    $this->_db = DbManager::getConnection();
  }
  // 登録メソッドの定義
  public function insertTel(&$tel) {
    // パラメータ付き登録クエリの作成
    $sql = 'insert into tbl_tel(name,tel_number,updated)'
      . 'value(:name,:tel_number,:updated);';
    // パラメータ付き登録クエリの発行
    $result = $this->_db->query($sql
           ,array(
                ':name' => $tel['name']
               ,':tel_number' => $tel['tel_number']
               ,':updated' => $tel['updated']
              )
           );
    $num = $result->rowCount();
    return $num;
  }
  // 全レコード検索メソッドの定義
  public function searchAllTel() {
    // クエリの作成(更新日の降順にソート)
    $sql = 'select * from tbl_tel order by updated desc;';
    //クエリの発行と結果取得
    $result = $this->_db->fetchAll($sql);
    return $result;
  }
  // 指定IDレコード検索メソッドの定義
  public function searchIdTel($tel_id) {
    // クエリの作成
    $sql = 'select * from tbl_tel where tel_id = :tel_id;';
    //クエリの発行と結果取得
    $result = $this->_db->fetchAll($sql,array(':tel_id'=>$tel_id));
    return $result;
  }
  // 指定IDレコード更新メソッドの定義
  public function updateTel(&$tel) {
    // クエリの作成
    $sql = 'update tbl_tel '
       . ' set name = :name'
       . ', tel_number = :tel_number'
       . ', updated = :updated'
       . ' where tel_id = :tel_id;';
    //クエリの発行と結果取得
    $result = $this->_db->query($sql
           ,array(
                ':name' => $tel['name']
               ,':tel_number' => $tel['tel_number']
               ,':updated' => $tel['updated']
               ,':tel_id' => $tel['tel_id']
              )
           );

    $num = $result->rowCount();
    return $num;
  }
  // 指定IDレコード削除メソッドの定義
  public function deleteTel($tel_id) {
    // クエリの作成
    $sql = 'delete from tbl_tel where tel_id = :tel_id;';
    //var_dump($sql);
    //クエリの発行と結果取得
    $result = $this->_db->query($sql,array(':tel_id'=>$tel_id);
    $num = $result->rowCount();
    return $num;

  }
}

 ビュースクリプトedit/index.phtmlは、次のように各レコードごとに更新リンクを記述します。

c:\Apache2.2\zendapps\zf\db\12\views\script\index\index.phtml

<html>
<head>
<title>update_tel</title>
</head>
<body>
「更新」・「削除」リンクをクリックしてください<br/>
<table border="1" cellspacing="0" cellpadding="0">
 <tr>
  <th>更新</th>
  <th>削除</th>
  <th>id</th>
  <th>氏名</th>
  <th>電話番号</th>
  <th>更新日</th>
 </tr>
 <?php foreach ($this->result as $rows) { ?>
 <tr>
  <td><a href="<?php print BASE_DIR . 'edit/edit?tel_id='
            . $rows['tel_id']; ?>">更新</td>
  <td><a href="<?php print BASE_DIR . 'edit/confirm?tel_id='
            . $rows['tel_id']; ?>">削除</td>

  <td><?php print $rows['tel_id']; ?></td>
  <!-- ユーザ入力データの表示にはサニタイジングを行う -->
  <td><?php print $this->escape($rows['name']); ?></td>
  <td><?php print $this->escape($rows['tel_number']); ?></td>
  <td><?php print $rows['updated']; ?></td>
 </tr>
 <?php } ?>
</table>
</body>
</html>

 削除リンクのURIは、アクセス先の「コントローラ名/アクション名」を「edit/confirm」とし、選択された削除対象レコードID(tel_id)の値を次のように付記します。

print BASE_DIR . 'edit/confirm?tel_id=' . $rows['tel_id'];

 delete_telアプリケーションの実行結果例は次のようになります。

■電話帳アプリケーションの統合

 これまで、データベース利用の基本である、表示、検索、登録、更新、削除の処理を実現するMVCモデルを電話帳アプリケーションを例に、個々に記述してきましたが、ここでは、これらの処理を一つの統合した場合のMVCモデルを、admin_telアプリケーションとして記述してみます。

 

c:\Apache2.2\htdocs\zf\db\13
  .htaccess          
  index.php   
       
c:\Apache2.2\zendapps\db
  zend.ini
  \13
    \controllers        
      IndexController.php   
    \models   
      DbManager.class.php
      telModel.php     
    \views           
      \scripts        
        \index
          confirm.phtml 
          edit.phtml      
          index.phtml   
          input.phtml
          search.phtml

公開ディレクトリ
Rewriteエンジン制御ファイル
フロントコントローラファイル

アプリケーションディレクトリ
設定ファイル
アプリケーションルートディレクトリ
コントローラディレクトリ
デフォルトアクションコントローラファイル
モデル用ディレクトリ
データベース接続用クラス
電話帳モデル
ビュー関連ディレクトリ
ビュースクリプトディレクトリ
デフォルトアクションコントローラ用ディレクトリ
確認画面用ビュースクリプト
編集画面用ビュースクリプト
初期画面用ビュースクリプト
入力画面用ビュースクリプト
検索結果表示用ビュースクリプト

MVCモデルにおける相互関連は以下のようにします。

アクションコントローラ/アクション(C) モデル(M) ビュー(V)
(->:フォアワード)
概要
index/index totalTel
searchPageTel
index/index.phtml 開始画面表示
index/search searchNameTel index/search.phtml 電話帳データ検索画面表示
index/input なし index/input.phtml  
index/insert insertTel

・入力データOK
 ->index/index
・入力データエラー
 ->index/input

・入力データOK
 電話帳データ登録
index/edit searchIdTel index/index.phtml 電話帳データ更新画面表示
index/update updateTel ・入力データOK
 ->index/index
・入力データエラー
 ->index/edit
・入力データOK
 電話帳データ更新
index/confirm searchIdTel index/confirm.phtml 電話帳データ削除確認画面表示
index/delete deleteTel ->index/index 電話帳データ削除

c:\Apache2.2\zendapps\zf\db\13\controllers\IndexController.php

<?php
// 電話帳モデルのロード
require_once APP_DIR . 'models/telModel.php';
// デフォルトアクションコントローラクラスを生成
class IndexController extends Zend_Controller_Action {
  // 電話帳モデル用オブジェクト(インスタンス)のためのメンバー変数の定義
  private $_tel;

  // 初期化メソッドの定義
  public function init() {
    // 電話帳モデル用オブジェクト(インスタンス)の生成
    $this->_tel = new telModel();
  }

  // デフォルト(index/index)アクションの定義
  public function indexAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // フォアワード後のエラーメッセージの受信
    $msg = $request->getUserParam('msg');
    // エラーメッセージをビュー変数に設定
    $this->view->msg = $msg;
    // 電話帳全データを取得
    $result = $this->_tel->searchALLTel();
    // 電話帳全データをビュー変数に設定
    $this->view->result = $result;
    // 電話帳管理初期画面表示
    // (index/index.phtml)
  }

  // 検索(index/search)アクションの定義
  public function searchAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // POSTデータの受信
    $key =$request->getPost('key') ;
    // 電話帳データを取得
    $result = $this->_tel->searchNameTel($key);
    $num = count($result);
    // ビュー変数の設定
    $this->view->key = $key;
    $this->view->result = $result;
    $this->view->msg = "該当レコードは " . $num . "件です。<br />";
    // 検索結果の表示
    // index/search.phtml
  }

  // 入力(index/input)アクションの定義
  public function inputAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // フォアワード後のエラーメッセージの受信
    $errorMsgAll = $request->getUserParam('errorMsgAll');
    //var_dump($_errorMsgAll);
    // エラーメッセージをビュー変数に設定
    $this->view->errorMsgAll = $errorMsgAll;

    // フォアワード後のPOSTデータの受信(アンエスケープ処理も行う)
    $var['name'] = stripslashes($request->getPost('name')) ;
    $var['tel_number'] = stripslashes($request->getPost('tel_number')) ;
    // 受信データをビュー変数に設定
    $this->view->var = $var;
    // 電話帳データ入力画面表示
    // (index/input.phtml)
  }

  // 登録(index/insert)アクションの定義
  public function insertAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    $command = stripslashes($request->getPost('command')) ;
    if($command == "登録") {
      // Zend_Validateコンポーネントのロード
      require_once 'Zend/Validate/NotEmpty.php';
      require_once 'Zend/Validate/Regex.php';
      // バリデータのインスタンスを生成する
      $notEmpty = new Zend_Validate_NotEmpty;
      $regex = new Zend_Validate_Regex('/^[0-9-]+$/');

      // POSTデータの受信(アンエスケープ処理も行う)
      $var['name'] = stripslashes($request->getPost('name')) ;
      $var['tel_number'] = stripslashes($request->getPost('tel_number')) ;

      // エラーメッセージの設定
      $varName = '氏名';
      $notEmpty->setMessage("{$_varName}は必須入力です");
      // 空白チェック
      if(!$notEmpty->isValid($var['name'])) {
        foreach($notEmpty->getMessages() as $msg) {
          $errorMsg[] = $msg;
        }
      }

      // エラーメッセージの設定
      $varName = '電話番号';
      $notEmpty->setMessage("{$varName}は必須入力です");
      // 空白チェック
      if(!$notEmpty->isValid($_var['tel_number'])) {
        foreach($notEmpty->getMessages() as $msg) {
          $errorMsg[] = $msg;
        }
      }

      // エラーメッセージの設定
      $varName = '電話番号';
      $regex->setMessage("{$varName}には数字と「-」のみが使えます");
      // 文字種チェック
      if(!$regex->isValid($_var['tel_number'])) {
        foreach($regex->getMessages() as $msg) {
          $errorMsg[] = $msg;
        }
      }

      // 現在の年月日、時刻を取得
      $var['updated'] = date('Y-m-d H:i:s');

      if(count($errorMsg)) {
        // バリデートでエラーが発生した場合
        $errorMsgAll = implode('<br/>',$errorMsg);
        $request->setParam('errorMsgAll',$errorMsgAll);
        // inputアクションにフォアワード
        $this->_forward('input');
      } else {
        // 電話帳データを登録
        $num = $this->_tel->insertTel($_var);
        $request->setParam('msg',"{$num}レコードを登録しました。");
        // 初期画面にフォアワード
        $this->_forward('index');
      }
    } else {
      // 初期画面にフォアワード
      $this->_forward('index');
    }
  }

  // 編集用画面表示(index/edit)アクションの定義
  public function editAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // 更新対象レコードIDのQueryデータ受信
    $var['tel_id'] = stripslashes($request->getQuery('tel_id')) ;
    if($var['tel_id']) {
      // 新規に更新対象レコード取得
      $result = $this->_tel->searchIdTel($var['tel_id']);
      // 取得データをビュー変数に設定
      $this->view->var = array('tel_id' => $result[0]['tel_id']
                  ,'name' => $result[0]['name']
                  ,'tel_number' => $result[0]['tel_number']);
    } else {
      // フォアワード後のエラーメッセージの受信
      $errorMsgAll = $request->getUserParam('errorMsgAll');
      // エラーメッセージをビュー変数に設定
      $this->view->errorMsgAll = $errorMsgAll;
      // フォアワード後のPOSTデータの受信(アンエスケープ処理も行う)
      $var['tel_id'] = stripslashes($request->getPost('tel_id')) ;
      $var['name'] = stripslashes($request->getPost('name')) ;
      $var['tel_number']
               = stripslashes($request->getPost('tel_number')) ;
      // 受信データをビュー変数に設定
      $this->view->var = $var;
    }
    // 電話帳データ更新画面表示
    // (index/edit.phtml)
  }

  // 更新(index/update)アクションの定義
  public function updateAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    $command = stripslashes($request->getPost('command')) ;
    if($command == "更新") {
      // Zend_Validateコンポーネントのロード
      require_once 'Zend/Validate/NotEmpty.php';
      require_once 'Zend/Validate/Regex.php';
      // バリデータのインスタンスを生成する
      $notEmpty = new Zend_Validate_NotEmpty;
      $regex = new Zend_Validate_Regex('/^[0-9-]+$/');

      // POSTデータの受信(アンエスケープ処理も行う)
      $var['tel_id'] = stripslashes($request->getPost('tel_id')) ;
      $var['name'] = stripslashes($request->getPost('name')) ;
      $var['tel_number'] = stripslashes($request->getPost('tel_number')) ;

      // エラーメッセージの設定
      $varName = '氏名';
      $notEmpty->setMessage("{$varName}は必須入力です");
      // 空白チェック
      if(!$notEmpty->isValid($var['name'])) {
        foreach($notEmpty->getMessages() as $msg) {
          $errorMsg[] = $msg;
        }
      }

      // エラーメッセージの設定
      $varName = '電話番号';
      $notEmpty->setMessage("{$varName}は必須入力です");
      // 空白チェック
      if(!$notEmpty->isValid($var['tel_number'])) {
        foreach($notEmpty->getMessages() as $msg) {
          $errorMsg[] = $msg;
        }
      }

      // エラーメッセージの設定
      $varName = '電話番号';
      $regex->setMessage("{$varName}には数字と「-」のみが使えます");
      // 文字種チェック
      if(!$regex->isValid($var['tel_number'])) {
        foreach($regex->getMessages() as $msg) {
          $errorMsg[] = $msg;
        }
      }

      // 現在の年月日、時刻を取得
      $var['updated'] = date('Y-m-d H:i:s');

      if(count($errorMsg)) {
        // バリデートでエラーが発生した場合
        $errorMsgAll = implode('<br/>',$errorMsg);
        $request->setParam('errorMsgAll',$errorMsgAll);
        // editアクションにフォアワード
        $this->_forward('edit');
      } else {
        // 電話帳データを更新
        $num = $this->_tel->updateTel($var);
        $request->setParam('msg',"{$num}レコードを更新しました。");
        // 初期画面にフォアワード
        $this->_forward('index');
      }
    } else {
      // 初期画面にフォアワード
      $this->_forward('index');
    }
  }

  // 削除確認(index/confirm)アクションの定義
  public function confirmAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // 削除対象レコードIDのQueryデータ受信
    $var['tel_id'] = stripslashes($request->getQuery('tel_id')) ;
    // 新規に更新対象レコード取得
    $result = $this->_tel->searchIdTel($var[':tel_id']);
    // 取得データをビュー変数に設定
    $this->view->var = array('tel_id' => $result[0]['tel_id']
                ,'name' => $result[0]['name']
                ,'tel_number' => $result[0]['tel_number']);
    // 削除確認画面の表示
    // (index/confirm.phtml)
  }

  // 削除実行(index/delete)アクションの定義
  public function deleteAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // POSTデータの受信(アンエスケープ処理も行う)
    $command = stripslashes($request->getPost('command')) ;
    $tel_id = stripslashes($request->getPost('tel_id')) ;
    if($command == "削除") {
      // 電話帳データを削除
      $num = $this->_tel->deleteTel($tel_id);
      // 削除レコード数メッセージをビュー変数に設定
      $msg = $num . "件のレコードを削除しました<br />";
      $request->setParam('msg',$msg);
    }
    // 初期画面にフォアワード
    $this->_forward('index');
  }
}

c:\Apache2.2\zendapps\zf\db\13\views\script\index\index.phtml

<html>
<head>
<title>admin_tel</title>
<style type="text/css">
<!--
.ac {text-align:center
}
.ar {text-align:right
}
-->
</style>
</head>
<body>
<p class="ac">電話帳管理</p>
<!-- 処理終了後のメッセージ表示 -->
<?php if($this->msg != "") {
     print $this->msg;
   }
?>
<table border="0" width="100%">
 <tr>
  <form method="POST" action="<?php print BASE_DIR ?>index/search">
  <td class="ar">
   <input type="text" name="key" value="<?php print $this->name; ?>">
   <input type="submit" name="command" value="検索">
  </td>
  </form>
 </tr>
 <tr>
  <form method="POST" action="<?php print BASE_DIR ?>index/input">
  <td>
   <input type="submit" name="command" value="新規">
  </td>
  </form>
 </tr>
</table>
<table border="1" cellspacing="0" cellpadding="0">
 <tr>
  <th>更新</th>
  <th>削除</th>
  <th>id</th>
  <th>氏名</th>
  <th>電話番号</th>
  <th>更新日</th>
 </tr>
 <?php foreach ($this->result as $rows) { ?>
 <tr>
  <td><a href="<?php print BASE_DIR . 'index/edit?tel_id='
       . $rows['tel_id']; ?>">更新</td>
  <td><a href="<?php print BASE_DIR . 'index/confirm?tel_id='
       . $rows['tel_id']; ?>">削除</td>
  <td><?php print $rows['tel_id']; ?></td>
  <!-- ユーザ入力データの表示にはサニタイジングを行う -->
  <td><?php print $this->escape($rows['name']); ?></td>
  <td><?php print $this->escape($rows['tel_number']); ?></td>
  <td><?php print $rows['updated']; ?></td>
 </tr>
 <?php } ?>
</table>
</body>
</html>

 

■データ受信とデータチェック用メンバー関数

 クエリ情報をPOSTメソッドで受信するスクリプトと、受信データのバリデータでチェックするスクリプトは何ケ所かで出てくるので、おのおのメンバー関数getData()関数とcheckData()関数として定義し利用することとします。

 また、POSTデータを受信するgetData()関数を定義するに当たり、受信データを格納するメンバー変数$_varを定義し、利用することとします。

c:\Apache2.2\zendapps\zf\db\14\controllers\IndexController.php

<?php
// 電話帳モデルのロード
require_once APP_DIR . 'models/telModel.php';
// デフォルトアクションコントローラクラスを生成
class IndexController extends Zend_Controller_Action {
  // 電話帳モデル用オブジェクト(インスタンス)のためのメンバー変数の定義
  private $_tel;
  // 受信データ格納用メンバー変数の定義
  private $_var;

  // 初期化メソッドの定義
  public function init() {
    // 電話帳モデル用オブジェクト(インスタンス)の生成
    $this->_tel = new telModel();
    // 配列の初期化
    $this->_var = array();
  }

  // デフォルト(index/index)アクションの定義
  public function indexAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // フォアワード後のエラーメッセージの受信
    $msg = $request->getUserParam('msg');
    // エラーメッセージをビュー変数に設定
    $this->view->msg = $msg;
    // 電話帳全データを取得
    $result = $this->_tel->searchALLTel();
    // 電話帳全データをビュー変数に設定
    $this->view->result = $result;
    // 電話帳管理初期画面表示
    // (index/index.phtml)
  }

  // 検索(index/search)アクションの定義
  public function searchAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // POSTデータの受信
    $key =$request->getPost('key') ;
    // 電話帳データを取得
    $result = $this->_tel->searchNameTel($key);
    $num = count($result);
    // ビュー変数の設定
    $this->view->key = $key;
    $this->view->result = $result;
    $this->view->msg = "該当レコードは " . $num . "件です。<br />";
    // 検索結果の表示
    // index/search.phtml
  }

  // 入力(index/input)アクションの定義
  public function inputAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // フォアワード後のエラーメッセージの受信
    $errorMsgAll = $request->getUserParam('errorMsgAll');
    // エラーメッセージをビュー変数に設定
    $this->view->errorMsgAll = $errorMsgAll;
    // フォアワード後のPOSTデータの受信
    $this->getData();
    // 受信データをビュー変数に設定
    $this->view->var = $this->_var;
    // 電話帳データ入力画面表示
    // (index/input.phtml)
  }

  // 登録(index/insert)アクションの定義
  public function insertAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    $command = stripslashes($request->getPost('command')) ;
    if($command == "登録") {
      // 受信データチェック
      $errorMsg = $this->checkData();
      if(count($errorMsg)) {
        // バリデートでエラーが発生した場合
        $errorMsgAll = implode('<br/>',$errorMsg);
        $request->setParam('errorMsgAll',$errorMsgAll);
        // inputアクションにフォアワード
        $this->_forward('input');
      } else {
        // 現在の年月日、時刻を取得
        $this->_var['updated'] = date('Y-m-d H:i:s');
        // 電話帳データを登録
        $num = $this->_tel->insertTel($this->_var);
        $request->setParam('msg',"{$num}レコードを登録しました。");
        // 初期画面にフォアワード
        $this->_forward('index');
      }
    } else {
      // 初期画面にフォアワード
      $this->_forward('index');
    }
  }

  // 編集用画面表示(index/edit)アクションの定義
  public function editAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // 更新対象レコードIDのQueryデータ受信
    $this->_var['tel_id'] = stripslashes($request->getQuery('tel_id')) ;
    if($this->_var['tel_id']) {
      // 新規に更新対象レコード取得
      $result = $this->_tel->searchIdTel($this->_var['tel_id']);
      // 取得データをビュー変数に設定
      $this->view->var = array('tel_id' => $result[0]['tel_id']
                   ,'name' => $result[0]['name']
                   ,'tel_number' => $result[0]['tel_number']);
    } else {
      // フォアワード後のエラーメッセージの受信
      $errorMsgAll = $request->getUserParam('errorMsgAll');
      // エラーメッセージをビュー変数に設定
      $this->view->errorMsgAll = $errorMsgAll;
      // フォアワード後のPOSTデータの受信
      $this->getData();
      // 受信データをビュー変数に設定
      $this->view->var = $this->_var;
    }
    // 電話帳データ更新画面表示
    // (index/edit.phtml)
  }

  // 更新(index/update)アクションの定義
  public function updateAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    $command = stripslashes($request->getPost('command')) ;
    if($command == "更新") {
      // 受信データチェック
      $errorMsg = $this->checkData();
      if(count($errorMsg)) {
        // バリデートでエラーが発生した場合
        $errorMsgAll = implode('<br/>',$errorMsg);
        $request->setParam('errorMsgAll',$errorMsgAll);
        // editアクションにフォアワード
        $this->_forward('edit');
      } else {
        // 現在の年月日、時刻を取得
        $this->_var['updated'] = date('Y-m-d H:i:s');
        // 電話帳データを更新
        $num = $this->_tel->updateTel($this->_var);
        $request->setParam('msg',"{$num}レコードを更新しました。");
        // 初期画面にフォアワード
        $this->_forward('index');
      }
    } else {
      // 初期画面にフォアワード
      $this->_forward('index');
    }
  }

  // 削除確認(index/confirm)アクションの定義
  public function confirmAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // 削除対象レコードIDのQueryデータ受信
    $this->_var['tel_id'] = stripslashes($request->getQuery('tel_id')) ;
    // 新規に更新対象レコード取得
    $result = $this->_tel->searchIdTel($this->_var['tel_id']);
    // 取得データをビュー変数に設定
    $this->view->var = array('tel_id' => $result[0]['tel_id']
                ,'name' => $result[0]['name']
                ,'tel_number' => $result[0]['tel_number']);
    // 削除確認画面の表示
    // (index/confirm.phtml)
  }

  // 削除実行(index/delete)アクションの定義
  public function deleteAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // POSTデータの受信(アンエスケープ処理も行う)
    $command = stripslashes($request->getPost('command')) ;
    $tel_id = stripslashes($request->getPost('tel_id')) ;
    if($command == "削除") {
      // 電話帳データを削除
      $num = $this->_tel->deleteTel($tel_id);
      // 削除レコード数メッセージをビュー変数に設定
      $msg = $num . "件のレコードを削除しました<br />";
      $request->setParam('msg',$msg);
    }
    // 初期画面にフォアワード
    $this->_forward('index');
  }
  /**
   * メンバー関数の定義
   */
  // データ受信
  public function getData() {
    
// リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    
// POSTデータの受信(アンエスケープ処理も行う)
    $var = $request->getPost('tel_id');
    if($var) {
      $this->_var[':tel_id'] = stripslashes($var) ;
    }
    $this->_var[':name']
       = stripslashes($request->getPost('name')) ;
    $this->_var[':tel_number']
       = stripslashes($request->getPost('tel_number')) ;
    return;
  }

  // 受信データのチェック
  public function checkData() {
    
// リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    
// Zend_Validateコンポーネントのロード
    require_once 'Zend/Validate/NotEmpty.php';
    require_once 'Zend/Validate/Regex.php';
    
// バリデータのインスタンスを生成する
    $notEmpty = new Zend_Validate_NotEmpty;
    $regex = new Zend_Validate_Regex('/^[0-9-]+$/');
    
// POSTデータの受信(アンエスケープ処理も行う)
    $this->getData();

    
// エラーメッセージの設定
    $varName = '氏名';
    $notEmpty->setMessage("{$varName}は必須入力です");
    
// 空白チェック
    if(!$notEmpty->isValid($this->_var['name'])) {
      foreach($notEmpty->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }

    
// エラーメッセージの設定
    $varName = '電話番号';
    $notEmpty->setMessage("{$varName}は必須入力です");
    
// 空白チェック
    if(!$notEmpty->isValid($this->_var['tel_number'])) {
      foreach($notEmpty->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }

    
// エラーメッセージの設定
    $varName = '電話番号';
    $regex->setMessage("{$varName}には数字と「-」のみが使えます");
    
// 文字種チェック
    if(!$regex->isValid($this->_var['tel_number'])) {
      foreach($regex->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }
    return $errorMsg;
  }

}

■Fetchメソッド

 データベースから得られた結果セットの内容を取得するために、Zend_Dbは種々のFetchメソッドを提供しています。上記のアプリケーションでは、結果セット内のすべてのレコードを連想配列で取得するFetchAllメソッドを使いました。

 ただ、結果セットに1行しかない場合、あるいは1番目のレコードしか取得しない場合は、FetchAllメソッドでなくFetchRowメソッドが効率的です。電話帳モデルtelModelクラス内のsearchIdTelメソッド内では、主キーのtel_idが一致するレコードのみ抽出するので、FetchAllメソッドを使うと、アクションコントローラ内のindex/editアクション内では、連想配列の最初のレコードを$result[0]['tel_id']のように2次元配列として記述する必要があります。

c:\Apache2.2\zendapps\zf\db\15\models\telModel.php

<?php
//データベース接続クラス(ユーザ定義)のロード
require_once 'DbManager.class.php';

// 電話帳モデルをクラスとして定義
class telModel {
  // データベースアダプタ用メンバー変数を定義
  private $_db;

(中略)

  // 指定IDレコード検索メソッドの定義
  public function searchIdTel($tel_id) {
    // クエリの作成
    $sql = 'select * from tbl_tel where tel_id = :tel_id;';
    //クエリの発行と結果取得
    $result = $this->_db->fetchAll($sql,array(':tel_id'=>$tel_id));
    return $result;
  }
(後略)

c:\Apache2.2\zendapps\zf\db\15\controllers\IndexController.php

<?php
// 電話帳モデルのロード
require_once APP_DIR . 'models/telModel.php';
// デフォルトアクションコントローラクラスを生成

(中略)

  // 編集用画面表示(index/edit)アクションの定義
  public function editAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // 更新対象レコードIDのQueryデータ受信
    $this->_var[':tel_id'] = stripslashes($request->getQuery('tel_id')) ;
    if($this->_var[':tel_id']) {
      // 新規に更新対象レコード取得
      $result = $this->_tel->searchIdTel($this->_var[':tel_id']);
      // 取得データをビュー変数に設定
      $this->view->var = array(':tel_id' => $result[0]['tel_id']
                   ,':name' => $result[0]['name']
                   ,':tel_number' => $result[0]['tel_number']);
    } else {
(後略)

 この例で、FetchAllメソッドをFetchRowメソッドに書き換えると、以下のようになります。

c:\Apache2.2\zendapps\zf\db\15\models\telModel.php

<?php
//データベース接続クラス(ユーザ定義)のロード
require_once 'DbManager.class.php';

// 電話帳モデルをクラスとして定義
class telModel {
  // データベースアダプタ用メンバー変数を定義
  private $_db;

(中略)

  // 指定IDレコード検索メソッドの定義
  public function searchIdTel($tel_id) {
    // クエリの作成
    $sql = 'select * from tbl_tel where tel_id = :tel_id;';
    //クエリの発行と結果取得
    $result = $this->_db->fetchRow($sql,array(':tel_id'=>$tel_id));
    return $result;
  }
(後略)

c:\Apache2.2\zendapps\zf\db\15\controllers\IndexController.php

<?php
// 電話帳モデルのロード
require_once APP_DIR . 'models/telModel.php';
// デフォルトアクションコントローラクラスを生成

(中略)

  // 編集用画面表示(index/edit)アクションの定義
  public function editAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // 更新対象レコードIDのQueryデータ受信
    $this->_var['tel_id'] = stripslashes($request->getQuery('tel_id')) ;
    if($this->_var['tel_id']) {
      // 新規に更新対象レコード取得
      $result = $this->_tel->searchIdTel($this->_var['tel_id']);
      // 取得データをビュー変数に設定
      $this->view->var = array('tel_id' => $result['tel_id']
                   ,'name' => $result['name']
                   ,'tel_number' => $result['tel_number']);
    } else {
(後略)

index/editアクションへのアクセス例を以下に示します。

■プリペアド・ステートメント

 同一のSQL文をパラメータを変えて繰り返し実行する場合には、プリペアド・ステートメントが有効です。プリペアド・ステートメントでは、SQL文を解析しコンパイル状態で準備しておき、2回目以降はコンパイル済みのSQL文を即実行するので、処理速度が向上します。

プリペアド・ステートメントは以下の構文で使用します。

$stt = $db->prrepare(SQL文);
$stt->execute([パラメータの配列];

SQL文にプレイスホルダがある場合、executeメソッドの引数にはパラメータの配列を設定します。

c:\Apache2.2\zendapps\zf\db\16\models\telModel.php

<?php
//データベース接続クラス(ユーザ定義)のロード
require_once 'DbManager.class.php';

// 電話帳モデルをクラスとして定義
class telModel {
  // データベースアダプタ用メンバー変数を定義
  private $_db;

  // コンストラクタの定義
  public function __construct() {
    // データベースへの接続
    $this->_db = DbManager::getConnection();
  }
  // 登録メソッドの定義
  public function insertTel(&$tel) {
    // パラメータ付き登録クエリの作成
    $sql = 'insert into tbl_tel(name,tel_number,updated)'
      . 'value(:name,:tel_number,:updated);';
    // パラメータ付き登録クエリの準備
    $stt = $this->_db->prepare($sql);
    // クエリを発行
    $stt->execute(array(':name' => $tel['name']
              , ':tel_number' => $tel['tel_number']
              , ':updated' => $tel['updated']
              )
           );

    $num = $stt->rowCount();
    return $num;
  }
  // 全レコード検索メソッドの定義
  public function searchAllTel() {
    // クエリの作成(更新日の降順にソート)
    $sql = 'select * from tbl_tel order by updated desc;';
    // クエリの準備
    $stt = $this->_db->prepare($sql);
    // クエリの発行
    $stt->execute();
    // 結果取得
    $result = array();
    while($row = $stt->fetch()) {
      array_push($result,$row);
    }

    return $result;
  }

  // キー検索メソッドの定義
  public function searchNameTel($key) {
    $key = "%{$key}%";
    // 名前つきパラメータを使ったクエリの作成
    $sql = 'select * from tbl_tel where name like :key ;';
    // パラメータ付き登録クエリの準備
    $stt = $this->_db->prepare($sql);
    // クエリを発行
    $stt->execute(array(':key'=>$key));
    // 結果取得
    $result = array();
    while($row = $stt->fetch()) {
      array_push($result,$row);
    }

    return $result;
  }

  // 指定IDレコード検索メソッドの定義
  public function searchIdTel($tel_id) {
    // クエリの作成
    $sql = 'select * from tbl_tel where tel_id = :tel_id;';
    $stt = $this->_db->prepare($sql);
    // クエリを発行
    $stt->execute(array(':tel_id'=>$tel_id));
    // 結果取得
    $row = array();
    $row = $stt->fetch();

    return $row;
  }
  // 指定IDレコード更新メソッドの定義
  public function updateTel(&$tel) {
    // クエリの作成
    $sql = 'update tbl_tel '
       . ' set name = :name'
       . ', tel_number = :tel_number'
       . ', updated = :updated'
       . ' where tel_id = :tel_id;';
    // クエリの準備
    $stt = $this->_db->prepare($sql);
    // クエリの発行
    $stt->execute(array(':name' => $tel['name']
              , ':tel_number' => $tel['tel_number']
              , ':updated' => $tel['updated']
              , ':tel_id' => $tel['tel_id']
              )
           );

    $num = $stt->rowCount();
    return $num;
  }
  // 指定IDレコード削除メソッドの定義
  public function deleteTel($tel_id) {
    // クエリの作成
    $sql = 'delete from tbl_tel where tel_id = :tel_id;';
    // クエリの準備
    $stt = $this->_db->prepare($sql);
    // クエリの発行
    $stt->execute(array(':tel_id'=>$tel_id));
    $num = $stt->rowCount();
    return $num;
  }
}

■bindValueメソッド

SQL文にプレイスホルダがある場合、executeメソッドの引数にはパラメータの配列を設定するかわりに、bindValueメソッドによってパラメータに値を引き渡す方法があります。bindValueメソッドの構文は次のとおりです。bindValueメソッドでは、データ型を明示的に指定できます。

$stt->bindValue(n,値,[データ型]);             // 「?」パラメータの場合(n=0,1,2・・・)

$stt->bindValue(’:パラメータ名’,値,[データ型]);     // 名前つきパラメータの場合

c:\Apache2.2\zendapps\zf\db\17\models\telModel.php

<?php
//データベース接続クラス(ユーザ定義)のロード
require_once 'DbManager.class.php';

// 電話帳モデルをクラスとして定義
class telModel {
  // データベースアダプタ用メンバー変数を定義
  private $_db;

  // コンストラクタの定義
  public function __construct() {
    // データベースへの接続
    $this->_db = DbManager::getConnection();
  }
  // 登録メソッドの定義
  public function insertTel(&$tel) {
    // パラメータ付き登録クエリの作成
    $sql = 'insert into tbl_tel(name,tel_number,updated)'
      . 'value(:name,:tel_number,:updated);';
    // パラメータ付き登録クエリの準備
    $stt = $this->_db->prepare($sql);
    // 名前つきパラメータにデータをバインド
    $stt->bindValue(':name',$tel['name']);
    $stt->bindValue(':tel_number',$tel['tel_number']);
    $stt->bindValue(':updated',$tel['updated']);

    // クエリを発行
    $stt->execute();
    $num = $stt->rowCount();
    return $num;
  }
  // 全レコード検索メソッドの定義
  public function searchAllTel() {
    // クエリの作成(更新日の降順にソート)
    $sql = 'select * from tbl_tel order by updated desc;';
    // クエリの準備
    $stt = $this->_db->prepare($sql);
    // クエリの発行
    $stt->execute();
    // 結果取得
    $result = array();
    while($row = $stt->fetch()) {
      array_push($result,$row);
    }
    return $result;
  }

  // キー検索メソッドの定義
  public function searchNameTel($key) {
    $key = "%{$key}%";
    // 名前つきパラメータを使ったクエリの作成
    $sql = 'select * from tbl_tel where name like :key ;';
    // パラメータ付き登録クエリの準備
    $stt = $this->_db->prepare($sql);
    // 名前つきパラメータにデータをバインド
    $stt->bindValue(':key',$key);
    // クエリを発行
    $stt->execute();
    // 結果取得
    $result = array();
    while($row = $stt->fetch()) {
      array_push($result,$row);
    }
    return $result;
  }

  // 指定IDレコード検索メソッドの定義
  public function searchIdTel($tel_id) {
    // クエリの作成
    $sql = 'select * from tbl_tel where tel_id = :tel_id;';
    $stt = $this->_db->prepare($sql);
    // 名前つきパラメータにデータをバインド
    $stt->bindValue(':tel_id',$tel_id);
    // クエリを発行
    $stt->execute();
    // 結果取得
    $row = array();
    $row = $stt->fetch();
    return $row;
  }
  // 指定IDレコード更新メソッドの定義
  public function updateTel(&$tel) {
    // クエリの作成
    $sql = 'update tbl_tel '
       . ' set name = :name'
       . ', tel_number = :tel_number'
       . ', updated = :updated'
       . ' where tel_id = :tel_id;';
    // クエリの準備
    $stt = $this->_db->prepare($sql);
    // 名前つきパラメータにデータをバインド
    $stt->bindValue(':name',$tel['name']);
    $stt->bindValue(':tel_number',$tel['tel_number']);
    $stt->bindValue(':updated',$tel['updated']);
    $stt->bindValue(':tel_id',$tel['tel_id']);

    // クエリの発行
    $stt->execute();
    $num = $stt->rowCount();
    return $num;
  }
  // 指定IDレコード削除メソッドの定義
  public function deleteTel($tel_id) {
    // クエリの作成
    $sql = 'delete from tbl_tel where tel_id = :tel_id;';
    // クエリの準備
    $stt = $this->_db->prepare($sql);
    // 名前つきパラメータにデータをバインド
    $stt->bindValue(':tel_id',$tel_id);
    // クエリの発行
    $stt->execute();
    $num = $stt->rowCount();
    return $num;
  }
}

■SQLレスでデータベース操作(insert/update/deleteメソッド)

 Zend_Dbには、SQL文を記述しなくてもデータベースを操作できるinsert/update/deleteメソッドがあります。構文は次のとおりです。戻り値は処理レコード数になります。したがって、クエリ発行後あらためてrowCountメソッドを呼び出す必要はありません。

$num = $this->_db->insert('テーブル名',入力データ配列);
$num = $this->_db->update('テーブル名',更新データ配列,'条件式');
$num = $this->_db->delete('テーブル名','条件式',);

入力/更新データ配列は、「フィールド名=>値」の連想配列とします。連想配列で与えられた値(文字列)は自動的にクオート処理されます。ただ、条件式については、明示的にクオート処理する必要があります。その場合、以下のquoteIntoメソッドが利用できます。

$this->_db->quoteInto('プレイスホルダ付文字列',プレイスホルダに割り当てる値)

ただ、quoteIntoメソッドでは、プレイスホルダに名前付パラメータは利用できません。

insert/update/deleteメソッドを使った場合、電話帳モデルtelModelクラスは次のようになります。

c:\Apache2.2\zendapps\zf\db\18\models\telModel.php

<?php
//データベース接続クラス(ユーザ定義)のロード
require_once 'DbManager.class.php';

// 電話帳モデルをクラスとして定義
class telModel {
  // データベースアダプタ用メンバー変数を定義
  private $_db;

  // コンストラクタの定義
  public function __construct() {
    // データベースへの接続
    $this->_db = DbManager::getConnection();
  }
  // 登録メソッドの定義
  public function insertTel(&$tel) {
    // insertメソッド(戻り値は入力レコード数)
    $num = $this->_db->insert(
        'tbl_tel'
              // テーブル名
        ,array('name'=>$tel['name']
   // 入力データ
           ,'tel_number'=>$tel['tel_number']
           ,'updated'=>$tel['updated'])
           );
    return $num;

  }
  // 全レコード検索メソッドの定義
  public function searchAllTel() {
    // クエリの作成(更新日の降順にソート)
    $sql = 'select * from tbl_tel order by updated desc;';
    // クエリの準備
    $stt = $this->_db->prepare($sql);
    // クエリの発行
    $stt->execute();
    // 結果取得
    $result = array();
    while($row = $stt->fetch()) {
      array_push($result,$row);
    }
    return $result;
  }

  // キー検索メソッドの定義
  public function searchNameTel($key) {
    $key = "%{$key}%";
    // 名前つきパラメータを使ったクエリの作成
    $sql = 'select * from tbl_tel where name like :key ;';
    // パラメータ付き登録クエリの準備
    $stt = $this->_db->prepare($sql);
    // 名前つきパラメータにデータをバインド
    $stt->bindValue(':key',$key);
    // クエリを発行
    $stt->execute();
    // 結果取得
    $result = array();
    while($row = $stt->fetch()) {
      array_push($result,$row);
    }
    return $result;
  }

  // 指定IDレコード検索メソッドの定義
  public function searchIdTel($tel_id) {
    // クエリの作成
    $sql = 'select * from tbl_tel where tel_id = :tel_id;';
    $stt = $this->_db->prepare($sql);
    // 名前つきパラメータにデータをバインド
    $stt->bindValue(':tel_id',$tel_id);
    // クエリを発行
    $stt->execute();
    // 結果取得
    $row = array();
    $row = $stt->fetch();
    return $row;
  }
  // 指定IDレコード更新メソッドの定義
  public function updateTel(&$tel) {
    // updateメソッド(戻り値は更新レコード数)
    $num = $this->_db->update(
         'tbl_tel'
             // テーブル名
         ,array('name'=>$tel['name']   
// 入力データ
            ,'tel_number'=>$tel['tel_number']
            ,'updated'=>$tel['updated'])
            ,$this->_db->quoteInto('tel_id = ?',$tel['tel_id'])
                          
// 条件式
         );
    return $num;

  }
  // 指定IDレコード削除メソッドの定義
  public function deleteTel($tel_id) {
    // deleteメソッド(戻り値は削除レコード数)
    $num = $this->_db->delete(
         'tbl_tel'
            // テーブル名
         ,$this->_db->quoteInto('tel_id = ?',$tel_id)
                         
// 条件式
         );
    return $num;

  }
}

 

■selectメソッド

Zend_DbではSQLレスでSELECT文のコードを記述できるselectメソッドも準備されています。主な構文は次のとおりです。

// Zend_Db_Selectオブジェクトを生成
$sel = $this->_db->select();
// FROM句の追加
$sel->from('テーブル名',取得フィールド名の配列);
// WHERE句の追加
$sel->where('条件式',プレイスホルダに割り当てる値);
// ORDER句の追加
$sel->order('ORDER BY句' );
// LIMIT...OFFSET句の追加
$sel->limit(取得レコード数,[オフセット値]);
// クエリを実行
$stt = $this->_db->query($sel);

 selectメソッドはSQL文を直接コード化しないので、データベースに依存しないコードを記述できる利点があります。たとえば、LIMIT句はMySQLとPostgreSQLでは、次のように異なりますが、selectメソッドではlimitメソッドで両方の違いを気にせず同じように記述できます。

SELECT * FROM tbl_tel LIMIT オフセット,取得レコード数           // MySQL
SELECT * FROM tbl_tel LIMIT 取得レコード数 OFFSET オフセット    // PostgreSQL

MySQLでは、PostgreSQLと同じLIMIT...OFFSETの構文も互換維持のためサポートしています。

 selectメソッドを使った場合、電話帳モデルtelModelクラスは次のようになります。

c:\Apache2.2\zendapps\zf\db\19\models\telModel.php

<?php
//データベース接続クラス(ユーザ定義)のロード
require_once 'DbManager.class.php';

// 電話帳モデルをクラスとして定義
class telModel {
  // データベースアダプタ用メンバー変数を定義
  private $_db;

  // コンストラクタの定義
  public function __construct() {
    // データベースへの接続
    $this->_db = DbManager::getConnection();
  }
  // 登録メソッドの定義
  public function insertTel(&$tel) {
    // insertメソッド(戻り値は入力レコード数)
    $num = $this->_db->insert(
             'tbl_tel'              // テーブル名
             ,array('name'=>$tel['name']     // 入力データ
             ,'tel_number'=>$tel['tel_number']
             ,'updated'=>$tel['updated'])
             );
    return $num;
  }
  // 全レコード検索メソッドの定義
  public function searchAllTel() {
    // Zend_Db_Selectオブジェクトを生成
    $sel = $this->_db->select();
    // FROM句の追加
    $sel->from('tbl_tel','*');
    // ORDER句の追加
    $sel->order('updated desc' );
    // クエリを実行
    $stt = $this->_db->query($sel);
    // 結果取得
    $result = array();
    while($row = $stt->fetch()) {
      array_push($result,$row);
    }
    return $result;
  }

  // キー検索メソッドの定義
  public function searchNameTel($key) {
    $key = "%{$key}%";
    // Zend_Db_Selectオブジェクトを生成
    $sel = $this->_db->select();
    // FROM句の追加
    $sel->from('tbl_tel','*');
    // ORDER句の追加
    $sel->order('updated desc' );
    // WHERE句の追加
    $sel->where('name like ?',$key);
    // クエリを実行
    $stt = $this->_db->query($sel);
    // 結果取得
    $result = array();
    while($row = $stt->fetch()) {
      array_push($result,$row);
    }
    return $result;
  }

  // 指定IDレコード検索メソッドの定義
  public function searchIdTel($tel_id) {
    // Zend_Db_Selectオブジェクトを生成
    $sel = $this->_db->select();
    // FROM句の追加
    $sel->from('tbl_tel','*');
    // ORDER句の追加
    $sel->order('updated desc' );
    // WHERE句の追加
    $sel->where('tel_id = ?',$tel_id);
    // クエリを実行
    $stt = $this->_db->query($sel);
    // 結果取得
    $row = array();
    $row = $stt->fetch();
    return $row;
  }
  // 指定IDレコード更新メソッドの定義
  public function updateTel(&$tel) {
    // updateメソッド(戻り値は更新レコード数)
    $num = $this->_db->update(
             'tbl_tel'              // テーブル名
             ,array('name'=>$tel['name']    // 入力データ
             ,'tel_number'=>$tel['tel_number']
             ,'updated'=>$tel['updated'])
             ,$this->_db->quoteInto('tel_id = ?',$tel[':tel_id'])
                               // 条件式
             );
    return $num;
  }
  // 指定IDレコード削除メソッドの定義
  public function deleteTel($tel_id) {
    // deleteメソッド(戻り値は削除レコード数)
    $num = $this->_db->delete(
             'tbl_tel'             // テーブル名
             ,$this->_db->quoteInto('tel_id = ?',$tel_id)
                              // 条件式
             );
    return $num;
  }
}

■ページ単位表示

 データベースのデータをリスト(一覧)表示する場合、その件数が数十件以上と多くなると、一括表示ではなく何ページかに区切って表示する必要性が出てきます。電話帳データをページ単位でリスト表示する場合のスクリプトの例を次に示します。ここでは、1ページあたりの表示件数を5件としています。

MVCモデルにおける相互関連は以下のようにします。

アクションコントローラ/アクション(C) モデル(M) ビュー(V)
(->:フォアワード)
概要
index/index totalTel
searchPageTel
index/index.phtml 開始画面表示
index/search searchNameTel index/search.phtml 電話帳データ検索画面表示
index/input なし index/input.phtml  
index/insert insertTel

・入力データOK
 ->index/index
・入力データエラー
 ->index/input

・入力データOK
 電話帳データ登録
index/edit searchIdTel index/index.phtml 電話帳データ更新画面表示
index/update updateTel ・入力データOK
 ->index/index
・入力データエラー
 ->index/edit
・入力データOK
 電話帳データ更新
index/confirm searchIdTel index/confirm.phtml 電話帳データ削除確認画面表示
index/delete deleteTel ->index/index 電話帳データ削除

 

c:\Apache2.2\zendapps\zf\db\20\controllers\IndexController.php

<?php
// 電話帳モデルのロード
require_once APP_DIR . 'models/telModel.php';
// デフォルトアクションコントローラクラスを生成
class IndexController extends Zend_Controller_Action {
  // 電話帳モデル用オブジェクト(インスタンス)のためのメンバー変数の定義
  private $_tel;
  // 受信データ格納用メンバー変数の定義
  private $_var;

  // 初期化メソッドの定義
  public function init() {
    // 電話帳モデル用オブジェクト(インスタンス)の生成
    $this->_tel = new telModel();
    // 配列の初期化
    $this->_var = array();
  }

  // デフォルト(index/index)アクションの定義
  public function indexAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // フォアワード後のエラーメッセージの受信
    $msg = $request->getUserParam('msg');
    // エラーメッセージをビュー変数に設定
    $this->view->msg = $msg;
    // ページ番号受信
    $page = $request->getQuery('page');
    // 1ページ当たりの表示レコード数
    $perPage = 5;
    if($page == NULL) {
      $page = 1;
    }

    // 全レコード数の取得
    $total = $this->_tel->totalTel();
    // 最終ページ番号計算
    $lastPage = ceil($total/$perPage);
    // 指定ページの電話帳データを取得
    $result = $this->_tel->searchPageTel($page,$perPage);
    // ページ関連データをビュー変数に設定
    $this->view->lastPage = $lastPage;
    $this->view->page = $page;

    // 指定ページの電話帳データをビュー変数に設定
    $this->view->rs = $result;
    // 電話帳管理初期画面表示
    // (index/index.phtml)
  }

  // 検索(index/search)アクションの定義
  public function searchAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // POSTデータの受信
    $key =$request->getPost('key') ;
    // 電話帳データを取得
    $result = $this->_tel->searchNameTel($key);
    $num = count($result);
    // ビュー変数の設定
    $this->view->key = $key;
    $this->view->rs = $result;
    $this->view->msg = "該当レコードは " . $num . "件です。<br />";
    // 検索結果の表示
    // index/search.phtml
  }

  // デフォルト(index/input)アクションの定義
  public function inputAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // フォアワード後のエラーメッセージの受信
    $errorMsgAll = $request->getUserParam('errorMsgAll');
    // エラーメッセージをビュー変数に設定
    $this->view->errorMsgAll = $errorMsgAll;
    // フォアワード後のPOSTデータの受信
    $this->getData();
    // 受信データをビュー変数に設定
    $this->view->var = $this->_var;
    // 電話帳データ入力画面表示
    // (index/input.phtml)
  }

  // 登録(index/insert)アクションの定義
  public function insertAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    $command = stripslashes($request->getPost('command')) ;
    if($command == "登録") {
      // 受信データチェック
      $errorMsg = $this->checkData();
      if(count($errorMsg)) {
        // バリデートでエラーが発生した場合
        $errorMsgAll = implode('<br/>',$errorMsg);
        $request->setParam('errorMsgAll',$errorMsgAll);
        // inputアクションにフォアワード
        $this->_forward('input');
      } else {
        // 現在の年月日、時刻を取得
        $this->_var[':updated'] = date('Y-m-d H:i:s');
        // 電話帳データを登録
        $num = $this->_tel->insertTel($this->_var);
        $request->setParam('msg','1レコードを登録しました。');
        // 初期画面にフォアワード
        $this->_forward('index');
      }
    } else {
      // 初期画面にフォアワード
      $this->_forward('index');
    }
  }

  // 編集用画面表示(index/edit)アクションの定義
  public function editAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // 更新対象レコードIDのQueryデータ受信
    $this->_var['tel_id'] = stripslashes($request->getQuery('tel_id')) ;
    if($this->_var['tel_id']) {
      // 新規に更新対象レコード取得
      $result = $this->_tel->searchIdTel($this->_var['tel_id']);
      // 取得データをビュー変数に設定
      $this->view->var = array('tel_id' => $result['tel_id']
                  ,'name' => $result['name']
                  ,'tel_number' => $result['tel_number']);
    } else {
      // フォアワード後のエラーメッセージの受信
      $errorMsgAll = $request->getUserParam('errorMsgAll');
      // エラーメッセージをビュー変数に設定
      $this->view->errorMsgAll = $errorMsgAll;
      // フォアワード後のPOSTデータの受信
      $this->getData();
      // 受信データをビュー変数に設定
      $this->view->var = $this->_var;
    }
    // 電話帳データ更新画面表示
    // (index/edit.phtml)
  }

  // 更新(index/update)アクションの定義
  public function updateAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    $command = stripslashes($request->getPost('command')) ;
    if($command == "更新") {
      // 受信データチェック
      $errorMsg = $this->checkData();
      if(count($errorMsg)) {
        // バリデートでエラーが発生した場合
        $errorMsgAll = implode('<br/>',$errorMsg);
        $request->setParam('errorMsgAll',$errorMsgAll);
        // editアクションにフォアワード
        $this->_forward('edit');
      } else {
        // 現在の年月日、時刻を取得
        $this->_var['updated'] = date('Y-m-d H:i:s');
        // 電話帳データを更新
        $numt = $this->_tel->updateTel($this->_var);
        $request->setParam('msg','1レコードを更新しました。');
        // 初期画面にフォアワード
        $this->_forward('index');
      }
    } else {
      // 初期画面にフォアワード
      $this->_forward('index');
    }
  }

  // 削除確認(index/confirm)アクションの定義
  public function confirmAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // 削除対象レコードIDのQueryデータ受信
    $this->_var['tel_id'] = stripslashes($request->getQuery('tel_id')) ;
    // 新規に更新対象レコード取得
    $result = $this->_tel->searchIdTel($this->_var[':tel_id']);
    // 取得データをビュー変数に設定
    $this->view->var = array('tel_id' => $result['tel_id']
                ,'name' => $result['name']
              ,'tel_number' => $result['tel_number']);
    // 削除確認画面の表示
    // (index/confirm.phtml)
  }

  // 削除実行(index/delete)アクションの定義
  public function deleteAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // POSTデータの受信(アンエスケープ処理も行う)
    $command = stripslashes($request->getPost('command')) ;
    $tel_id = stripslashes($request->getPost('tel_id')) ;
    if($command == "削除") {
      // 電話帳データを削除
      $num = $this->_tel->deleteTel($tel_id);
      // 削除レコード数メッセージをビュー変数に設定
      $msg = $num . "件のレコードを削除しました<br />";
      $request->setParam('msg',$msg);
    }
    // 初期画面にフォアワード
    $this->_forward('index');
  }
  /**
   * メンバー関数の定義
   */
  // データ受信
  public function getData() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // POSTデータの受信(アンエスケープ処理も行う)
    $var = $request->getPost('tel_id');
    if($var) {
      $this->_var['tel_id'] = stripslashes($var) ;
    }
    $this->_var['name']
       = stripslashes($request->getPost('name')) ;
    $this->_var['tel_number']
       = stripslashes($request->getPost('tel_number')) ;
    return;
  }

  // 受信データのチェック
  public function checkData() {
    // Zend_Validateコンポーネントのロード
    require_once 'Zend/Validate/NotEmpty.php';
    require_once 'Zend/Validate/Regex.php';
    // バリデータのインスタンスを生成する
    $notEmpty = new Zend_Validate_NotEmpty;
    $regex = new Zend_Validate_Regex('/^[0-9-]+$/');
    // POSTデータの受信(アンエスケープ処理も行う)
    $this->getData();

    // エラーメッセージの設定
    $varName = '氏名';
    $notEmpty->setMessage("{$varName}は必須入力です");
    // 空白チェック
    if(!$notEmpty->isValid($this->_var['name'])) {
      foreach($notEmpty->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }

    // エラーメッセージの設定
    $varName = '電話番号';
    $notEmpty->setMessage("{$varName}は必須入力です");
    // 空白チェック
    if(!$notEmpty->isValid($this->_var['tel_number'])) {
      foreach($notEmpty->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }

    // エラーメッセージの設定
    $varName = '電話番号';
    $regex->setMessage("{$varName}には数字と「-」のみが使えます");
    // 文字種チェック
    if(!$regex->isValid($this->_var['tel_number'])) {
      foreach($regex->getMessages() as $msg) {
        $errorMsg[] = $msg;
      }
    }
    return $errorMsg;
  }
}

 ページ単位表示のために、電話帳モデルtelModelクラスに、全レコード数検索メソッドtotalTelと、指定ページのレコード検索メソッドsearchPageTelを次のように追加します。

c:\Apache2.2\zendapps\zf\db\20\models\telModel.php

<?php
//データベース接続クラス(ユーザ定義)のロード
require_once 'DbManager.class.php';

// 電話帳モデルをクラスとして定義
class telModel {
  // データベースアダプタ用メンバー変数を定義
  private $_db;

(中略)

  // 全レコード数検索メソッドの定義
  public function totalTel() {
    // クエリの作成
    $sql = 'select count(*) as total from tbl_tel ;';
    // クエリの実行
    $total = $this->_db->fetchOne($sql);
    return $total;
  }

  // 指定ページのレコード検索メソッドの定義
  public function searchPageTel($page,$perPage) {
    // オフセット
    $offset = ($page-1)*$perPage;
    // Zend_Db_Selectオブジェクトを生成
    $sel = $this->_db->select();
    // FROM句の追加
    $sel->from('tbl_tel','*');

    // ORDER句の追加
    $sel->order('updated desc' );
    // LIMIT-OFFSET句の追加
    $sel->limit($perPage,$offset);
    // クエリを実行
    $stt = $this->_db->query($sel);
    // 結果取得
    $result = array();
    while($row = $stt->fetch()) {
      array_push($result,$row);
    }
    return $result;
  }

指定ページのレコード検索メソッドsearchPageTelでは、指定ページのデータを取得するためにSELECT文でLIMIT句を使用します。LIMIT句の構文は次のとおりです。$selは、Zend_Db_Selectオブジェクトです。

$sel->limit(1ページ当たりの表示件数,オフセット)

【例】
$sel->limit(5,25);

c:\Apache2.2\zendapps\zf\db\20\views\script\index\index.phtml

<html>
<head>
<title>admin_tel</title>
<style type="text/css">
<!--
.ac {text-align:center
}
.ar {text-align:right
}
-->
</style>
</head>
<body>
<p class="ac">電話帳管理</p>
<!-- 処理終了後のメッセージ表示 -->
<?php if($this->msg != "") {
     print $this->msg;
   }
?>
<table border="0" width="100%">
 <tr>
  <form method="POST" action="<?php print BASE_DIR ?>index/search">
  <td class="ar">
   <input type="text" name="key" value="<?php print $this->name; ?>">
   <input type="submit" name="command" value="検索">
  </td>
  </form>
 </tr>
 <tr>
  <form method="POST" action="<?php print BASE_DIR ?>index/input">
  <td>
   <input type="submit" name="command" value="新規">
  </td>
  </form>
 </tr>
</table>
<div class='ac'>
<table border="1" cellspacing="0" cellpadding="0">
 <tr>
  <th>更新</th>
  <th>削除</th>
  <th>id</th>
  <th>氏名</th>
  <th>電話番号</th>
  <th>更新日</th>
 </tr>
 <?php foreach ($this->result as $rows) { ?>
 <tr>
  <td><a href="<?php print BASE_DIR . 'index/edit?tel_id='
       . $rows['tel_id']; ?>">更新</td>
  <td><a href="<?php print BASE_DIR . 'index/confirm?tel_id='
       . $rows['tel_id']; ?>">削除</td>
  <td><?php print $rows['tel_id']; ?></td>
  <!-- ユーザ入力データの表示にはサニタイジングを行う -->
  <td><?php print $this->escape($rows['name']); ?></td>
  <td><?php print $this->escape($rows['tel_number']); ?></td>
  <td><?php print $rows['updated']; ?></td>
 </tr>
 <?php } ?>
</table>
<span class='xs'>
<?php if($this->page > 1) { ?>
  <a href="<?php print BASE_DIR ?>index/index?page=1">最初</a>
  <a href="<?php print BASE_DIR ?>index/index?page=<?php
          print($this->page-1) ?>">前のページ</a>
<?php } else { ?>
  最初
    前のページ";
<?php } ?>
   
<?php if($this->page < $this->lastPage) { ?>
  <a href="<?php print BASE_DIR ?>index/index?page=
       <?php print($this->page+1) ?>">次のページ</a>
  <a href="<?php print BASE_DIR ?>index/index?page=<?php
        print $this->lastPage?>">最後</a>
<?php } else { ?>
  次のページ
    最後
<?php } ?>
</span>
</div>

</body>
</html>

 ページ単位でリストを表示するビュースクリプトには、表示ページを制御するためのリンク、いわゆる「ページャ」情報が必要です。ページャ情報にはいろんな形式がありますが、ここでは、「最初  前のページ   次のページ  最後」というリンクを表示します。「最初」リンクは最初のページを表示するリンク、「前のページ」リンクは前ページを表示するリンク、「次のページ」リンクは次ページを表示するリンク、「最後」リンクは最後のページを表示するリンクです。

■PEAR::Pager

 ページ単位表示のためのページリンクの生成のために、PHPの汎用ライブラリであるPEARに、PEAR::Pagerライブラリが提供されています。ページ単位表示のためにこのPEAR::Pagerライブラリを使用したpager_telアプリケーションのスクリプトを以下に示します。

// PEAR::Pagerライブラリのロード
require_once 'Pager/Pager.php';
$p = Pager::factory(array('perPage'=>1ページのレコード数
                ,'totalItems'=>全レコード数
                ,'path'=>'リンク生成のためのドキュメントルートURI'
                ,'fileName'=>'アプリケーションのベースURI/コントローラ名/アクション名/page/%d'
                ,'append'=>FALSE
                ,'currentPage'=>現在のページ番号
                )
            );
$pager = $p->getLinks();

クエリ情報にページ情報しかない場合はappendパラメータはFALSEにし、ページ番号は、fileNameパラメータの中に、「/page/%d」として記述します。

 pager_telアプリケーションのアクションコントローラIndexControllerに以下のようにPEAR::Pager関連コードを記述します。

c:\Apache2.2\zendapps\zf\db\21\controllers\IndexController.php

<?php
// 電話帳モデルのロード
require_once APP_DIR . 'models/telModel.php';
// PEAR::Pagerライブラリのロード
require_once 'Pager/Pager.php';
// デフォルトアクションコントローラクラスを生成
class IndexController extends Zend_Controller_Action {

(中略)

  // デフォルト(index/index)アクションの定義
  public function indexAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // フォアワード後のエラーメッセージの受信
    $msg = $request->getUserParam('msg');
    // エラーメッセージをビュー変数に設定
    $this->view->msg = $msg;

    // ページ番号受信
    $page = $request->getUserParam('page');
    // 1ページ当たりの表示レコード数
    $perPage = 5;
    if($page == NULL){
      $page = 1;
    }
    // 全レコード数の取得
    $total = $this->_tel->totalTel();
    // 指定ページの電話帳データを取得
    $result = $this->_tel->searchPageTel($page,$perPage);
    // 最終ページ数計算
    $lastPage = ceil($total/$perPage);
    // ページャオブジェクトの生成
    $p = Pager::factory(array('perPage'=>$perPage
                 ,'totalItems'=>$total
                 ,'path'=>''
                 ,'fileName'
                   =>BASE_DIR.'index/index/page/%d'
                 ,'append'=>FALSE
                 ,'currentPage'=>$page
                 )
              );

    // ページャ情報取得
    $pager = $p->getLinks();
    // ページャ情報をビュー変数に設定
    $this->view->pager = $pager;
    // 指定ページの電話帳データをビュー変数に設定
    $this->view->result = $result;
    // 電話帳管理初期画面表示
    // (index/index.phtml)
}
(後略)

 PEAR::Pagerライブラリが生成するページャ関連情報は次のとおり連想配列で提供されます。デザインにあわせて必要な配列要素をprintすることになります。ここでは$pager['all']を使ってみます。

$pager[0]=<< Back
$pager[1]=1 2 3
$pager[2]=Next >>
$pager[3]=[1]
$pager[4]=[3]
$pager[5]=<< Back 1 2 3 Next >>
$pager[6]=
$pager[back]=<< Back
$pager[pages]=1 2 3
$pager[next]=Next >>
$pager[first]=[1]
$pager[last]=[3]
$pager[all]=<< Back 1 2 3 Next >>
$pager[linktags]=
$pager[linkTagsRaw][first][url]=/zf/db/21/index/index/page/1
$pager[linkTagsRaw][first][title]=first page
$pager[linkTagsRaw][prev][url]=/zf/db/21/index/index/page/1
$pager[linkTagsRaw][prev][title]=previous page
$pager[linkTagsRaw][next][url]=/zf/db/21/index/index/page/3
$pager[linkTagsRaw][next][title]=next page
$pager[linkTagsRaw][last][url]=/zf/db/21/index/index/page/3
$pager[linkTagsRaw][last][title]=last page

 ビュースクリプトindex/index.phtmlにページャ情報を使用する例を以下に示します。単にビュー変数$this->pager['all']をprintするのみで、非常に単純なコードで記述できます。

c:\Apache2.2\zendapps\zf\db\21\views\script\index\index.phtml

<html>
<head>
<title>admin_tel</title>
<style type="text/css">
<!--
.ac {text-align:center
}
.ar {text-align:right
}
-->
</style>
</head>
<body>
<p class="ac">電話帳管理</p>
<!-- 処理終了後のメッセージ表示 -->
<?php if($this->msg != "") {
     print $this->msg;
   }
?>
<table border="0" width="100%">
 <tr>
  <form method="POST" action="<?php print BASE_DIR ?>index/search">
  <td class="ar">
   <input type="text" name="key" value="<?php print $this->name; ?>">
   <input type="submit" name="command" value="検索">
  </td>
  </form>
 </tr>
 <tr>
  <form method="POST" action="<?php print BASE_DIR ?>index/input">
  <td>
   <input type="submit" name="command" value="新規">
  </td>
  </form>
 </tr>
</table>
<div class='ac'>
<table border="1" cellspacing="0" cellpadding="0">
 <tr>
  <th>更新</th>
  <th>削除</th>
  <th>id</th>
  <th>氏名</th>
  <th>電話番号</th>
  <th>更新日</th>
 </tr>
 <?php foreach ($this->result as $rows) { ?>
 <tr>
  <td><a href="<?php print BASE_DIR . 'index/edit?tel_id='
       . $rows['tel_id']; ?>">更新</td>
  <td><a href="<?php print BASE_DIR . 'index/confirm?tel_id='
       . $rows['tel_id']; ?>">削除</td>
  <td><?php print $rows['tel_id']; ?></td>
  <!-- ユーザ入力データの表示にはサニタイジングを行う -->
  <td><?php print $this->escape($rows['name']); ?></td>
  <td><?php print $this->escape($rows['tel_number']); ?></td>
  <td><?php print $rows['updated']; ?></td>
 </tr>
 <?php } ?>
</table>
<span class='xs'>
<!-- PEAR::Pagerライブラリによるページャの表示 -->
<?php print $this->pager['all'] ?>
</span>
</div>
</body>
</html>

■ページャの日本語表記

PEAR::Pagerライブラリで生成されるページャ情報を日本語表記にすることも可能です。その例を以下に示します。ページャオブジェクトの生成時に、必要なパラメータに日本語を設定します。

c:\Apache2.2\zendapps\zf\db\22\controllers\IndexController.php

<?php
// 電話帳モデルのロード
require_once APP_DIR . 'models/telModel.php';
// PEAR::Pagerライブラリのロード
require_once 'Pager/Pager.php';
// デフォルトアクションコントローラクラスを生成
class IndexController extends Zend_Controller_Action {

(中略)

  // デフォルト(index/index)アクションの定義
  public function indexAction() {
    // リクエストオブジェクト(インスタンス)の生成
    $request = $this->getRequest();
    // フォアワード後のエラーメッセージの受信
    $msg = $request->getUserParam('msg');
    // エラーメッセージをビュー変数に設定
    $this->view->msg = $msg;

    // ページ番号受信
    $page = $request->getUserParam('page');
    // 1ページ当たりの表示レコード数
    $perPage = 5;
    if($page == NULL){
      $page = 1;
    }
    // 全レコード数の取得
    $total = $this->_tel->totalTel();
    // 指定ページの電話帳データを取得
    $result = $this->_tel->searchPageTel($page,$perPage);
    // 最終ページ数計算
    $lastPage = ceil($total/$perPage);
    // ページャオブジェクトの生成
    $p = Pager::factory(array('perPage'=>$perPage
                 ,'totalItems'=>$total
                 ,'path'=>''
                 ,'fileName'=>BASE_DIR.'index/index/page/%d'
                 ,'append'=>FALSE
                 ,'currentPage'=>$page
                 ,'prevImg'=>'前のページ'
                 ,'nextImg'=>'次のページ'
                 ,'firstPageText'=>'最初'
                 ,'lastPageText'=>'最後'
                 )
              );
    // ページャ情報取得
    $pager = $p->getLinks();
    // ページャ情報をビュー変数に設定
    $this->view->pager = $pager;
    // 指定ページの電話帳データをビュー変数に設定
    $this->view->rs = $result;
    // 電話帳管理初期画面表示
    // (index/index.phtml)
}
(後略)

 ビュースクリプトindex/index.phtmlは、以下のように変更します。連想配列情報$pager['all']でなく、$pager['first']、$pager['back']、$pager['next']、$pager['last']を組み合わせて使うこととします。

c:\Apache2.2\zendapps\zf\db\22\views\script\index\index.phtml

<html>
<head>
<title>pager_tel</title>

(中略)

 <?php foreach ($this->rs as $rows) { ?>
 <tr>
  <td><a href="<?php print BASE_DIR . 'index/edit?tel_id='
       . $rows['tel_id']; ?>">更新</td>
  <td><a href="<?php print BASE_DIR . 'index/confirm?tel_id='
       . $rows['tel_id']; ?>">削除</td>
  <td><?php print $rows['tel_id']; ?></td>
  <!-- ユーザ入力データの表示にはサニタイジングを行う -->
  <td><?php print $this->escape($rows['name']); ?></td>
  <td><?php print $this->escape($rows['tel_number']); ?></td>
  <td><?php print $rows['updated']; ?></td>
 </tr>
 <?php } ?>
</table>
<!-- PEAR::Pagerライブラリによるページャの表示 -->
<?php print $this->pager['first'] ?>
<?php print $this->pager['back'] ?>
<?php print '    ' ?>
<?php print $this->pager['next'] ?>
<?php print $this->pager['last'] ?>

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

 

 


前へ | 目次へ |次へ  | Yamada-Lab

執筆 山田豊通
更新日: 2009年4月6日