バグ報告
動作環境
checkPHP
PHP 5.3以上推奨
※制作者環境は5.6.15
※PHP7には対応していません
checkMySQL
なるべく最新 ※制作者環境はMariaDB 10.1.10
checkPostgreSQL
なるべく最新 ※制作者環境はPostgreSQL9.5
checkライブラリなど
GDライブラリ、ZipArchiveライブラリ利用
check設定など
mod_rewrite利用

お使いのサーバがそれぞれ必要なバージョン・ライブラリ・設定をサポートしているかどうかは、サーバー管理者に問い合わせをするか、サーバーにてphpinfo() を実行して確認してください。

ちなみに制作者はEclipse Platform Version: Neon.2 (4.6.2)にて開発しております。
インストール
check最新パッケージをダウンロード して解凍
check「fwcore」「htdocs」フォルダをサーバの任意の場所に設置
「fwcore」はフレームワークのファイル群です。公開ディレクトリ(Webブラウザから参照できる場所)以外に 置いてください。
「htdocs」はWebからアクセスする公開ディレクトリです。ディレクトリ名は自由に変えても構いません。
いずれもディレクトリ名は自由に変更しても良いですが、変更した場合はPHPソース内のパス設定を変更する必要があります。
また、配下のディレクトリ構成やディレクトリ名は変更しないことを推奨します。
※変更したことにより発生した問題については一切の責任を負いません。
「docs」はテーブル作成用のマクロExcelが入っています。別の項で説明します。
checkサーバの設定で「htdocs」をDocumentRootに設定する
正確に設定しないとWebページが表示されません。

ここまで設定し、WEBブラウザでドキュメントルートにアクセスしてみましょう。下記のような画面が表示されればインストール完了です。
Welcom KoMA-PHP !!



この画面が表示されない場合は、DocumentRootの設定がうまくいっていない場合があります。
fwcoreと同階層にhtdocs(または変更後のディレクトリ)を置いていない場合は、htdocs/index.phpの3行目が、fwcoreへの階層と相違ないかを確認してください。
require_once __DIR__.DS.'..'.DS.'..'.DS.'fwcore'.DS.'Start'.DS.'start.php'; 
※初期は同階層を想定したパスになっています

※最低限の機能で提供させていただいているフレームワークのためデザインについては制作者様の方で対応いただくことになります。
ディレクトリ構成
KoMA-PHPの初期ディレクトリ構成は下記のようになっています。
[フレームワーク本体ディレクトリ]
fwcore┬Core 処理クラス群 各コントローラ・ビューよりクラス名::メソッド名で呼び出し可能 
 │    ├Read コアクラスの読み込み順番を定義 Coreにクラスを作った場合でもここに書かないと呼び出し不可
 │    ├Start index.phpから実行される
 │    ├Model 各DBテーブルとのやり取り用のクラス群 「Model」を継承すること 「・Modelについて」を参照
 │    ├Config Core/Configクラスから読まれる設定ファイル群。アプリケーション全体の設定はapp.php参照。
 │    ├Controller 各ページの処理クラス群
 │    ├storage	ストレージ ログファイル、アップロードファイルの一時置き場、アイコン置き場、CSVなど
 │    ├View  画面出力ファイル
 │    │└template  テンプレート(画面枠、入力部品共通、一覧共通、検索部品共通、WEBテンプレート読み込み画面枠 など)
 │    └route.php ルーティング設定ファイル
 │
[Webのルートディレクトリ]
htdocs┬css CSSファイル格納ディレクトリ
    ├js JavaScriptファイル格納ディレクトリ+jQueryファイル群
    ├.htaccess すべてのアクセスをindex.phpにRewriteする設定
    └index.php .htaccessで設定したアクセス先

URLについて
通常PHPをべた書きする場合は、●●.phpに直接アクセスし、URLもそのままとなりますが、
KoMA-PHPでは全てのアクセスをhtdocs/index.phpに集約しています。内部でアクセスURLを解析し、ルーティングで受け取り、各処理(コントローラ、ビュー)に渡しています。
例えば、ユーザ編集画面は下記のようなURLになります。
http://hogehoge/UserInput/Edit/3

KoMA-PHPでは「UserInput」をルーティングで機能名と解釈し、それより下位の階層はルーティングで設定した変数名で受け取ります。

クエリストリング(?hoge=2のようなGETパラメータ)も許可していますが、ルーティングURLでは無視されます。
GETパラメータとしてプログラムで受け取ることは可能なので、ルーティングだけでは制御がしきれないケース、例えば一覧画面の並べ替え昇順降順などを渡すときなどの利用が考えられます。
もちろん、工夫次第で並べ替えもルーティングのみで対応は可能です。

ただし文字列に「/」や全角文字が入ってしまうと正しくパラメータを制御できなくなりますので、urlencode() を利用したり「/」を別の文字列に変更するエンコードを施し、受け取る際にデコードしてリスクを回避しましょう。
※KoMA-PHPではfwcore/Config/appの「SLASH」利用を推奨しています。
データベース設定
接続設定
KoMA-PHPはMySQLまたはPostgreSQLのみ対応しています。
fwcore/Config/database.phpを開きます。
<?php
/**
 * [Config]databse:DB接続設定
 * @link http://komaphp.k-akimoto-matsu.net/Manual#subtitle4 マニュアル@データベース設定
 */
return [
	"prefix"=>"hogehoge",
	"db"=>"mysql",
	"charset"=>"utf8",
	"postgres"=>[
		"dbname"=>"dbname",
		"host"=>"host",
		"user"=>"user",
		"pass"=>"password",
	],
	"mysql"=>[
		"dbname"=>"dbname",
		"host"=>"host",
		"user"=>"user",
		"pass"=>"password",
	]
];

prefix・・・KoMA-PHPでは単純なテーブル名を指定することによるセキュリティリスク回避のために接頭辞をつけたテーブル名をデフォルトとして取り扱っております。自身で接頭辞のないテーブルを作成される際には、プログラムコードの下記のようになっている箇所を全て修正してください。
Config::get("database","prefix")."_".$this->table

db・・・利用するDBを入力します。"mysql","postgresql"のいずれかを設定してください。
charset・・・データベースの文字コードを設定します。よほどのことがない限り"utf8"で問題ないと思います。
dbname,host,user,pass・・・お使いのデータベースのログイン情報を入力します。

テーブル設定
fwcore/Config/tables.phpを開きます。接頭辞を含んだ テーブル名とカラム名を設定します。登録、更新などの処理で存在しないカラムは除くチェックをしていますので、正確に入力してください。
※次項で同梱のマクロについて解説していますのでお読みいただきぜひご利用ください。
<?php
/**
 * [Config]tables:DBテーブル設定
 * @link http://komaphp.k-akimoto-matsu.net/Manual#subtitle4 マニュアル@データベース設定
 */
return [
		//ユーザーテーブル
		"hogehoge_m_user"=>[
				"user_id"=>"ID",
				"login_id"=>"ログインID",
				"password"=>"パスワード",
				"name"=>"名前",
				"mail"=>"メールアドレス",
				"lockcnt"=>"",
				"last_logindate"=>"最終ログイン日時",
				"create_date"=>"登録日時",
				"update_date"=>"最終更新日時",
		],
];

テーブル作成マクロ
docsディレクトリにtable_createツールを同梱しております。
KoMA-PHPでの開発をよりスムーズにするため、データベース構築をサポートするためのツールとして作成しました。そのままデータベース設計書としてもお使いいただけます。
ただし、単にCreateTable(DROP含む)文、初期データINSERT文とtables.phpを発行するだけのツールであるため、詳細の入力チェックは行っておりません。

sqlシート:
接頭辞と利用するDBを設定します。DBはMySQLとPostgreSQLのみ選択可能となっています。接頭辞については「_」を最後に付加してください。
例:hogehoge_ ※接頭辞はfwcore/Config/database.prefixに同じものを設定します。
※モデル親クラスを改修する場合はご自身で調整してください。

