前回までに足りなかった部分を補足してみる。



「V」に任せる処理はすでにはっきりしているが、「M」に書くべきか「C」に書くべきかいまいち迷ってしまうことがある。


そこで方針をより明確にして「M」なのか「C」なのか迷わないような線引きを考える。



まず「M」にはビジネスロジックを書くという方針は決まっているので、「C」にはそれ以外を書くことになる。


「C」で受け取った値(リクエストパラメータやフォームデータ)は「C」内の関数reqdata()によって未定義なら空文字にする処理を施す。(これはビジネスロジックにかかわらず施す処理)


function reqdata($key)
{
    return isset($_REQUEST[$key]) ? $_REQUEST[$key] : '';
}



これ以上の処理(必須入力かどうかとか最大文字列長チェックなどの検証いわゆるバリデーション)になるとビジネスロジック側でやるべき範ちゅうに入ってくるので「M」に任せる。


ということで「M」内には必須入力などをチェックするis_data()関数を用意することでうまくいく。


function is_data($s)
{
    return strlen(trim($s));
}
function seterrmsg($s)
{
    $this->val['errmsg']= $s; return false;
}


if (!$this->is_data($s)){
    return $this->seterrmsg("必須入力されてません。");
}
if ($this->is_data($s) > MAX_LENGTH){
    return $this->seterrmsg("入力が多すぎます。");
}



「C」側から「M」にデータ要求してそれを表示させる場合にはhtmlspecialchars()関数など使ってエンティティ変換する必要があるのでこの処理は「C」で行う。(いわゆるサニタイズなどと言われるもの)


つまり「M」から返すデータはタグを含まないものかもしくはエンティティ変換済みでタグ付きのものにしなければ処理はうまくいかなくなる。


「M」から受け取った値のリストを「C」で表にしたい場合は、「C」内に表作成関数(エンティティ変換付き)を作るなどするのが素直な解り易い方法になる。



今朝方すごい久しぶりに夢を見た。


つまり久しぶりに頭脳を使ったことで記憶の再編成が起きたということか?



夢の内容は一言で言うとタイトルに書いた「ツナ缶ストック計画」だった。





世界的なまぐろ漁の全面禁止を受けて自然と集まったまぐろ好き達の中に私がいて、もうこうなってしまってはツナ缶買い占めるしかないという話になって実行部隊が組まれた。



この実行部隊のトップに私がいて、近所のスーパーから順に金にモノを言わせて手当たり次第に買いあさって行くわけですが、そのうちに私達組織が町の嫌われ者なりながらもお構いなしにどんどん範囲を拡大していって大エンディングを迎える。



大エンディングで「私達の闘いはこれからも続く」的なことを言って目が覚めた。





そう言えば最近まぐろ漁に関する制限のニュースがあってそれが頭に残っていたんだろう。。



私はまぐろはまったく食べていないなぁ。。いやツナ缶はよく食べてるぞ



ということで「ツナ缶ストック計画」がリアルに始まる日が来るやも知れませぬが、とりあえず現ツナ缶ストック数を数えてみたら6個だった。



昨日はひどいMVCになってしまいましたので、もう少しマシになるように書いていきます。


ここにある特集サイト (ショッピングカート作成)が解り易かったのでここを参考に自分なりによりシンプルになるようにしてみます。


まず「Model」部分は値を保持する必要性があるのでクラス化します。
「View」に関してはとりあえずクラス化せずに外部ファイル分けにとどめて処理は前回同様、テンプレートエンジンは使わないでいきたいと思います。
直接呼ばれることになる「Controller」も同様にクラス化しなくてもいけそうです。



そして、多人数で一つのシステムを開発する場合を考えてこれらはそれぞれ最低でも3つのファイルに独立させておくこととします。



