このフレームワークは処理をMVC(Model-View-Controller)に分割して管理します。
モデルはデータを扱う部品です。データの検索、変換、検証、登録などを行います。
ビューはデータの表示を担当します。主にHTMLのテンプレートを扱います。
コントローラはユーザからのリクエストを扱います。モデルとビューの助けを借りてユーザに結果を返します。
public_html / config.php ... 設定ファイル
| index.php ... 起動ファイル
|
+-- app / ... アプリケーション格納ディレクトリ
| |
| +-- controllers / ... コントローラ格納ディレクトリ
| |
| +-- models / ... モデル格納ディレクトリ
| |
| +-- views / ... ビュー格納ディレクトリ
|
+-- libs /
|
+-- cores / ... コアライブラリ格納ディレクトリ
|
+-- plugins / ... プラグイン格納ディレクトリ
app/
内に作成したいアプリケーションのファイルを格納します。モデルは app/models/
内に、ビューは app/views/
内に、コントローラは app/controllers/
内にそれぞれ格納します。
libs/cores/
内にフレームワークの動作に必須の命令(コアライブラリ)が格納されています。この内容は編集する必要はありません。
libs/plugins/
内にフレームワーク内部からは直接呼び出されていない命令(プラグイン)が格納されています。自分で作成した共通関数のファイルもここに格納できます。
大まかな処理の流れは以下のとおりです。(具体例はブログチュートリアルを参考にしてください。)
http://www.example.com/index.php/test1/test2
へリクエストが送られたとします。app/controllers/test1/test2.php
が読み込まれます。$view
に値を割り当てます。app/views/test1/test2.php
が読み込まれます。$view
の内容を表示します。フレームワークを呼び出すURLは通常 http://www.example.com/index.php/test1/test2
のような形式ですが、mod_rewrite
を使用すれば http://www.example.com/test1/test2
のような形式にすることができます。
mod_rewrite
を使用する場合、index.php
と同じディレクトリ内に .htaccess
ファイルを作成し、以下の内容を記載します。
DirectoryIndex index.php
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) index.php/$1
</IfModule>
さらに、config.php
を修正します。
define('MAIN_FILE', $_SERVER['SCRIPT_NAME']);
この部分に、設置ディレクトリのパスを指定します。例えば http://www.example.com/test/sample/
に設置する場合、以下のように修正します。(最後の /
は不要です。)
define('MAIN_FILE', '/test/sample');
mod_rewrite
を使用する場合で公開ディレクトリ直下に設置する場合、以下のように修正します。
define('MAIN_FILE', '');
コントローラは app/controllers/
内に格納します。
URLで指定された2つの値に従ってコントローラが読み込まれます。(3つ目以降の値を指定しても、コントローラの呼び出しに影響しません。)値が省略されると home
と index
が省略されたものとみなされます。
http://www.example.com/index.php/test1/test2
へリクエストを送ると、app/controllers/test1/test2.php
が読み込まれます。http://www.example.com/index.php/test1
へリクエストを送ると http://www.example.com/index.php/test1/index
へのリクエストとみなされ、app/controllers/test1/index.php
が読み込まれます。http://www.example.com/
へリクエストを送ると http://www.example.com/index.php/home/index
へのリクエストとみなされ、app/controllers/home/index.php
が読み込まれます。app/controllers/before.php
があるとコントローラの実行前に、app/controllers/after.php
があるとコントローラの実行後にそれぞれ自動で読み込まれます。ここに、すべてのコントローラで共通して実行したい処理を書いておくことができます。
また、/test1
や /test1/test2
へのリクエストの場合、app/controllers/before_test1.php
があると before.php
の実行直後に、app/controllers/after_test1.php
があると after.php
の実行直前にそれぞれ自動で読み込まれます。(Ver6.8以降。)
コントローラ内で連想配列 $view
に値を格納すると、ビューから参照することができます。
モデルは app/models/
内に格納します。
データベースのテーブル名とモデルのファイル名は一致させ、原則として1テーブルにつき1モデルを作成します。名前は複数形を推奨します。
例えば members
テーブルのモデルは app/models/members.php
となります。ファイルを作成することにより、select_members()
・insert_members()
・update_members()
・delete_members()
などの命令が使えるようになります。これらはモデル内に以下のような関数を定義することにより、各機能を上書きすることができます。
//データを取得する命令
function select_members($queries)
{
$queries = db_placeholder($queries);
$queries['from'] = DATABASE_PREFIX . 'members';
$results = db_select($queries);
return $results;
}
//データを登録する命令
function insert_members($queries)
{
$queries = db_placeholder($queries);
$queries['insert_into'] = DATABASE_PREFIX . 'members';
$resource = db_insert($queries);
return $resource;
}
//データを編集する命令
function update_members($queries)
{
$queries = db_placeholder($queries);
$queries['update'] = DATABASE_PREFIX . 'members';
$resource = db_update($queries);
return $resource;
}
//データを削除する命令
function delete_members($queries)
{
$queries = db_placeholder($queries);
$queries['delete_from'] = DATABASE_PREFIX . 'members';
$resource = db_delete($queries);
return $resource;
}
//データを整理する命令
function normalize_members($queries)
{
return $queries;
}
//データを検証する命令
function validate_members($queries)
{
return array();
}
テーブルに対応する命令(テーブルのデータの初期値を定義する命令など)を追加する場合、モデル内に追加します。その際、命令の名前はモデル名を接尾語にします。具体的には default_members()
のような名前にします。
単体テストなどに支障をきたすため、モデル内でトランザクションは呼び出さず、コントローラ側で制御することを推奨します。
多機能なアプリケーションの作成において、モデルの処理をまとめて1つの命令にして使いまわしたい場合、サービスとして定義します。
ビューは app/views/
内に格納します。
URLで指定された2つの値に従ってコントローラが読み込まれます。値が省略されると home
と index
が省略されたものとみなされます。
http://www.example.com/index.php/test1/test2
へリクエストを送ると、app/views/test1/test2.php
が読み込まれます。http://www.example.com/index.php/test1
へリクエストを送ると http://www.example.com/index.php/test1/index
へのリクエストとみなされ、app/views/test1/index.php
が読み込まれます。http://www.example.com/
へリクエストを送ると http://www.example.com/index.php/home/index
へのリクエストとみなされ、app/views/home/index.php
が読み込まれます。コントローラ内で view('test1/test2.php');
のように書くと、上のルールを無視して任意のビューを読み込めます。またその際、$contents = view('test1/test2.php', true);
のように書くと結果を直接出力せずに取得できます。
コントローラ内で連想配列 $view
に値を格納すると、ビューから参照することができます。
ページが見つからない場合は404エラー画面が表示されますが、app/views/404.php
があると404エラー画面のビューとして使われます。
フレームワークの動作に必須の命令ですが、自分で書くプログラムに流用することもできます。
コアライブラリによって提供される命令の一部をフレームワークから提供される命令で紹介しています。
フレームワーク内部からは直接呼び出されていない命令です。プラグインは libs/plugins/
内に格納されています。
はじめから用意されているプラグインによって提供される命令をフレームワークから提供されるプラグインで紹介しています。
プラグインは自分で追加することもできます。プラグインは関数をまとめただけのファイルなので特別な規約はありません。ですが、ファイル名はその機能を端的にあらわす名前にし、関数名はファイル名をプレフィックスとすることを推奨します。(file.php
に file_info()
と file_mimetype()
を実装するなど。)
この機能はVer6.4以降で対応しています。
app/
内に services
ディレクトリを作成すると、サービス用のディレクトリと認識されます。このディレクトリ内に置いたPHPファイルは自動で読み込まれるようになります。
levisにおけるサービスとは、「MVCに記述していた共通処理を何らかの観点でまとめたもので、他案件での使い回しが難しいもの」としています。(他案件でも使いまわせるものは、プラグインにすることを推奨します。)
処理をまとめることにより単体テストを容易にする、という側面もあります。
サービスは関数をまとめただけのファイルなので特別な規約はありません。ですが、ファイル名はその機能を端的にあらわす名前にし、関数名は service_
とファイル名をプレフィックスとすることを推奨します。(news.php
に service_news_recent_articles()
と service_news_recent_count()
を実装するなど。)
単体テストなどに支障をきたすため、サービス内でトランザクションは呼び出さず、コントローラ側で制御することを推奨します。
モデル内の命令として実装するかサービスとして実装するか…の基準ですが、モデルは
select_xxx()
や insert_xxx()
を使わない範囲の、基本的なデータ操作を行う。(無限ループ回避のためにも、自身の select_xxx()
などはモデル内で呼ばない。)という思想で作っており、サービスは上の範疇を超えるものという考えで作成しています。
外部のライブラリを使用したり独自に作成した汎用クラスを使用する場合、libs/vendors/
を作成してその中に格納することを推奨します。制限は無いので、フレームワークとまったく関係のないディレクトリに置いても何も問題はありません。
libs/vendors/
内のファイルを呼び出す場合、require_once()
や import()
で読み込む必要があります。自動で読み込まれたりはしません。
外部のライブラリを使用する場合、プラグインやサービスとしてラッパー関数を作ると使い方を統一することができます。これも制限は無いので、ライブラリを直接呼び出しても何も問題はありません。
http://www.example.com/index.php/?mode=info_levis
へアクセスすると、フレームワークの情報が表示されます。表示されない場合、config.php
の以下の部分を 1
か 2
に設定してください。(本番環境では 0
にしておくことを推奨します。)
define('DEBUG_LEVEL', 0);
config.php
にある、以下の部分でデータベースの接続設定を行います。データベースを使用しない場合、設定の必要はありません。
define('DATABASE_TYPE', '');
define('DATABASE_HOST', '');
define('DATABASE_PORT', '');
define('DATABASE_USERNAME', '');
define('DATABASE_PASSWORD', '');
define('DATABASE_NAME', '');
define('DATABASE_PREFIX', '');
上から「接続方法」「ホスト」「ポート番号」「ユーザー名」「パスワード」「データベース名」「テーブル名のプレフィックス」です。
SQLiteを使用する場合、「データベース名」はデータベースファイルへのパスを設定します。例えば DATABASE_NAME
を db/levis.db
と設定した場合、index.php
と階層に db
ディレクトリを作成してパーミッションを 707
に設定し、さらにその中に levis.db
を作成してパーミッションを 606
に設定します。
PDOでMySQLへ接続する場合、一例ですが以下のように設定します。
define('DATABASE_TYPE', 'pdo_mysql');
define('DATABASE_HOST', 'localhost');
define('DATABASE_PORT', '');
define('DATABASE_USERNAME', 'root');
define('DATABASE_PASSWORD', '1234');
define('DATABASE_NAME', 'levis');
define('DATABASE_PREFIX', '');
PDOでPostgreSQLへ接続する場合、一例ですが以下のように設定します。
define('DATABASE_TYPE', 'pdo_pgsql');
define('DATABASE_HOST', 'localhost');
define('DATABASE_PORT', '');
define('DATABASE_USERNAME', 'root');
define('DATABASE_PASSWORD', '1234');
define('DATABASE_NAME', 'levis');
define('DATABASE_PREFIX', '');
PDOでSQLite3へ接続する場合、一例ですが以下のように設定します。
define('DATABASE_TYPE', 'pdo_sqlite');
define('DATABASE_HOST', '');
define('DATABASE_PORT', '');
define('DATABASE_USERNAME', '');
define('DATABASE_PASSWORD', '');
define('DATABASE_NAME', 'db/levis.db');
define('DATABASE_PREFIX', '');
PDOでSQLite2へ接続する場合、一例ですが以下のように設定します。
define('DATABASE_TYPE', 'pdo_sqlite2');
define('DATABASE_HOST', '');
define('DATABASE_PORT', '');
define('DATABASE_USERNAME', '');
define('DATABASE_PASSWORD', '');
define('DATABASE_NAME', 'db/levis.db');
define('DATABASE_PREFIX', '');
mysql関数で接続する場合、一例ですが以下のように設定します。
define('DATABASE_TYPE', 'mysql');
define('DATABASE_HOST', 'localhost');
define('DATABASE_PORT', '');
define('DATABASE_USERNAME', 'root');
define('DATABASE_PASSWORD', '1234');
define('DATABASE_NAME', 'levis');
define('DATABASE_PREFIX', '');
pg関数で接続する場合、一例ですが以下のように設定します。
define('DATABASE_TYPE', 'pgsql');
define('DATABASE_HOST', 'localhost');
define('DATABASE_PORT', '');
define('DATABASE_USERNAME', 'root');
define('DATABASE_PASSWORD', '1234');
define('DATABASE_NAME', 'levis');
define('DATABASE_PREFIX', '');
sqlite関数で接続する場合、一例ですが以下のように設定します。
define('DATABASE_TYPE', 'sqlite');
define('DATABASE_HOST', '');
define('DATABASE_PORT', '');
define('DATABASE_USERNAME', '');
define('DATABASE_PASSWORD', '');
define('DATABASE_NAME', 'db/levis.db');
define('DATABASE_PREFIX', '');
config.php
にある、以下の部分でデータベースの文字コード設定を行います。
MySQL使用時に DATABASE_CHARSET
を指定すると、「'SET NAMES ' . DATABASE_CHARSET
」でデータベースの文字コードが設定されます。
データベースへ入力する際、DATABASE_CHARSET_INPUT_FROM
から DATABASE_CHARSET_INPUT_TO
に文字コードが変換されます。同じ値が指定されている場合、何も行われません。
データベースから出力する際、DATABASE_CHARSET_OUTPUT_FROM
から DATABASE_CHARSET_OUTPUT_TO
に文字コードが変換されます。同じ値が指定されている場合、何も行われません。
define('DATABASE_CHARSET', 'UTF8');
define('DATABASE_CHARSET_INPUT_FROM', 'UTF-8');
define('DATABASE_CHARSET_INPUT_TO', 'UTF-8');
define('DATABASE_CHARSET_OUTPUT_FROM', 'UTF-8');
define('DATABASE_CHARSET_OUTPUT_TO', 'UTF-8');
データベースの文字コードが SJIS
で表示の際に EUC-JP
を使う場合、一例ですが以下のように指定します。
define('DATABASE_CHARSET', 'SJIS');
define('DATABASE_CHARSET_INPUT_FROM', 'auto');
define('DATABASE_CHARSET_INPUT_TO', 'SJIS');
define('DATABASE_CHARSET_OUTPUT_FROM', 'auto');
define('DATABASE_CHARSET_OUTPUT_TO', 'EUC-JP');
http://www.example.com/index.php/?mode=db_admin
へアクセスすると、データベースの管理画面が表示されます(データベース使用時のみ)。表示されない場合、config.php
の以下の部分を 1
か 2
に設定してください。(本番環境では 0
にしておくことを推奨します。)
define('DEBUG_LEVEL', 0);
GETリクエストとPOSTリクエストは、通常のPHPプログラムと同様に $_GET
と $_POST
で取得します。$_REQUEST
はフレームワーク側で扱う特別なリクエスト以外は受け付けないようになっているため、基本的には使えません。(このフレームワークに限らず、意図しない値を受け取らないためにも $_REQUEST
ではなく $_GET
と $_POST
の利用が推奨されます。)
index.php/test1/test2/test3
のようなリクエストの場合、「test1」「test2」「test3」の値はコントローラ内では $params[0]
・$params[1]
・$params[2]
でそれぞれ取得できます。
グローバル変数を扱いたい場合、PHPの文法通り $GROBALS
を利用できます。ただし $GROBALS['core']
はフレームワーク内部で使用しているので、この値を不用意に上書きしたりしないように注意してください。
セッションは常に開始されているので、通常のPHPプログラムと同様に $_SESSION
でセッションを読み書きします。ただし $_SESSION['core']
はフレームワーク内部で使用しているので、この値を上書きしたりしないように注意してください。
セッションの開始を自分で制御したい場合、config.php
で以下の設定を false
にしてください。
define('SESSION_AUTOSTART', true);
この場合、セッションは以下のコードで開始できます。
session()
config.php
の以下の部分を 1
に設定すると、エラー発生時に詳細が表示されます。また、データベース管理ツールなども利用できるようになります。2
に設定すると、さらに実行したSQLがその都度画面に表示されるようになります。
define('DEBUG_LEVEL', 0);
DEBUG_LEVEL
が 0
に設定されている場合でも、以下の部分にパスワードを指定しておくとデータベース管理ツールなどへのアクセスが許可されます。(この機能はVer7以降で対応しています。)
define('DEBUG_PASSWORD', '');
例えば以下のように設定すると、データベース管理ツールなどへアクセスする際にパスワードが求められます。
define('DEBUG_PASSWORD', '1234');
また、以下の部分にIPアドレスを指定しておくとデータベース管理ツールなどへのアクセスが許可されます。(この機能はVer7以降で挙動が変更されています。)
define('DEBUG_ADDR', '');
例えば以下のように設定すると、IPアドレス 127.0.0.1
からアクセスした場合のみ、データベース管理ツールなどにアクセスできます。
define('DEBUG_ADDR', '127.0.0.1');
IPアドレスは ,
で区切って複数指定できます。
define('DEBUG_ADDR', '127.0.0.1,127.0.0.2,127.0.0.3');
この機能はVer7以降で対応しています。
特定のタイミングでログを自動記録するように設定できます。ログの記録場所は config.php
の以下の部分で設定します。
define('LOGGING_PATH', 'logs/');
以下の部分を true
に設定すると、システムエラー(必要なファイルを読み込めなかった、データベースにアクセスできなかった、など)がログに記録されます。記録する場合、LOGGING_PATH
で指定したディレクトリ内に message
ディレクトリを作成し、書き込み権限を与えておきます。
define('LOGGING_MESSAGE', false);
以下の部分を true
に設定すると、GETアクセスがログに記録されます。記録する場合、LOGGING_PATH
で指定したディレクトリ内に get
ディレクトリを作成し、書き込み権限を与えておきます。
define('LOGGING_GET', false);
以下の部分を true
に設定すると、POSTアクセスがログに記録されます。記録する場合、LOGGING_PATH
で指定したディレクトリ内に post
ディレクトリを作成し、書き込み権限を与えておきます。
define('LOGGING_POST', false);
以下の部分を true
に設定すると、ファイルアップロードがログに記録されます。記録する場合、LOGGING_PATH
で指定したディレクトリ内に files
ディレクトリを作成し、書き込み権限を与えておきます。
define('LOGGING_FILES', false);
簡易な記事管理システムを例に、プログラムの作成手順を紹介します。ここでは、PHP5+MySQLで作成するものとします。また、作成するファイルの文字コードはすべてUTF-8Nとします。
任意のディレクトリにフレームワークを配置します。
config.php
を編集します。設定内容は環境に合わせます。
define('DATABASE_TYPE', 'pdo_mysql');
define('DATABASE_HOST', 'localhost');
define('DATABASE_PORT', '');
define('DATABASE_USERNAME', 'root');
define('DATABASE_PASSWORD', '1234');
define('DATABASE_NAME', 'levis');
define('DATABASE_PREFIX', '');
任意のデータベース管理ツールもしくはlevisのデータベース管理ツールから、テーブルを作成します。
CREATE TABLE posts(
id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '代理キー',
created DATETIME NOT NULL COMMENT '作成日時',
modified DATETIME NOT NULL COMMENT '更新日時',
title VARCHAR(255) COMMENT 'タイトル',
body TEXT COMMENT '本文',
PRIMARY KEY(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT '記事';
データを登録します。
INSERT INTO posts VALUES(NULL, NOW(), NOW(), 'テスト1', 'これはテスト1です。');
INSERT INTO posts VALUES(NULL, NOW(), NOW(), 'テスト2', 'これはテスト2です。');
INSERT INTO posts VALUES(NULL, NOW(), NOW(), 'テスト3', 'これはテスト3です。');
これで準備は完了です。引き続き、プログラムを作成していきます。
app/models/posts.php
を作成します。ひとまずファイルの内容はカラで大丈夫です。これにより、以下の命令が使えるようになります。
select_posts()
insert_posts()
update_posts()
delete_posts()
normalize_posts()
validate_posts()
app/controllers/posts/list.php
を作成し、以下の内容を入力します。select_posts()
は app/models/posts.php
を作成することにより使えるようになった関数です。
<?php
$view['posts'] = select_posts(array(
'order_by' => 'id DESC',
));
app/views/posts/list.php
を作成し、以下の内容を入力します。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=<?php t(MAIN_CHARSET) ?>" />
<title>ブログ</title>
</head>
<body>
<h1>ブログ</h1>
<table>
<tr>
<th>ID</th>
<th>登録日時</th>
<th>修正日時</th>
<th>タイトル</th>
</tr>
<?php foreach ($view['posts'] as $post) : ?>
<tr>
<td><?php h($post['id']) ?></td>
<td><?php h(localdate('Y/m/d H:i', $post['created'])) ?></td>
<td><?php h(localdate('Y/m/d H:i', $post['modified'])) ?></td>
<td><?php h($post['title']) ?></td>
</tr>
<?php endforeach ?>
</table>
</body>
</html>
index.php/posts/list
にアクセスし、記事一覧が表示されれば成功です。
引き続き機能を実装していきます。
app/controllers/posts/view.php
を作成し、以下の内容を入力します。
<?php
$posts = select_posts(array(
'where' => 'id = ' . db_escape($_GET['id']),
));
if (empty($posts)) {
warning('記事が見つかりません。');
} else {
$view['post'] = $posts[0];
}
app/views/posts/view.php
を作成し、以下の内容を入力します。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=<?php t(MAIN_CHARSET) ?>" />
<title>ブログ</title>
</head>
<body>
<h1>ブログ</h1>
<table>
<tr>
<th>ID</th>
<td><?php h($view['post']['id']) ?></td>
</tr>
<tr>
<th>登録日時</th>
<td><?php h(localdate('Y/m/d H:i', $view['post']['created'])) ?></td>
</tr>
<tr>
<th>修正日時</th>
<td><?php h(localdate('Y/m/d H:i', $view['post']['modified'])) ?></td>
</tr>
<tr>
<th>タイトル</th>
<td><?php h($view['post']['title']) ?></td>
</tr>
<tr>
<th>本文</th>
<td><?php h($view['post']['body']) ?></td>
</tr>
</table>
</body>
</html>
app/views/posts/list.php
にある
<td><?php h($post['title']) ?></td>
この部分を以下のように修正します。
<td><a href="<?php t(MAIN_FILE) ?>/posts/view?id=<?php t($post['id']) ?>"><?php h($post['title']) ?></a></td>
記事一覧の各タイトルから、個別表示ページヘリンクされます。
app/controllers/posts/add.php
を作成し、以下の内容を入力します。
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$resource = insert_posts(array(
'values' => '(NULL, ' . db_escape(localdate('Y-m-d H:i:s')) . ', ' . db_escape(localdate('Y-m-d H:i:s')) . ', ' . db_escape($_POST['title']) . ', ' . db_escape($_POST['body']) . ')',
));
if (!$resource) {
error('データを登録できません。');
}
redirect('/posts/list');
}
app/views/posts/add.php
を作成し、以下の内容を入力します。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=<?php t(MAIN_CHARSET) ?>" />
<title>ブログ</title>
</head>
<body>
<h1>ブログ</h1>
<form action="<?php t(MAIN_FILE) ?>/posts/add" method="post">
<fieldset>
<legend>登録フォーム</legend>
<dl>
<dt>タイトル</dt>
<dd><input type="text" name="title" size="30" value="" /></dd>
<dt>本文</dt>
<dd><textarea name="body" rows="10" cols="50"></textarea></dd>
</dl>
<p><input type="submit" value="登録する" /></p>
</fieldset>
</form>
</body>
</html>
app/views/posts/list.php
に以下のコードを追加します。
<p><a href="<?php t(MAIN_FILE) ?>/posts/add">新規登録</a></p>
index.php/posts/list
にアクセスすると「新規登録」リンクが表示されるので、そこから記事を投稿できます。
app/models/posts.php
に以下の内容を入力します。
<?php
function validate_posts($queries)
{
$messages = array();
//タイトル
if (isset($queries['title'])) {
if ($queries['title'] === '') {
$messages[] = 'タイトルが入力されていません。';
} elseif (mb_strlen($queries['title'], MAIN_INTERNAL_ENCODING) > 20) {
$messages[] = 'タイトルは20文字以内で入力してください。';
}
}
//本文
if (isset($queries['body'])) {
if (mb_strlen($queries['body'], MAIN_INTERNAL_ENCODING) > 1000) {
$messages[] = '本文は1000文字以内で入力してください。';
}
}
return $messages;
}
app/controllers/posts/add.php
の
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
この直後に以下の内容を追加します。
$warnings = validate_posts(array(
'title' => isset($_POST['title']) ? $_POST['title'] : '',
'body' => isset($_POST['body']) ? $_POST['body'] : '',
));
if (!empty($warnings)) {
warning($warnings);
}
例えばタイトルを空欄のままで記事を投稿しようとすると、警告画面が表示されるようになります。
app/controllers/posts/edit.php
を作成し、以下の内容を入力します。
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$warnings = validate_posts(array(
'title' => isset($_POST['title']) ? $_POST['title'] : '',
'body' => isset($_POST['body']) ? $_POST['body'] : '',
));
if (!empty($warnings)) {
warning($warnings);
}
$resource = update_posts(array(
'set' => 'modified = ' . db_escape(date('Y-m-d H:i:s')) . ', title = ' . db_escape($_POST['title']) . ', body = ' . db_escape($_POST['body']),
'where' => 'id = ' . db_escape($_POST['id']),
));
if (!$resource) {
error('データを編集できません。');
}
redirect('/posts/list');
} else {
$posts = select_posts(array(
'where' => 'id = ' . db_escape($_GET['id']),
));
if (empty($posts)) {
warning('記事が見つかりません。');
} else {
$view['post'] = $posts[0];
}
}
app/views/posts/edit.php
を作成し、以下の内容を入力します。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=<?php t(MAIN_CHARSET) ?>" />
<title>ブログ</title>
</head>
<body>
<h1>ブログ</h1>
<form action="<?php t(MAIN_FILE) ?>/posts/edit" method="post">
<fieldset>
<legend>編集フォーム</legend>
<input type="hidden" name="id" value="<?php t($view['post']['id']) ?>" />
<dl>
<dt>タイトル</dt>
<dd><input type="text" name="title" size="30" value="<?php t($view['post']['title']) ?>" /></dd>
<dt>本文</dt>
<dd><textarea name="body" rows="10" cols="50"><?php t($view['post']['body']) ?></textarea></dd>
</dl>
<p><input type="submit" value="編集する" /></p>
</fieldset>
</form>
</body>
</html>
app/views/posts/list.php
にある
<td><a href="<?php t(MAIN_FILE) ?>/posts/view?id=<?php t($post['id']) ?>"><?php h($post['title']) ?></a></td>
この部分を以下のように修正します。
<td>
<a href="<?php t(MAIN_FILE) ?>/posts/view?id=<?php t($post['id']) ?>"><?php h($post['title']) ?></a>
<a href="<?php t(MAIN_FILE) ?>/posts/edit?id=<?php t($post['id']) ?>">編集</a>
</td>
記事一覧の「編集」リンクから、記事を編集できるようになります。
app/controllers/posts/delete.php
を作成し、以下の内容を入力します。
<?php
$resource = delete_posts(array(
'where' => 'id = ' . db_escape($_GET['id']),
));
if (!$resource) {
error('データを削除できません。');
}
redirect('/posts/list');
app/views/posts/list.php
にある
<td>
<a href="<?php t(MAIN_FILE) ?>/posts/view?id=<?php t($post['id']) ?>"><?php h($post['title']) ?></a>
<a href="<?php t(MAIN_FILE) ?>/posts/edit?id=<?php t($post['id']) ?>">編集</a>
</td>
この部分を以下のように修正します。
<td>
<a href="<?php t(MAIN_FILE) ?>/posts/view?id=<?php t($post['id']) ?>"><?php h($post['title']) ?></a>
<a href="<?php t(MAIN_FILE) ?>/posts/edit?id=<?php t($post['id']) ?>">編集</a>
<a href="<?php t(MAIN_FILE) ?>/posts/delete?id=<?php t($post['id']) ?>">削除</a>
</td>
記事一覧の「削除」リンクから、記事を削除できるようになります。
これで単純な登録編集削除の仕組みが実装できました。ですが簡単なものなら、雛形作成(Scaffold)の機能を使えばフレームワークが自動でコードを作成します。