{テーブル名}シート:
テーブルの設定を書き込んでいきます。カラムは24個までとなっていますが、B30セルから設定する初期データさえ気にしなければ幾つまで増やしてもらっても構いません。カラム名、カラム型、桁数、NotNull、PKを正確に設定してください。

設定が終わったらsqlシートの「CREATE&INSERT作成」ボタンを押下してください。ツールと同じ階層に「creat.sql」と「tables.php」が生成されます。

※integer,timestampなどは桁数は必要ありませんが、簡易ツールのため入力があった場合はSQLに含まれてしまいます。
 そのままSQLを実行するとエラーとなります。ご注意ください。
※簡易ツールのためPKはひとつまでしか有効となりません。複数PKを設定される場合は、発行されたSQL文を修正してください。
※tables.phpの文字コードはSJISであり、phpプログラム冒頭の<?phpが含まれません。fwcore/Config/tables.phpに上書きしても利用できません。
 ファイルを開き必要部分をテキストコピーし、fwcore/Config/tables.phpにペーストしてご利用ください。
※create_date,update_dateはinsert,update実行時に自動で設定される項目です。
 tables.phpに含まれていなければ登録時のSQLには含まれないので不要な場合は設定しなくても構いません。
命名規則
KoMA-PHPにおけるファイル名・クラス名などの命名は基本的には自由ですが幾つかの命名規則を設けさせていただいています。

クラス名全体
・ファイル名とクラス名は同一とすること。
・クラス名の1文字目は大文字とすること(大文字でなくても構いませんが統一のため)
 これは指定のクラスファイルの存在チェック後、すぐに同名のクラス名を呼び出すような仕組みになっているためです。
 万が一、違う名称にしてしまった場合は、呼び出しやnewが失敗しますのでご注意ください。

コントローラ
・{機能名}Controllerとすること。

ルーティング変数
・同じ親ルート内で重複させないこと。
・長い名前にしないこと。できれば単語1つで3~8文字程度が望ましい
※ただしViewには幾つか予約語があります。上書きしてしまうので、$root,$masterroot,$ow,$route,$modeなどは設定しないようにしてください。
 詳しくはfwcore/Core/Viewをご確認ください。

ファイル名拡張子
・コントローラ、ビュー、その他クラスPHPプログラムファイル ・・・.php
・テンプレート(利用する場合のみ)・・・.tpl

その他についてはディレクトリ名、ファイル名、メソッド名などなど制作者様自身で自由に名づけることができます。
※当然ですが、名づけた名前で正確に呼び出し・読み込みを行うようにコーディングしてください。
ルーティング
特徴 でも述べている通り、KoMA-PHPの全てと豪語しているのがこのルーティングです。
MVCが基本ですが、リクエストを受けるのは全てこのルーティングということもありますので「RMVC」 と表現してもいいかもしれません。それだけKoMA-PHPでは重要な役割を担っています。
※MVCのことが今ひとつわかっていないという方は以下の記事が参考になります。
Model View Controller(Wikipedia)
MVC、本当にわかってますか?
ルーティングとは、データを目的地まで送信するための配送経路の制御である(Wikipedia より)とあるように、
ブラウザからのURLリクエストを受けて該当する処理に振り分けるための設定を行うためのものです。システム構築を行う際の基本設計部分とも言えます。
KoMA-PHPを扱う上では、ほとんどのコーディングをコントローラとビューに費やすこととなります。
最も自由に構築ができるコントローラとビューのディレクトリ設定や命名まで行います。
このルーティングで設定した内容に沿ってコントローラ・ビューを配置し、受けたリクエストに対する処理を書いていくこととなります。

代表的な設定例を幾つか挙げます。
/**
 * [Route]ルーティング
 * URLにより呼び出すコントローラとビューを指定する
 *
 * @link http://komaphp.k-akimoto-matsu.net/Manual#subtitle6 マニュアル@ルーティング
 * @author TakanoriMatsushige < http://k-akimoto-matsu.net/Inquiry >
 * @copyright k_akimoto_matsu. All Rights Reserved
 */
return [
    'index'=>[
        'view'=>'index/top',
        'controller'=>'index/Top',
    ],
    'UserList'=>[
        'view'=>'user/userlist',
        'controller'=>'user/UserList',
        'pager'=>true,
        'set'=>[
            1=>'page',
        ],
    ],
    'UserInput'=>[
        'view'=>'user/userinput',
        'controller'=>'user/UserInput',
        'pager'=>true,
        'set'=>[
            1=>'action',
            2=>'target',
        ],
    ],
];

必須条件
’view’と'controller'は基本的に必須です。しかしfwcore/Core/View内で設定されなかったときのデフォルトで読み込むControllerを設定することもできます。
特に管理ページと公開ページで役割を分ける際に、管理ページでコンテンツを登録した名前で動的にURLとページを生成する場合などは、全てのアクセスを受けるControllerに集中させ、リクエストを処理したほうが良いでしょう。
実際に本サイト では汎用記事ページに対してはデフォルトで利用するコントローラを設定しています。

URLアクセス
リクエストURLの最初の区切り(当ページであれば「Manual」部分)を最初に設定します。上記例ですと「index」「UserList」「UserInput」がそれにあたります。
fwcore/Core/Viewにより「index」をURLに何も付加されていない場合の初期設定に当てることもできます。

上記例から以下のように受け取り先が決まります。
http://hogehoge/ → index
http://hogehoge/UserList → UserList
http://hogehoge/UserInput → UserInput

ビュー・コントローラの呼び出し先
ディレクトリ構成 の項ではコントローラは「fwcore/Controller」にビューは「fwcore/View」にそれぞれ格納するようになっています。
それぞれの親ディレクトリから下のディレクトリを指すようになっています。
'view'=>'index/top' と指定すれば fwcore/View/index/top.phpを読み込み、
'controller'=>'index/Top' と指定すればfwcore/Controller/index/TopController.phpを読み込むこととなります。

作り方次第ではありますが、別のURLリクエストを受け取り同じコントローラ、ビューを利用する...ということも可能です。それだけ自由度が高いということです。

※指定したファイルがない場合、エラーページ(fwcore/View/error.php)を表示するようになっています。
※「/」で区切ることにより機能別ディレクトリにプログラムを配置できるようになっています。
 何階層でも可能ですが、最後の区切りが呼び出すファイル名となっています。
※コントローラはControllerは設定名称に必要ありません。

パラメータ
'set'項目に配列で1から順番にリクエストを受け取りたい変数名で設定します。
例の「UserInput」の場合、下記のようなリクエストがあったとします。
http://hogehoge/UserInput/Add/100
すると、ビューでは$actionで「Add」が取得でき、$targetで「100」が取得できます。
コントローラでは$ret["route"]["action"]で「Add」が取得でき、$ret["route"]["target"]で「100」が取得できます。
このように機能名以下の区切りで受け取るパラメータの設定をします。
パラメータの設定がなく、リクエストがあった場合は受け取れませんのでご注意ください。
つまり、ルーティングを設定することにより、不正アクセスの防止としても利用できます。

特殊設定
’pager’=>true
一覧ページによるページング機能を提供します。
ページ番号を取得するために、'set'の一番目に 1=>'page' をセットで設定する必要があります。そのほか、独自の設定を組み込むことも可能です。