ということで、「Model」にはビジネスロジックとなる作成サイトに則したデータの読み書き処理いわゆるCRUD(Create,Read,Update,Delete)を関数分けして、「View」の処理コードはほぼ固定のままでViewから読み込むテンプレート類をデザイナーが独立して作成できる形にして、「Controller」にはswitch文で処理分けしてModelに書いた関数でデータ操作していく形でいけそうな気がします。



[テンプレート規則]
デザインがされていてコンテンツのない抜け殻のHTML
%TITLE%, %BODY%のように置き換え部分を「%」で囲ったキーワードという形式にします。



■init.tpl

<body>
<h3>%TITLE%</h3>
<form action="%ACTION%" method="post">
<input type="hidden" name="act" value="%MOVE%">
<input type="text" name="t1">%t1%<br>
<input type="submit" value="送信">
</form>
</body>



■end.tpl

<body>
<h3>%TITLE%</h3>
<form action="%ACTION%" method="post">
<input type="text" name="t1">%t1%<br>
<input type="submit" value="送信">
</form>
</body>



テンプレートはデザインもなにもない適当なものです。
今回は表示ができてるかどうかを見るだけにとどめておくということで



■inc_model.php
<?php
class model
{
    // データ保持用
    var $val= array();

    // コンストラクタ
    function model()
    {
        // このクラス内で起きたエラーメッセージをセット
        $this->val['errmsg']= '';
    }


    // ビジネスロジックのCRUD群(ファイルやDBなどデータ操作)
    function create($id)
    {
        // create処理
        return "createが成功したかどうかの真偽値";
    }
    function read($id)
    {
        // read処理
        return "読み出しデータ値";
    }
    function update($id, $data)
    {
        // update処理
        return "updateが成功したかどうかの真偽値";
    }
    function delete($id)
    {
        // delete処理
        return "deleteが成功したかどうかの真偽値";
    }
}
?>



■inc_view.php

<?php
function view($obj)
{
    $tpl= empty($obj->val['errmsg']) ? $obj->val['template'] : $obj->val['errtpl'];
    $html= file_get_contents($tpl);
    echo preg_replace('/%(\w+)%/e', 'isset($obj->val["$1"]) ? $obj->val["$1"] : ""', $html);
    exit;
}
?>



■mvc.php(最初に直接呼ばれるControllerに当たるファイル)
<?php
error_reporting(E_ALL);
ini_set("display_errors", 1);


// 外部ファイルのModelとViewを呼び出し
include("./inc_model.php");
include("./inc_view.php");



// Controller内関数
function reqdata($key)
{
    return isset($_REQUEST[$key]) ? $_REQUEST[$key] : '';
}



// データ保持用のModelクラスをインスタンス化してテンプレートおよび置換データをセット
$obj= new model;
$obj->val['errtpl']= 'error.tpl';
$obj->val['ACTION']= $_SERVER['SCRIPT_NAME'];
$act= reqdata('act');


switch ($act){
case "act1": $tpl= "init.tpl";
    $obj->val['TITLE']= "ACT1画面";
    $obj->val['MOVE']= "act2";
    $obj->val['t1']= htmlspecialchars(reqdata('t1'));
    break;
case "act2": $tpl= "end.tpl";
    $obj->val['TITLE']= "最終画面";
    $obj->val['t1']= htmlspecialchars(reqdata('t1'));
    break;
default: $tpl= 'init.tpl';
    $obj->val['TITLE']= "初期画面";
    $obj->val['MOVE']= "act1";
}
$obj->val['template']= $tpl;



// 保持されたデータをViewに渡して表示させる
view($obj);



?>



ここではModelの関数を適当に書いてみたので使ってないですが、冗長になるデータの読み書き処理がModelにまとまることでController側では制御がその分スッキリして見通しがよくなるのでswitch文でまとめやすい感じがします。


どの画面でどのテンプレートを使ってるかとか、act値で次にどの画面に遷移させているかとかが見やすいですね。

次回は実際に実践的なものを作ってみたいと思います。