バリデーションだけを使う場合などは、通常の手順で任意の名前のモデルを作成します。ただしデータベースを扱う命令を呼び出すとエラーになります。
token() でトークンの発行と確認ができます。
専用の機能は無いので、通常の方法でアップロードします。
専用の機能は無いので、セッションを使うなどして実装します。
専用の機能は無いので、import() で共通テンプレートを読み込むなどします。
専用の機能は無いので、共通関数を定義するなどします。
専用の機能は無いので、SQLの発行には db_select() や select_xxx() などを使います。
アソシエーションのように関連データを取得したい場合、モデル内で db_select() や db_query() などによってSQLを発行するなどします。
一例ですが、PHPプログラム側で
$GLOBALS['http_path'] = '/path/to/app/';
このようにパスを定義しておき、共通のビューで常に
<script>
var HTTP_PATH = '<?php t($GLOBALS['http_path']) ?>';
</script>
このような変数もしくは定数を定義し、その後外部JSファイルを読み込むことでパスを参照できます。
Gitなどでソースコードを管理している場合、本番環境・検収環境・開発環境の切り替えは一例ですが以下の手順で対応できます。
PRODUCTION という定数が定義済みか否かで判定する。同様に、検収環境か否かは STAGING という定数で判定する。どちらも定義されていなければ開発環境とする。config.php を config.php.default という名前で保存しておき、環境に依存しない内容のみ設定しておく。config.php はリポジトリの管理対象外にしておく。config.php.default を複製して config.php を作成し、環境に応じた設定を行う。PRODUCTION という定数を、検収環境なら STAGING という定数を config.php 内で定義する。以下の語句からはじまる定数。
MAIN_DATABASE_SESSION_TOKEN_REGEXP_PAGE_TEST_DEBUG_LOGGING_VERSION_以下の変数。
$params$db$view以下のリクエスト変数。
modeworktypetokentest以下のグローバル変数。
core以下のセッション変数。
core以下の関数。
import()bootstrap()session()database()normalize()routing()service()model()controller()view()unescape()sanitize()unify()convert()alt()truncate()e()t()h()localdate()clientip()ssl()token()redirect()debug()logging()auth()ok()warning()error()password()about()style()以下の語句からはじまる関数。
db_info_rand_regexp_test_cookie_directory_file_hash_mail_string_ui_この機能はVer6.1以降で対応しています。
グローバル変数 $GLOBALS['core']['target'] に値を代入すると、import() でファイルを読み込む際の場所として認識されます。
解説作成中。
この機能はVer6.6以降で対応しています。
グローバル変数 $GLOBALS['core']['routing'] に値を代入すると、コントローラとビューのファイル読み込み先を変更できます。(モデルの呼び出しには影響しません。)
これを利用して app/routing.php に以下の処理を書くと、URLルーティングのルールを変更できます。これで index.php/admin/param2/param3 でアクセスした時、param2 と param3 の値によって admin 内のコントローラとビューが呼び出されるようになります。
<?php
if (isset($params[0]) && preg_match('/^admin$/', $params[0])) {
$GLOBALS['core']['routing'] = $params[0];
$_REQUEST['mode'] = empty($params[1]) ? 'home' : $params[1];
$_REQUEST['work'] = empty($params[2]) ? 'index' : $params[2];
}
具体的には、
index.php/param1/param2 にアクセスすると app/controllers/param1/param2.php が呼び出されるindex.php/admin/param2/param3 にアクセスすると app/controllers/admin/param2/param3.php が呼び出されるとなります。管理ページが非常に多機能な場合など、処理をフォルダごとに分けることができるようになります。
admin の部分などを変更すれば、ルーティングのルールは自由に変更できます。
app/routing.php の詳細については、フレームワーク本体の処理内容に手を加えるを参照してください。
この機能はVer6.6以降で対応しています。
モデルでデータベースのテーブルを操作する際、option を渡すとクエリの最後に任意の文字列を指定できます。これにより、例えば以下のように書くことでテーブル・ロックを行うことができます。
$view['posts'] = select_posts(array(
'where' => 'id >= 1 AND id <= 10',
'option' => 'FOR UPDATE',
));
テーブルロックのために指定できる文字列や、テーブルロックが使えるかどうかは、使用するデータベースの仕様に依存します。
$view['posts'] = select_posts(array(
'where' => 'id >= 1 AND id <= 10',
));
このSQLを扱う場合、プレースホルダを使って以下のように書くことができます。(配列で指定した値が、先頭から順に :? へ代入されます。)
$view['posts'] = select_posts(array(
'where' => array('id >= :? AND id <= :?', array(1, 10)),
));
以下のように、プレースホルダに文字列を使うこともできます。
$view['posts'] = select_posts(array(
'where' => array(
'id >= :from AND id <= :to',
array(
'from' => 1,
'to' => 10,
),
),
));
なお、これは擬似プレースホルダです。同じSQLで値だけ変えて複数実行することはできません。大量のSQLを実行する際に速度アップを図りたい場合、一例ですが以下のようにトランザクションを使用してください。
//トランザクションを開始
db_transaction();
while (/* データ順次取り出し */) {
//データ内容確認&登録処理
}
if (/* エラーがなければ */) {
//トランザクションを終了
db_commit();
} else {
//エラーがあればロールバック
db_rollback();
}
登録・編集の際は以下のような特別な書き方を利用できます。(values や set の内容が、自動的にエスケープされます。)特に理由がなければ、この書き方を推奨します。
$resource = insert_posts(array(
'values' => array(
'id' => $_POST['id'],
'created' => $_POST['created'],
'modified' => $_POST['modified'],
'title' => $_POST['title'],
'body' => $_POST['body'],
),
));
if (!$resource) {
error('insert error.');
}
$resource = update_posts(array(
'set' => array(
'created' => $_POST['created'],
'modified' => $_POST['modified'],
'title' => $_POST['title'],
'body' => $_POST['body'],
),
'where' => array(
'id = :id',
array(
'id' => $_POST['id']
),
),
));
if (!$resource) {
error('update error.');
}
上の書き方であえて式を渡したい場合、以下のように配列で渡します。
$resource = update_posts(array(
'set' => array(
'sort' => array('sort + 1'),
),
'where' => array(
'id = :id',
array(
'id' => 2,
),
),
));
db_connect に接続情報を渡すと、別のデータベースに接続できます。
db_connect(array(
'master' => array(
'host' => 'localhost',
'username' => 'root',
'password' => '1234',
'name' => 'master_db',
),
));
$test = db_result(db_query('SELECT * FROM members'));
このように接続した以降は、データベースを扱う命令を呼び出すと master_db データベースが呼ばれるようになります。再度本来のデータベースを呼び出したい場合、
db_connect('default');
を呼び出します。その上で再度 master_db データベースを呼び出したい場合、
db_connect('master');
とすれば呼び出せます。(連想配列のキーの値を指定。)
接続情報として、以下の値を渡せます。値を省略した場合、config.php で指定した値が使われます。
この機能はVer6以降で対応しています。
複数人でプログラムを作成したり複数箇所でプログラムを実行したりする場合のために、データベースのバージョン管理(マイグレーション)を行えます。
この機能を使用する場合、まずはデータベースに接続できるようにしておきます。
次に index.php と同じ場所(config.php で DATABASE_MIGRATE_PATH の値を変更した場合はその場所)に migrate ディレクトリを作成し、http://www.example.com/index.php/?mode=db_migrate にアクセスすると migrate/ 内に置いたSQLファイルが実行されます。
SQLファイルは YYYYMMDDHHIISS-任意の半角英数字.sql という名前にします。YYYYMMDDHHIISS はバージョン番号として扱われるため、重複しないようにします。具体的には以下のような名前にします。
20151128210200-create_table_address.sql
20151128210201-insert_into_address.sql
初回実行時、バージョン管理用のテーブルが levis_migrations という名前で作成されます。実行したファイルはこのテーブルに記録されるため、何度も同じSQLファイルが実行されることはありません。この仕組みを利用すれば、各環境でデータベースの定義内容を同じ状態に保つことができます。
この機能はVer7以降で対応しています。
データベースのバックアップをサーバ上に作成できます。
この機能を使用する場合、まずはデータベースに接続できるようにしておきます。
次に index.php と同じ場所(config.php で DATABASE_BACKUP_PATH の値を変更した場合はその場所)に backup ディレクトリを作成し、http://www.example.com/index.php/?mode=db_backup にアクセスするとバックアップ画面が表示されます。「backup」ボタンを押すと backup/ 内にSQLファイルが保存されます。
バックアップが有効になっていると、データベースのバージョン管理でデータベースに変更を加える直前に自動でバックアップが作成されます。
app/ 内に特定の名前でファイルを置いておくと、フレームワーク本体から読み込まれます。これを利用して、フレームワークの挙動を調整できます。
app/bootstrap.php があると、最初に自動で読み込まれます。(この機能はVer6.3以降で対応しています。)app/session.php があると、セッション初期化処理の後に自動で読み込まれます。app/database.php があると、データベース接続処理の後に自動で読み込まれます。app/normalize.php があると、データ正規化処理の後に自動で読み込まれます。app/routing.php があると、ルーティング処理の後に自動で読み込まれます。例えば app/database.php に以下の処理を書くと、他データベースへの接続準備になります。これで、モデルやコントローラから db_connect('master'); と書くだけで他のデータベースに接続できます。
<?php
db_connect(array(
'master' => array(
'host' => 'localhost',
'username' => 'root',
'password' => '1234',
'name' => 'master_db',
),
));
db_connect('default');
例えば app/routing.php に以下の処理を書くと、URLルーティングのルールを変更できます。これで index.php/test/param2/param3 でアクセスした時、param2 と param3 の値によってコントローラなどが呼び出されるようになります。
<?php
if (isset($params[0]) && $params[0] === 'test') {
if (isset($params[1])) {
$_REQUEST['mode'] = empty($params[1]) ? 'home' : $params[1];
}
if (isset($params[2])) {
$_REQUEST['work'] = empty($params[2]) ? 'index' : $params[2];
}
}
定数 MAIN_PATH にlevisの配置場所を設定し、libs/cores/loader.php を読み込むとフレームワーク外からフレームワークの命令を利用できます。
loader.php を読み込むことによりフレームワークの基本的な命令は使えるようになりますが、モデル・ビュー・コントローラ・サービスは明示的に読み込む必要があります。model() と書くとすべてのモデルを、service() と書くとすべてのサービスを、一括で読み込みます。
<?php
define('MAIN_PATH', '/var/www/levis/');
require_once MAIN_PATH . 'libs/cores/loader.php';
//教室を取得
model('classes.php');
$classes = select_classes(array(
'order_by' => 'id',
));
//名簿を取得
model('members.php');
$members = select_members(array(
'order_by' => 'id',
));
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>テスト</title>
</head>
<body>
<p>テスト。</p>
<ul>
<?php foreach ($classes as $class) : ?>
<li><?php h($class['name']) ?></li>
<?php endforeach ?>
</ul>
<ul>
<?php foreach ($members as $member) : ?>
<li><?php h($member['name']) ?></li>
<?php endforeach ?>
</ul>
</body>
</html>
<?php
define('MAIN_PATH', '/var/www/levis/');
require_once MAIN_PATH . 'libs/cores/loader.php';
//ページを表示
model('classes.php');
controller('home/index.php');
view('home/index.php');
<?php
define('MAIN_PATH', '/var/www/levis/');
require_once MAIN_PATH . 'libs/cores/loader.php';
//引数付きでページを表示
model();
$params = array('class', 'class1');
controller('class/index.php');
view('class/index.php');
<?php
define('MAIN_PATH', '/var/www/levis/');
require_once MAIN_PATH . 'libs/cores/loader.php';
//ページを取得
model('classes.php');
controller('home/index.php');
echo view('home/index.php', true);
index.php と同じ場所(config.php で PAGE_PATH の値を変更した場合はその場所)に page ディレクトリを作成し、その中にPHPファイルを作成すると通常ページとして扱われます。
つまり、例えば page/test.php を作成すると、/index.php/test(もしくは /test) にアクセスしたとき、フレームワークを経由してこのページが表示されます。フレームワークとURL規則を統一した通常ページを作成したり、プログラムへのログイン状態に応じて通常ページの表示内容を切り替えたりする場合に利用できます。
app/controllers/page.php があると、通常ページ共通のコントローラとして扱われます。
フレームワーク外からフレームワークの命令を利用の方法でプログラムを作成し、そのプログラムをcronで呼び出します。
例えば cron.php を作成した場合、一例ですがcronでは以下のように指定します。
*/5 * * * * webmaster /usr/bin/php /var/www/html/cron.php
データベースにテーブルを作成し、index.php と同じ場所(config.php で DATABASE_SCAFFOLD_PATH の値を変更した場合はその場所)に scaffold ディレクトリを作成し、http://www.example.com/index.php/?mode=db_scaffold にアクセスすると scaffold/app/ ディレクトリ内にプログラムの雛形が作成されます。
scaffold/app/ を app/ に移動させると、そのままプログラムのモデル・ビュー・コントローラとして利用できます。(ただし即座に公開できるプログラムというより、プログラム作成の補助に使うためのコードという位置づけで作成しています。)
Ver6以降では、scaffold/test/ に単体テスト用のプログラムも自動作成されます。
index.php と同じ場所(config.php で TEST_PATH の値を変更した場合はその場所)に test ディレクトリを作成し、その中にテスト用プログラム(calculate.php など、ファイル名は任意。)を作成し、http://www.example.com/index.php/?mode=test_index にアクセスすると単体テストを行えます。ページ内に表示される All Test. リンクから、一括テストも行えます。
テスト用プログラムは、具体的には以下のような内容になります。
<?php
//掛け算の結果をテスト(「multiplication 1」のみ成功する)
test_equals('multiplication 1', multiplication(4, 2), 8);
test_equals('multiplication 2', multiplication(4, 2), 6);
test_equals('multiplication 3', multiplication(4, 2), 4);
//割り算の結果をテスト(「division 3」のみ成功する)
test_equals('division 1', division(4, 2), 6);
test_equals('division 2', division(4, 2), 4);
test_equals('division 3', division(4, 2), 2);
//テスト用の関数
function multiplication($x, $y) {
return $x * $y;
}
function division($x, $y) {
return $x / $y;
}
<?php
//返り値に特定の文字列が含まれるかテスト(「message 1」のみ成功する)
test_contains('message 1', message('test'), 'test');
test_contains('message 2', message('test'), 'sample');
//テスト用の関数
function message($message) {
return 'This is a \'' . $message . '\'!';
}
<?php
//返り値に特定の形式の文字列が含まれるかテスト
test_regexp('check_date', check_date(), '\d\d\d\d-\d\d-\d\d');
//テスト用の関数
function check_date() {
return date('Y-m-d');
}
<?php
//トップページに「テスト」という文字が含まれているかテスト
model('classes.php');
controller('home/index.php');
$html = view('home/index.php', true);
test_contains('home/index', $html, 'テスト');
//index.php/class/class1に「テスト」という文字が含まれているかテスト
model();
$params = array('class', 'class1');
controller('class/index.php');
$html = view('class/index.php', true);
test_contains('class/index', $html, 'テスト');
テスト用の関数は libs/cores/test.php 内で定義されており、以下を利用できます。
test_equals('テストの説明', '実際の値', '期待する値') //「実際の値」と「期待する値」が等しければテスト成功とみなします。
test_not_equals('テストの説明', '実際の値', '期待する値') //「実際の値」と「期待する値」が等しくなければテスト成功とみなします。
test_greaterthan('テストの説明', '実際の値', '期待する値') //「実際の値」が「期待する値」より大きければテスト成功とみなします。
test_greaterthanorequal('テストの説明', '実際の値', '期待する値') //「実際の値」が「期待する値」以上ならばテスト成功とみなします。
test_lessthan('テストの説明', '実際の値', '期待する値') //「実際の値」が「期待する値」より小さければテスト成功とみなします。
test_lessthanorequal('テストの説明', '実際の値', '期待する値') //「実際の値」が「期待する値」以下ならばテスト成功とみなします。
test_contains('テストの説明', '実際の値', '期待する値') //「実際の値」に「期待する値」が含まれていればテスト成功とみなします。
test_not_contains('テストの説明', '実際の値', '期待する値') //「実際の値」に「期待する値」が含まれていなければテスト成功とみなします。
test_regexp('テストの説明', '実際の値', '期待する値') //「実際の値」と「期待する値」の正規表現にマッチすればテスト成功とみなします。
test_not_regexp('テストの説明', '実際の値', '期待する値') //「実際の値」と「期待する値」の正規表現にマッチしなければテスト成功とみなします。
test_array_haskey('テストの説明', '配列', '期待する配列のキー') //「配列」に「期待する配列のキー」が存在すればテスト成功とみなします。
test_array_not_haskey('テストの説明', '配列', '期待する配列のキー') //「配列」に「期待する配列のキー」が存在しなければテスト成功とみなします。
test_array_subset('テストの説明', '配列', '期待する配列の値') //「配列」と「期待する配列の値」が存在すればテスト成功とみなします。
test_array_not_subset('テストの説明', '配列', '期待する配列の値') //「配列」と「期待する配列の値」が存在しなければテスト成功とみなします。