例:
「管理画面専用の機能」という意味で 'master'=>trueとし、fwcore/Core/View内でログインしているかどうかを判定する時に
管理画面専用ページへのリクエストかを判断する材料として利用する etc.
コントローラ
ここでようやくMVCの「C」を担う、コントローラの登場です。
基本的には「ModelとViewをつなぐ」ですとか、ロジックをゴリゴリ書く場所、といったイメージかと思いますが、KoMA-PHPでも基本的な考え方を踏襲しており、その上で独自の仕様で設計されております。
それが「ルーティングから来たのだからルーティングから渡されたURLリクエストを受け取る」という考え方です。
fwcore/Controller/BaseController.phpを見てみましょう。コントローラの基底クラスとして鎮座しています。
<?php
/**
 * [Controller]BaseController:基底コントローラ
 * 全てのコントローラで継承すること
 *
 * abstract class : post()|get()
 * @link http://komaphp.k-akimoto-matsu.net/Manual#subtitle7 マニュアル@コントローラ
 * @author TakanoriMatsushige < http://k-akimoto-matsu.net/Inquiry >
 * @copyright k_akimoto_matsu. All Rights Reserved
 */
abstract class BaseController{
	/**
	 * Getメソッド(強制)画面表示
	 */
	abstract protected function get();

	/**
	 * Postメソッド(強制)POST送信
	 * Input::get({コントロール名})またはInput::all()でPOST値(FILES,REQUESTも)を取得可能
	 */
	abstract protected function post();
//~省略~
}
コントローラを作る場合は必ずこの基底クラスを継承した上で、get()メソッド、post()メソッドを実装しなければなりません。
機能によってはいずれかのメソッドがが不要なページもあるかもしれませんが、不正なアクセスがあった場合なども考慮して、両方実装することを数少ない規約のひとつとさせていただいております。
例えば、データベースも利用しない、表示するだけのページのコントローラの場合は、最低限の記述をすると以下のようになります。

<?php
/**
 * [Controller]index/TopController:トップページ表示処理コントローラ
 * トップページの表示を行う
 */
 class TopController extends BaseController{
	protected $param = []; //viewに渡すパラメータ
	protected $title = "トップ";

	function get($ret = []){
		$this->param['title'] = $this->title;
		return $this->param;
	}

	function post($ret = []){
		$this->param['title'] = $this->title;
		return $this->param;
	}
}

コントローラではget()メソッドもpost()メソッドも処理結果の情報をビューに渡したいときは$this->paramに情報を保管してreturnします。ビュー側では$paramとして受け取り、利用することができます。

get()メソッド
通常のURLリクエストが来た場合の処理を記述します。引数の$retにルーティングからリクエスト情報が渡ってきていますので、リクエストによりデータベースから情報を取得したい場合に利用します。

例:商品情報を取得したい場合
fwcore/route
'ItemInfo'=>[
    'view'=>'item/iteminfo',
    'controller'=>'item/ItemInfo',
        'set'=>[
            1=>'target',
        ],
],

fwcore/Controller/ItemInfoController
<?php
class ItemInfoController extends BaseController{
	protected $param = []; //viewに渡すパラメータ
	protected $title = "商品情報";

	function get($ret = []){
		if(Helper::ak("target",$ret["route"])){
			$this->param["iData"] = DbModel::read("Item")->getInfoByID($ret["route"]["target"]);
		}
		return $this->param;
	}

//~省略~

ここで「targetに指定した商品が存在しなかったケース」「そもそもtargetが設定されていなかったケース」については、ビュー側で$targetや$param["iData"]の内容を判断し、エラーページを表示する等の対応をします。
※取得できなかった時点でリダイレクトするという対応もできることはできますが、ビューに渡してから切り分けをすることで、本来のビューの役割を果たし、不正アクセスなどの調査にも役に立つため、ビュー側で行うことを推奨しております。

一覧ページなどはtarget関係なく一覧取得の処理を書きます。検索処理はPOST送信ですが、ページャーを利用した場合はGET送信となり、セッション情報より検索条件を取得するため、get()メソッドにも書きます。

※同梱のfwcore/Controller/IncludeControllerでは画像を表示する場合にget()メソッドを利用しています。

post()メソッド
ブラウザよりPOSTリクエストが送信された場合の処理を記述します。
具体的には以下のケースです。
・入力画面の値をデータベースに保存する処理
・検索条件をモデルに渡し、受け取る処理
・CSVやzip等をダウンロードする処理
・ajaxのPOSTリクエストを受け取る処理(fwcore/Controller/AjaxController参照)

例として「商品情報を保存する処理」は下記のように記述します。

fwcore/route.php
'ItemInfo'=>[
    'view'=>'item/iteminput',
    'controller'=>'indi/ItemInput',
        'set'=>[
            1=>'action',
            2=>'target',
        ],
],

fwcore/Controller/ItemInputController.php
	function post($ret = []){
		if(Helper::ak("action",$ret["route"]) && $ret["route"]["action"] == "Edit" && Helper::ak("target",$ret["route"])){
			$itemModel = DbModel::read("Item");
			$this->param["iData"] = $itemModel->getInfoByID($ret["route"]["target"]);
		}
		//入力チェック
		$err = Validation::validate(”ItemInput”);

		if(count($err) > 0){
			$this->param['error'] = $err;
			//入力エラーをログに保存
			$lor = [];
			foreach($err as $k=>$v){
				$lor[] = $k."->".join("/",$v);
			}
			Log::set("Input error:".join("",$lor));
		}else{
			//保存処理
			try{
				//トランザクション開始
				DbModel::Tran();

				switch($ret["route"]["action"]){
					case "Add":
						$nextid = $itemModel->createNextId();
						$add = ["item_id"=>$nextid];
						$res = $itemModel->insert(”ItemInput”,$add);
						break;
					case "Edit"
						$where= ["item_id"=>$ret["route"]["target"]];
						$res = $itemModel->update(”ItemInput”,$where);
						if(count($res) > 0){
							$res = true;
						}else{
							$res = false;
						}
						break;
				}
				if(!$res){
					//更新失敗
					DbModel::RollBack();//ロールバック
					Log::set("Update Error");
					$this->param['error'] = ["common"=>[Config::get("message","updateError")]];

				}else{
					DbModel::Commit();//コミット
					Log::set("Item {$ret["route"]["action"]} target:".$ret["route"]["target"]);
					Helper::redirect("ItemInput/Complete/".$ret["route"]["target"]); //完了画面に遷移
					return;
				}
			}catch (Exception $e){
				DbModel::RollBack();//ロールバック
				Log::set("DB Error:".$e->getMessage());
			}
		}
		return $this->param;
	}

※KoMA-PHPの推奨機能を利用して記述しています。不明な機能は当マニュアルかリファレンス を探してみてください
※オーソドックスな更新処理の記述例です。画像のアップロードだったり登録と更新でバリデーションをわけたり、ケースにより自由に記述できます。
※post()メソッドでもget()メソッドと同様にtargetによりデータを取得していますが、入力チェックでエラーがあった場合は、
 基本的には入力画面を再表示しますので、その際に必要な情報を取得するために記述しています。
※今回の例では「Add」で登録処理、「Edit」で更新処理とする想定のため「Edit」の場合のみデータを取得しています。
※ただし入力画面表示に入力フォームテンプレートをそのまま利用した場合、POST送信された入力値が優先されます。


その他
get()、post()の両メソッドは実装必須のメソッドでしたが、必要に応じてユーザの自作メソッドを実装することもできます。
その場合は、基本的には内部アクセスになると思いますので、privateメソッドとして実装します。

自作メソッドの例:
・ページのタイトルをリクエストにより分けるためのタイトルメソッド
・リクエストにより取得するデータが変わる場合のデータ取得メソッド
・共通バリデーションでは処理しきれない入力検証をする場合の個別バリデーションメソッド
・postメソッドが長くなるのを防ぐために登録用のメソッドを個別切り出し

※Ajaxについては全てユーザ個別メソッドとなっています。
ビュー
UI出力部分を担当するのがこのビューです。
単に「Hello world」したいだけであれば、コントローラ で述べた最低限の実装だけしたコントローラを準備し、ルーティングで設定した箇所にビューを設置し、
echo "Hello world";
とだけ書けば完了です。
しかし、KoMA-PHPはフレームワークです。様々な画面を表現するためのテンプレートを用意しています。同じ「Hello world」するにしてもせめてテンプレートを利用して表示しましょう。

・ルーティング
    'Hello'=>[
        'view'=>'hello',
        'controller'=>'Hello',
    ],
・コントローラ
fwcore/Controller/HelloController
(出力内容はビューのみで表現する最小限構成ため内容は省略)

・ビュー
fwcore/View/hello
<?php
$contents = "Hello world";
//テンプレートで出力
require_once __DIR__.'/../template/webtemplate.php';

そして、http://hogehoge/Helloにアクセスしてみましょう。

いかかでしょうか?インストールテンプレートのトップと同じデザインの画面の真ん中に「Hello world」と表示されたはずです。

以上で、ルーティング→コントローラー→ビューがつながりました。これでもうデータベースを介しない単純な画面であれば思うがままに作り進めることができます。

KoMA-PHPで用意されているテンプレートビューについて紹介します。

template/template
公開画面というよりも管理画面を想定としたテンプレートです。robotsもnoindex,nofollowと指定しています。
プログラムと表示画面との区別をあまり必要としない場合はこちらのテンプレートを利用すると良いと思います。必ずログインして利用するSNS,社内管理や業務管理を構築する場合はこちらの方が向いています。
ログインチェックをしてログイン状態の場合はログアウトボタンを表示するようになっています。同じif文の中に表示したい情報を盛り込むことでログイン時のみ表示する情報が利用できます(例えばログインユーザ名、会員専用メニューなど)

利用するには 表示したい内容を 変数 $contents に格納してrequireするだけです。

$contents = "hogehoge";
require_once __DIR__.'/../template/template.php';

template/webtemplate
公開画面を想定したテンプレートです。基本的な前半の作りはtemplateとあまり違いませんが、htmlを出力する際には .tplファイルを呼び出すようになっています。場所はfwcore/storage/template/配下です。
.tplの内容は変数を埋め込む{$fuga}が[fuga]に変わっただけですが、「この変数を埋め込むとこれが表示される」ということだけ共有しておけば、デザインとプログラムとの分離が可能となっています。
この構成はかの有名なテンプレートエンジン「Smarty 」を意識した作りとなっておりますが、Smartyと違い.tpl内でPHPロジックを記述することはできません。後のバージョンでSmarty対応をする予定ですのでもう少々お待ちください。

template/inputtemplate
入力フォームテンプレートです。
このtemplate/inputtemplateでは新規入力時の表示はもちろん編集時の初期値表示、入力エラー時の再表示・エラーメッセージの表示までサポートしています。
fwcore/Config/inputに設定をして入力フォームを設置したいビューでrequireするだけで入力フォーム用HTMLが取得できます。
取得したフォームは画面枠テンプレートに渡す$contentsに格納しましょう。
//fwcore/View/ItemInput
$btn=  Form::submit("post","更新",["class"=>"btn"]);
$conName = "ItemInput"; //
$inputForm = require_once __DIR__.DS.'..'.DS.'template'.DS.'inputtemplate.php';
$contents = <<<EOF
<form method="post" id="InputForm" action="{$_SERVER["REQUEST_URI"]}" enctype="multipart/form-data">
<span class="error">※</span>は必須
{$inputForm}
{$btn}
</form>
EOF;

//テンプレートで出力
require_once __DIR__.DS.'..'.DS.'template'.DS.'template.php';
この入力フォームを出力したい場合、fwcore/Config/inputでは"ItemInput"という名前で入力項目を設定していきます。

fwcore/Config/inputにより多種多様な入力フォームの表現が可能となっています。リファレンス に詳細を記載していますので、そちらをご確認ください。
もちろん、独自にパターンを追加することも可能です。

※サンプル記述でわかるように入力フォームテンプレートには<form>~</form>とsubmitボタンは含まれておりません。
サンプルのように呼び出し側のビューに記述するか、特殊な表現のないフォームであればtemplate/basicformtemplateを呼び出しておけば<form>~</form>とsubmitボタンとtemplate/inputtemplate込みで入力フォームが表示されます。

template/listtemplate
一覧表示用のテンプレートです。
画面表示初期に一覧を表示する、しない設定($initialDisplay:デフォルトはtrueで検索前提)やページャーの表示までサポートしています。
このtemplate/inputtemplateではコントローラで指定のパラメータ($param["list"])を渡し、fwcore/Config/listに設定をして一覧情報を設置したいビューでrequireするだけで一覧用HTMLが取得できます。
取得したフォームは画面枠テンプレートに渡す$contentsに格納しましょう。
//fwcore/View/ItemList
$conName = "ItemList";
$contents= require_once __DIR__.'/../template/listtemplate.php';

//テンプレートで出力
require_once __DIR__.'/../template/template.php';

ページャーを表示したい場合はルーティング の項を参照してください。
ページャーの1ページに表示する件数やページャーHTMLの区切りページ数についてはfwcore/Config/app.pagerおよびfwcore/Config/app.pagersplitを参照してください。

fwcore/Config/listにより多種多様な一覧の表現が可能となっています。リファレンス に詳細を記載していますので、そちらをご確認ください。
もちろん、独自にパターンを追加することも可能です。

template/searchtemplate
検索条件用のテンプレートです。
template/listtemplateで一覧を表示する際に情報が多い場合、ページャー機能により1ページの表示件数を少なくして表示の際の負荷を減らすことも大事ですが、
情報が多すぎる場合は何ページも進んでいかないと目的の情報にたどり着けない場合があります。
そんなときに検索条件を送信することによる絞込みを行うことで目的の情報にたどり着く時間を短縮することができます。
template/searchtemplateはtemplate/listtemplateと対で利用します。
fwcore/Config/searchに設定をして一覧情報を設置したいビューでrequireするだけで一覧用HTMLが取得できます。対となることがわかるようにfwcore/Config/listと同じ名前で設定することを推奨します。

$conName = "ItemList";
$initialDisplay = false; //初期表示しない
$contents = require_once __DIR__.'/template/searchtemplate.php';
$contents.= require_once __DIR__.'/template/listtemplate.php';

//テンプレートで出力
require_once __DIR__.'/template/template.php';

fwcore/Config/searchにより多種多様な検索条件の表現が可能となっています。リファレンス に詳細を記載していますので、そちらをご確認ください。
もちろん、独自にパターンを追加することも可能です。
入力が発生しますのでtemplate/inputtemplateと似通ったパターンを設定できます。
バリデーション
ユーザからの入力内容を保存したりメール送信したりする場合には、セキュリティ確保のため「ユーザからの入力は信用しない」ということを念頭に必ず入力内容の妥当性を検証しなければなりません。
必須項目を抜かしていないか、数字入力部分に数字以外が紛れ込んでいないか、必要以上たくさんの文字が打たれていないか・・・etc.
※もちろん凡例やヘルプ文言を用いてユーザに間違いなく入力してもらうように明記することも必要です

入力フォーム一体型
KoMA-PHPでは入力フォームと対になる形でバリデーションの設定を行うことができます。

設定箇所:
fwcore/Config/input

設定:
{フォーム名}=>[
    {コントロール名}=>[
        'type'=>{コントロールのタイプ},
        'name'=>{コントロール表示名},
        'validate'=>{バリデーション指定1}|{バリデーション指定2}|{バリデーション指定3}・・・,
      //省略
    ],
    {コントロール名}=>[
        'type'=>{コントロールのタイプ},
        'name'=>{コントロール表示名},
        'validate'=>{バリデーション指定1}|{バリデーション指定2}|{バリデーション指定3}・・・,
      //省略
    ],
    //省略
 ],

利用方法:
$err = Validation::validate({フォーム名});

上記のように入力コントロールの設定に「validation」項目を設け、「|」区切りでバリデーションパターンの指定をし、Controllerのpostでフォーム名を指定してバリデーションを呼び出すだけで入力コントロール内の必要なPOST値を取り出し、バリデーションを行います。
実行結果は配列でコントロール表示名とエラーメッセージが配列となって返されますので、count()関数などで入力エラー有無を確認し、次の処理を行うように記述します。
細かい指定内容とメッセージについてはリファレンス (InputConfigの項)に記述しています。

ビュー側でinputtemplateを呼び出している場合は、返却値を$this->param['error'] に格納するだけで、入力エラーのあった入力コントロールの出力箇所にエラーメッセージも一緒に表示するようになっています。
※バリデーションは一対型を利用し、inputtemplateを用いない特殊な入力フォームを独自で作成したい場合もあると思います。その場合は$param['error'] の内容を検証し、適切にエラーメッセージを配置させてください。

単発型
KoMA-PHPでは入力フォームと対になっていない、単体のバリデーションも可能です。
利用方法:
$err = Validation::single_validate({検証したい値},{バリデーション指定1}|{バリデーション指定2}|{バリデーション指定3}・・・,{接頭辞});
接頭辞には入力項目名などを設定すると「{接頭辞}必須です」というようなエラーメッセージが格納されます。自由度を高くするため「{接頭辞}は」というのを自動ではつけておりません。ご注意ください。
単発バリデータは一部を除いては一対型バリデータとパターンとチェック方法は同じです。違いは入力フォーム設定のPOST値から項目全てを検証するか、与えられた値のみを検証するか、です。
返却された配列の内容を検証し、適切にエラーメッセージを配置させてください。

その他
KoMA-PHPでは基本的なバリデーションパターンは用意しておりますが、複合的なバリデーションに対応しきれていない部分もあり、いずれも必要であれば自身でバリデーションパターンを作成することも可能です。
新しいパターンを作った場合、エラーメッセージはパターン名でfwcore/Config/messageに定義します。
モデル
コントローラなどからのリクエストを受けてデータベースへのアクセスを行います。
KoMA-PHPにおいては実際にSQLを発行しデータベースへのアクセスを行い結果を返すfwcore/Core/DbModelクラスと、入力内容を検証しデータベースに保管するための適切な型に変換するfwcore/Model/Modelクラスを用意しております。
コントローラなどから呼び出す場合はfwcore/Model/Modelクラスで必要なモデルを呼び出し、呼び出したモデルオブジェクトからinsert,update,delete,selectなどを実行する形となります。
データベースの各テーブルに対するモデルクラスはそれぞれ作る必要があり、fwcore/Model/Modelクラスを継承させます。
テーブル名($table)、キー名($key)はfwcore/Model/Modelクラスの親メソッドを実行する際には自動で呼び出されていることもあり、コンストラクタで必ず設定する必要があります。
※複数キーがあるテーブルの場合は利用の際に工夫が必要です。

モデルを呼び出す場合は従来のように直接newではなく、KoMA-PHPでは呼び出し用の記述方法があります。
DbModel::read({モデル名});
返却されるのは対象モデルのオブジェクトそのものです。
※DbModel::read()を利用することで親モデルも読み込みnewしますので、必ずDbModel::read()をご利用ください。

例として、商品(Item)テーブルを利用するためのモデルを作成します。
作成したモデルはfwcore/Model/配下に置き、ファイル名とクラス名を一致させます。
※下記コードはミニマム構成です。必要に応じてメソッドを作成、調整してください。
fwcore/Model/Item.php
<?php
/**
 * [Model]Item:商品モデル
 *
 * @link http://komaphp.k-akimoto-matsu.net/Manual#subtitle10 マニュアル@モデル
 */
class Item extends Model{
	/**
	 * コンストラクタ(tableとkeyの設定のため必須)
	 */
	public function __construct(){
		$this->table = "m_item";
		$this->key = "item_id";
	}
	/**
	 * IDからデータを取得
	 * @param unknown $id
	 * @return unknown[]
	 */
	public function getInfoByID($id = null){
		if(is_null($id)) return null;
		return $this->getByID($this->key,$id);
	}

	/**
	 * 検索条件、ソート順指定で一覧情報を取得
	 * @param array $w
	 * @param array $order
	 * @return unknown[]
	 */
	public function getList($w=[],$order = []){
		return $this->get($w,false,array_merge([$this->key=>0],$order));
	}

	/**
	 * 検索条件、ソート順指定で1件データを取得
	 * @param array $w
	 * @param array $order
	 * @return unknown[]
	 */
	public function getOneInfo($w=[],$order = []){
		return $this->get($w,true,$order);
	}

	/**
	 * 検索設定とソート指定で一覧情報を取得
	 * @param array $order
	 * @return unknown[]
	 */
	public function getListBySearch($order=[]){
		return $this->getBySearch("ItemList",$order);
	}
}

補足ですがgetListBySearch()メソッドは検索画面に表示する検索結果を取得するために実行し第1引数は初期オーダーとなります。
また、親クラスのgetBySearch()メソッドの第1引数はfwcore/Config/searchに設定した検索条件とリンクしています。

では、このItemモデルを利用して幾つか検索での呼び出し例を挙げます。
//商品詳細情報を取得したいとき
$itemInfo = DbModel::read("Item")->getInfoByID($target);  //$targetは商品コード

//ある条件から1件だけ情報を取得したいとき
$itemInfo = DbModel::read("Item")->getOneInfo($where,$order); 
//$whereは検索条件で項目名=>検索文字列の連想配列 $orderはソート条件で項目名=>昇降順(0,1)

検索(SELECT)についてはいずれのメソッドもin~やbetween、複数テーブルをまたぐ場合、JOIN、UNIONなどなど複雑な検索条件には対応しておりません。
複雑な検索条件を組みたい場合は、独自にSQLを作成し、DbModel::execute()を実行し、受け取った結果を適切にご利用ください。
※fwcore/Model/Modelクラスのデフォルトメソッドは全てエスケープ処理が自動で行われますが、独自にSQLを作成する場合はエスケープ処理は行われませんので、ユーザからの入力を検索値に含む場合はDbModel::es()にてエスケープを行うこと

登録処理、更新処理についてはコントローラ の項に記述があるとおりです。

それぞれのメソッドの役割や機能詳細についてはリファレンス に記載していますので、そちらをご確認ください。

※insert,update,delete,selectを実行した際に毎回DbModel::connect()して接続しているように見えますが、同一セッション内で一度接続に成功したコネクションは維持して再利用し、持続的データベース接続を採用しております。

※モデルについては最低限の役割を持たせる目的で作っているためプリペアドステートメントやストアドプロシージャは導入しておらず、SQL文字列を生成して実行している形をとっています。JOINなどを含めた複雑なSQLの対応と含めて今後の改修を検討中(2024年11月21日現在)です。
設定ファイル
KoMA-PHPでは様々な機能を設定で対応してます。もちろん、独自に設定ファイルを作成し、利用することもできます。fwcore/Config配下に置かれており、設定は全て連想配列で行います。

呼び出し方:
Config::get({Configファイル名(.phpなし)},{呼び出し設定.呼び出し設定.呼び出し設定・・・})

これだけだと少しわかりづらいかもしれませんが、呼び出すConfig名を.phpなしで指定し、中身の連想配列の添え字を「.」でつないで呼び出すようになっています。
例えばfwcore/Config/databaseより何か取り出してみましょう。

Config::get("database","db");
Config::get("database","postgres.dbname");

それぞれ現在の設定値が取り出せたはずです。
2つ目のように階層を追って出力することもできます。呼び出した添え字の内容が配列だった場合は配列が取得できます。
Config::get("type","yesno");
// ["0"=>"しない","1"=>"する"]

KoMA-PHPで標準搭載されている機能の多くはこのConfigから必要な情報を取り出して利用しています。
特に定数的な使い方をする設定についてはこのConfigを利用すると便利です。
コア提供機能
KoMA-PHPの根幹部分を支える様々な機能群です。fwcore/Core配下に置かれており、デフォルトで様々な機能を備えております。
もちろん、独自の機能を作成し利用することも可能です。呼び出し方は全て同じです。

呼び出し方:
{機能クラス名}::{呼び出しメソッド名}({引数});

しかしながらこの形で呼び出すために、新しい機能を作成した場合は下記ファイルに加える必要があります。
fwcore/Read/class.php
このファイルでは配列でCore機能名が羅列されておりますが、上から重要な順番(読み込み順)で並んでいますので、独自機能については適切な場所に挿入するようにお願いします。

デフォルトの機能について役割を簡単に説明しておきます。

Access
ユーザエージェントを解析する。
解析内容の設定はfwcore/Config/accesslistにあります。
このコア機能を利用して拡張することでデータベースにユーザアクセス情報を保存し、アクセス解析に利用することもできます。

Config
設定ファイルの設定取得する。
よく使う機能となりますので設定ファイル の項に使い方を記述しています。

DbModel
モデルクラスの読み込み、データベース接続、データベース処理を行う。
基本的にはDBコネクトしてSQLを実行するだけですが、whereやorderなど一部のオプションは整形も担当しています。

Form
入力コントロールのHTML生成を行う。
inputtemplate,listetemplate,searchtemplateなどからも呼び出されており、こちらもよく使う機能となります。
inputタグなどが直に書かれています。

Html
リンク生成などの簡易なHTML生成から、HTMLエスケープ、文字列置換(特にデータベースから取り出した文字列を表示用に置換するなど)を担当。

Input
POST値、FILES値を取得。

Log
アクセスログ出力 の項参照。

Mail
メール送信を行う。基本的な送信のみしか記述していないのでメールフォームからのメール送信や自動送信メールについては別途独自で拡張する必要があります。

Pager
ページャーHTMLを生成する。

Request
GET値、POST値を取得(GET優先)

Session
セッションの基本機能をラップ。

Url
REQUEST_URIを解析して配列化する。QueryStringは無視される。

Validation
バリデーション。詳しくはバリデーション の項を参照。

View
リクエストURLの解析をUrlクラスに投げ、ルーティング呼び出し、コントローラ、ビューの全てを司る。
管理画面機能を実装する場合等はこちらで公開モード、管理モードをわけて

Helper
個別機能にするほどではないが利用頻度が高い便利機能を搭載。
PHPの標準関数で長い名称の関数のラッパーとしても利用しています。


各詳細機能説明(メソッドの引数・返却値、利用ケースなど)についてはリファレンスのCoreの項 に記載しております。
アクセスログ出力
KoMA-PHPではテキストファイルでログ出力を行っています。
プログラム:fwcore/Core/Log
ログ出力場所:fwcore/storage/Log

何か問題が発生したとき、開発中に処理中の情報を確認したいときなど、デバッグ用としても利用できます。

呼び出し方:
Log::set({出力内容});

デフォルトでは管理画面など内部機能用にLog::set() , 公開画面など外部機能用にLog::webset()が用意されており、それぞれ同様の仕組みですが出力されるファイル名が違います。
Log::set() ・・・ aclog{yyyymmdd}.log
Log::webset() ・・・ aclog_web_{yyyymmdd}.log

ログイン機能を有している場合は、セッションなどを確認しログイン時はユーザIDなどを保管して原因解析に役立てることもできます。
Ajax
KoMA-PHPではもちろんAjaxについても実現が可能となっています。しかしながらフレームワークの規則に乗せて実装しなければなりません。
ここまでマニュアルを熟読された方ならわかると思います。そう、ルーティング・コントローラ・ビュー です。
もちろんAjaxの用途は様々で、POSTなどで直接PHPを実行せずともJavaScriptを介してPHPを実行してサーバを実行できることが大きなメリットであるため、
必要であればDBアクセスを司るモデル もこの仲間に入ることがあります。

フレームワークキットには既にAjax専用のルーティング・コントローラ・ビューが含まれており、サンプルコードが1つ登録されています。
つまり、基本はコントローラ(必要に応じてモデル)に任意のコードを追加し、そのコントローラを呼び出すJavaScript(と実行するビュー)さえ書けば実行が可能となっているのです。

サンプルコードを呼び出し、結果を受け取ってみましょう。

サンプルコード
	function k_ajax_test(){
		$t1 = Input::get("t1");
		$t2 = Input::get("t2");
		return ($t1+$t2);
	}

ビュー
※サンプルコードのためルーティング・コントローラの設定、入力チェック等は省略し、ビューにJavaScriptを直書きしています。
<?php
$form1 = Form::text("t1",null);
$form2 = Form::text("t2",null);
$btn = Form::button("add","足す!!");
$contents = <<<EOF
<p>{$form1}+{$form2}=<span id="result"></span></p>
{$btn}

<script>
$(function(){
	$("#add").click(function(e){
		var d = {"t1":$("#t1").val(),"t2":$("#t2").val()};
		$.ajax({
			type:"POST",
			url:"{$root}Ajax/k_ajax_test",
			data:d,
			success:function(data){
				$("#result").text(data);
			},
			error:function(XMLHttpRequest,textStatus,errorThrow){
				alert(XMLHttpRequest.responseText);
			}
		});
	});
});
</script>

EOF;

require_once __DIR__.'/../template/template.php';

テキストフォーム1とテキストフォーム2に入力した値をPHPで計算し、受け取った結果をHTMLに表示するコードです。
もちろん、単なる足し算であれば本来PHPを介す必要はなく、JavaScriptのみで完結できますが、これはあくまでKoMA-PHPでAjaxが動作する確認のためのサンプルコードです。
コントローラ(・モデル)・ビューを利用していますので、通常の画面表示と同じようにデータベースとのやりとりやPHPセッションの利用なども可能です。
Ajaxをご存知の方は適切な利用の仕方は熟知しているものと思いますので、あとは実装していくだけです。
一覧画面からの削除機能、セレクトボックスOnChangeで子要素情報を取得、画面POST以外での登録処理、Submit以外でフレームワークの機能を利用した入力チェックなどなど・・・
通常のAjax利用と同様に発想次第でどのようなシーンにも対応できます。


※注意事項:
大規模案件になってくるとこのAjaxControllerが肥大化しがちです。ビューはただ結果をechoしているだけなので目的やコード量によってコントローラを分割するなどしてプログラムコードのメンテナンス性を保てるように工夫してください。
サンプル
デフォルト搭載機能のみで作った入力画面、一覧画面のサンプルコードと出力結果です。
KoMA-PHPでの基本的な開発手順となりますので、プログラミングの参考にしてください。
※よく使いそうな機能を利用していますが、それぞれどのような機能なのか・その他の機能についてはリファレンス にて説明していますので、そちらをご参照ください

データベースのデータ(商品テーブル m_item)
商品コード 商品カテゴリ(コード) 商品名 商品紹介 値段 最終更新者(ログインID)
1 1 商品1 商品1商品商品商品商品商品商品商品商品商品 900 1
2 1 商品2 商品2商品商品商品商品商品商品商品商品商品 920 2
3 1 商品3 商品3商品商品商品商品商品商品商品商品商品 1200 2



以下省略

カテゴリ変換用Config
fwcore/Config/type
	"category"=>[
		"1"=>"紳士",
		"2"=>"婦人",
		"3"=>"子供",
		"4"=>"カジュアル",
		"5"=>"お買い得",
		"6"=>"期間限定",
	],

※ユーザテーブルは省略
 カテゴリもtype設定ではなくマスタテーブルにして利用することも可能です

モデル(Item)
fwcore/Model/Item
<?php
/**
 * [Model]Item:商品モデル
 *
 * @link http://komaphp.k-akimoto-matsu.net/Manual#subtitle10 マニュアル@モデル
 */
class Item extends Model{
	/**
	 * コンストラクタ(tableとkeyの設定のため必須)
	 */
	public function __construct(){
		$this->table = "m_item";
		$this->key = "item_id";
	}
	/**
	 * IDからデータを取得
	 * @param unknown $id
	 * @return unknown[]
	 */
	public function getInfoByID($id = null){
		if(is_null($id)) return null;
		return $this->getByID($this->key,$id);
	}

	/**
	 * 検索条件、ソート順指定で一覧情報を取得
	 * @param array $w
	 * @param array $order
	 * @return unknown[]
	 */
	public function getList($w=[],$order = []){
		return $this->get($w,false,array_merge([$this->key=>0],$order));
	}

	/**
	 * 検索条件、ソート順指定で1件データを取得
	 * @param array $w
	 * @param array $order
	 * @return unknown[]
	 */
	public function getOneInfo($w=[],$order = []){
		return $this->get($w,true,$order);
	}

	/**
	 * 検索設定とソート指定で一覧情報を取得
	 * @param array $order
	 * @return unknown[]
	 */
	public function getListBySearch($order=[]){
		return $this->getBySearch("ItemList",$order);
	}
}

ルーティング
fwcore/route
	'ItemList'=>[
		'view'=>'item/list',
		'controller'=>'item/List',
		'pager'=>true,
		'set'=>[
			1=>"page",
		],
	],
	'ItemInput'=>[
		'view'=>'item/input',
		'controller'=>'item/Input',
		'set'=>[
			1=>"action",
			2=>"target",
		],
	],

一覧画面のコントローラ
fwcore/Controller/item/ListController
<?php
/**
 * [Controller]item/ListController:商品一覧画面表示・検索処理コントローラ
 * 商品情報一覧画面の表示および検索処理を行う
 */
class ListController extends BaseController{
	protected $param = [];
	protected $title = '商品一覧';
	protected $order = ["update_date"=>0,"item_id"=>0];

	function get($ret = []){
		$this->param['title'] = $this->title;

		$rq = Request::getall();
		if(count($rq) > 0){
			$this->order = $rq;
		}
		$this->param["list"] = DbModel::read("Item")->getListBySearch($this->order);
		return $this->param;
	}

	function post($ret = []){
		$this->param['title'] = $this->title;
		Session::set("ItemList",Input::all());
		$rq = Request::getall();
		if(count($rq) > 0){
			$this->order = $rq;
		}
		$this->param["list"] = DbModel::read("Item")->getListBySearch($this->order);
		return $this->param;
	}
}

一覧画面のビュー
fwcore/View/item/list
<?php
$contents = null;

$conName = "ItemList";
$contents.= require_once __DIR__.'/../template/searchtemplate.php';
$contents.= Form::button("add","新規追加",["onclick"=>"location.href='{$root}ItemInput/Add';"]);
$contents.= require_once __DIR__.'/../template/listtemplate.php';
$back = null;

//テンプレートで出力
require_once __DIR__.'/../template/webtemplate.php';

一覧の表示設定
fwcore/Config/list
	"ItemList"=>[
		"item_id"=>[
			"label"=>"商品コード",
			"type"=>"label",
		],
		"category"=>[
			"label"=>"商品カテゴリー",
			"type"=>"select",
			"sort"=>true,
			"select"=>"category"
		],
		"name"=>[
			"label"=>"商品名",
			"type"=>"label",
		],
		"price"=>[
			"label"=>"値段",
			"type"=>"label",
			"sort"=>true,
		],
		"update_user"=>[
			"label"=>"最終更新者",
			"type"=>"label",
			"sort"=>true,
		],
		"update_user"=>[
			"label"=>"最終更新者",
			"type"=>"db",
			"select"=>"User-name",
		],
		"update_date"=>[
			"label"=>"最終更新日時",
			"type"=>"label",
			"format"=>"Y/m/d H:i:s",
			"sort"=>true,
		),
		"Update"=>array(
			"label"=>"?",
			"type"=>"button",
			"name"=>"編集",
			"action"=>"link",
			"link"=>"ItemInput/Edit/#item_id#",
		),
	),

検索の入力項目設定
fwcore/Config/search
	"ItemList"=>array(
		"item_id"=>array(
			"type"=>"text",
			"name"=>"item_id",
			"label"=>"商品コード",
			"setting"=>array(),
			"stype"=>0,
		),
		"category"=>array(
			"type"=>"select",
			"name"=>"category",
			"select"=>"category",
			"label"=>"商品コード",
			"setting"=>array(),
			"stype"=>0,
		),
		"user_name"=>array(
			"type"=>"text",
			"name"=>"name",
			"label"=>"商品名",
			"setting"=>array(),
			"stype"=>1,
		),
		"user_id"=>array(
			"type"=>"select",
			"name"=>"update_user",
			"label"=>"最終更新者",
			"select"=>"DB:User:user_id=name",
			"setting"=>array(),
			"stype"=>1,
		),
	),

一覧画面表示

現在までの手順で、一覧の表示、検索、並べ替え、新規作成画面への遷移・既存データ編集画面への遷移、までできあがりです。


入力画面のコントローラ
fwcore/Controller/item/InputController
/**
 * [Controller]item/InputController:商品入力画面表示・登録処理コントローラ
 * 商品情報入力画面の表示および登録処理(Insert/Update)を行う
 */
class InputController extends BaseController{
	protected $param = []; //viewに渡すパラメータ
	protected $title = '商品編集';

	function get($ret = array()){
		//編集モードはデータを取得する
		if(Helper::ak("action",$ret["route"]) && $ret["route"]["action"] == "Edit" && Helper::ak("target",$ret["route"])){
			$modl = DbModel::read("Item");
			$this->param["iData"] = $modl->getInfoByID($ret["route"]["target"]);
		}
		$this->titler($ret["route"]["action"]));

		$this->param['title'] = $this->title;
		return $this->param;
	}

	function post($ret = array()){
		$modl = DbModel::read("Item");

		//編集モードはデータを取得する
		if(Helper::ak("action",$ret["route"]) && $ret["route"]["action"] == "Edit" && Helper::ak("target",$ret["route"])){
			$this->param["iData"] = $modl->getInfoByID($ret["route"]["target"]);
		}

		switch($ret["route"]["action"]){
			case "Add":
				$err = Validation::validate("ItemInput");
				break;
			case "Edit":
				$err = Validation::validate("ItemEdit");
				break;
		}

		$tkerr = Validation::single_validate(Input::get("tk"),"token");

		if(count($tkerr) > 0){
			$err["common"] = $tkerr;
		}
		if(count($err) > 0){
			$this->param['error'] = $err;
			//入力エラーをログに保存
			$lor = array();
			foreach($err as $k=>$v){
				$lor[] = $k."->".join("/",$v);
			}
			Log::set("Input error:\n".join("\n",$lor));

		}else{

			try{
				//トランザクション開始
				DbModel::Tran();

				$user = Session::get("LoginUserInfo");
				$add = array("user_id"=>$user["user_id"]);
				//エラーなければ更新処理
				$res = false;
				switch($ret["route"]["action"]){
					case "Add":
						$res = $modl->insert("ItemInput",$add);
						break;
					case "Edit":
						$where = array(
							"item_id"=>$ret["route"]["target"]
						);
						$res = $modl->update("ItemEdit",$where,$add);

						break;
				}
				if(!$res){
					//更新失敗
					DbModel::RollBack();//ロールバック
					Log::set("Update Error");
					$this->param['error'] = array("common"=>array(Config::get("message","updateError")));

				}else{
					DbModel::Commit();//コミット

					Log::set("Item {$ret["route"]["action"]} target:{$ret["route"]["target"]}");
					//完了画面へ遷移
					Helper::redirect("ItemInput/Complete/{$ret["route"]["target"]}");
					return;
				}

			}catch (Exception $e){
				DbModel::RollBack();//ロールバック
				Log::set("DB Error:".$e->getMessage());
			}
		}
		$this->param['title'] = $this->title;
		return $this->param;
	}

	//actionにより画面タイトルを切り替える
	function titler($action=null){
		switch($action){
			//新規登録用
			case "Add":
				$this->title = "商品新規登録";
				break;
			//編集用
			case "Edit":
				$this->title = "商品編集(".Html::hsp($this->param["iData"]["item_name"]).")";
				break;
		}
	}
}

入力画面のビュー
fwcore/View/item/input
<?php
$contents = null;
$conName = "ItemInput";
$back = "ItemList";

//完了時は一覧へ
if($action == "Complete"){

	$contents = "<p>商品情報の更新が完了しました。</p>";
	$contents .= Html::link($back,'一覧へ',["title"=>"一覧へ","id"=>"back","class"=>""]);

	//テンプレートで出力
require_once __DIR__.'/../template/webtemplate.php';
//編集時はデータを取得
}elseif($action == "Edit"){
	$conName = "ItemEdit";
	$iData = $param["iData"];
	if(count($iData) == 0){
		$contents = "<p>".Config::get("message","notarget")."</p>\n";
		$contents .= Html::link($back,'一覧へ',["title"=>"一覧へ","id"=>"back","class"=>""]);
		$back = "BlogList";
		//テンプレートで出力
		require_once __DIR__.'/../template/webtemplate.php';
	}
}

$btnname = "更新";
$inputCss= "dInput";
$contents .= require_once __DIR__.'/../template/basicformtemplate.php';

//テンプレートで出力
require_once __DIR__.'/../template/webtemplate.php';

入力画面の項目設定
fwcore/Config/input
	"ItemInput"=>[
		"item_id"=>[
			"type"=>"text",
			"name"=>"item_id",
			"label"=>"商品コード",
			"validate"=>"requierd|number|overlap-Item",
			"setting"=>[],
			"help"=>"半角数字のみ。重複不可",
			"requierd"=>true,
		],
		"category"=>[
			"type"=>"radio",
			"name"=>"category",
			"label"=>"商品カテゴリー",
			"validate"=>"requierd",
			"setting"=>[],
			"select"=>"category",
			"requierd"=>true,
		],
		"name"=>[
			"type"=>"text",
			"name"=>"name",
			"label"=>"商品名",
			"setting"=>["style"=>"width:200px;"],
			"validate"=>"requierd|max-50",
			"requierd"=>true,
			"help"=>"最大50字",
		],
		"price"=>[
			"type"=>"text",
			"name"=>"price",
			"label"=>"値段",
			"setting"=>[],
			"validate"=>"requierd|number",
			"requierd"=>true,
			"help"=>"円",
		],
		"intro"=>[
			"type"=>"textarea",
			"name"=>"intro",
			"label"=>"商品紹介",
			"setting"=>["cols"=>"50","rows"=>"10"],
			"help"=>"HTMLは無効",
		],
		"tk"=>[
			"type"=>"hidden",
			"name"=>"tk",
			"label"=>"",
			"setting"=>[],
			"help"=>"",
			"requierd"=>true,
			"fxvalue"=>Session::createToken("tk"),//hiddenなので単発バリデータでチェック
		],
	],
	"ItemEdit"=>[
		"item_id"=>[
			"type"=>"label",
			"name"=>"item_id",
			"label"=>"商品コード",
			"setting"=>[],
			"help"=>"変更不可",
		],
		"category"=>[
			"type"=>"radio",
			"name"=>"category",
			"label"=>"商品カテゴリー",
			"validate"=>"requierd",
			"setting"=>[],
			"select"=>"category",
			"requierd"=>true,
		],
		"name"=>[
			"type"=>"text",
			"name"=>"name",
			"label"=>"商品名",
			"setting"=>["style"=>"width:200px;"],
			"validate"=>"requierd|max-50",
			"requierd"=>true,
			"help"=>"最大50字",
		],
		"price"=>[
			"type"=>"text",
			"name"=>"price",
			"label"=>"値段",
			"setting"=>[],
			"validate"=>"requierd|number",
			"requierd"=>true,
			"help"=>"円",
		],
		"intro"=>[
			"type"=>"textarea",
			"name"=>"intro",
			"label"=>"商品紹介",
			"setting"=>["cols"=>"50","rows"=>"10"],
			"help"=>"HTMLは無効",
		],
		"tk"=>[
			"type"=>"hidden",
			"name"=>"tk",
			"label"=>"",
			"setting"=>[],
			"help"=>"",
			"requierd"=>true,
			"fxvalue"=>Session::createToken("tk"),//hiddenなので単発バリデータでチェック
		],
	],


入力画面の表示

新規登録画面

新規登録画面でのバリデーション結果

既存データの編集画面

完了画面


設定により、「新規登録画面では重複する商品コードの登録はできないが、既存データの編集画面では商品コード編集不可としそのまま更新する」という機能を実現しています。
もちろん、商品コードを編集しなければならないこともあるので、「商品コードを編集可能とした上でコードに変更があった場合は商品コードの重複チェックを行い、変更がなかった場合は重複チェックを行わない」といったことも実現可能です。
登録時から商品コードを入力不可とし、プログラムで払い出すような仕組みも構築可能です。その際はModelにCreateCodeのような機能を儲け、商品コードを生成する処理を書き、呼び出してAddの際に付け加える$addに設定すると良いでしょう。
そこは作り方次第です。
また編集画面遷移時に存在しない商品コードを設定されたときなどの対応は今回は省略していますが、本来は不正アクセスとみなしエラーページを表示する対応を入れます。

今回紹介したサンプルコードはKoMA-PHPのデフォルト状態のフレームワークで実現しているので、サンプルコードを流用することで同様の機能がすぐに実装できます。

ダウンロード から取得できるフレームワークキットは最低限の機能で提供させていただいているフレームワークのためデザインについては制作者様の方で対応いただくことになります。
※今回のサンプルは少しだけCSSを適用し見栄え部分を調整していますが、フレームワークキットにはほとんど提供されていません。


ログイン処理サンプル
フレームワークキット内にfwcore/Controller/auth/LoginController、fwcore/Controller/auth/LogoutControllerを置いています。
最もベターなログイン認証処理を記述したものとなります。

ただし、Userテーブルを用意する必要がありますし、ルーティングはありません。認証後の画面用振り分けの機能はありますが(fwcore/Core/View)、認証後の画面自体の機能はほぼないといっても良いです。
※ビューはfwcore/View/auth/login
ざっくりと説明しますと、下記のように実装します。

・fwcore/Config/app.adminrootの設定を確認(要件により変更すること)
・fwcore/Config/app.authの設定を確認(要件により変更すること)
・fwcore/Config/app.no_loginにログインなしで利用する機能のルート名を記述する
・fwcore/Core/Viewの30行目付近に$mode にfwcore/Config/app.authを代入する処理を記述
例:
		//admin用
		if(count($url) > 0 && $url[0] == str_replace("/","",Config::get("app","adminroot"))){
			$mode = onfig::get("app","auth");
			$route = "Login";
			if(count($url) > 1){
				$u = [];
				$t = 0;
				foreach($url as $k=>$v){
					if($k == 0) continue;
					$u[$t] = Html::hsp($url[$k]);
					$t++;
				}
				$url = $u;
				$route = $u[0];
			}
			if(count($url) > 0 && $url[0] == "Login"){
				$route = "Login";
			}
		}
・これによりadminrootにアクセスがあった場合、adminroot以下を機能のルートとして認識できます。
 例:/admin/Login → Loginをルーティングから探す
・つまり、adminrootと同名のルーティングを設定してはいけない
・公開画面のアクセス情報をとりたいときにはビューのrequire_onceの前に$mode=="web"のときに(GETのみとかno_loginは除くとか色々条件を入れた上で)アクセスログをとるように条件を加える

この辺りは後日リリース予定のCMSには導入されているので、参考にしてみてもいいかもしれません。