FuelPHPサイド
FuelBeans 29 : Shooting Brake 2
まだ準備が済んだ段階です。続いて行きましょう。
まあ、ファイルでやってもいいのですが、プログラムが複雑になりますから、DB使いましょう。やっぱり。
必要になるのは、1.認証に必要なユーザーテーブル、2.投稿内容を保存するポストテーブル、3,カテゴリー分けに利用するカテゴリーテーブルです。
1のユーザーテーブルは使用するAuthのSimpleauthクラスで構造が決まっていました。次のSQLを実行して作成します。
CREATE TABLE `users` ( `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY , `username` VARCHAR( 50 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL , `password` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL , `group` INT NOT NULL DEFAULT 1 , `email` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL , `last_login` VARCHAR( 25 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL , `login_hash` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL , `profile_fields` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL , `created_at` INT( 11 ) UNSIGNED NOT NULL , UNIQUE ( `username` , `email` ) )
まず、簡単そうだから3のカテゴリーテーブルを考えましょう。ああ、これは私はSQL用意しませんので、SQLで実行したい方は自分で作成してください。PhpMyAdminを使用して作成していきます。
では、項目を考えます。私は使用するフレームワークに原則合わせます。スキャフォールディングに良い所があるとしたら、そのシステムで期待しているデフォルトのDB形式や、ネーミングルールが見て取れることです。
FuelPHPの場合、整数のid、整数のcreatedが期待されているようですので、それを使用します。Simpleauthはcreated_atになっている?まあ、Authはパッケージで、コアじゃないから、多少の違いはあるのでしょう。
idはカテゴリーのidになります。すると後はカテゴリー名と内容の説明ですね。
カテゴリー名は50文字あれば良いでしょう。説明は区切りがいいところで255文字にしましょう。別段理由はありませんから、もっと短くしたり、長くしたりしてもかまいません。
テーブル名はcotegoryにしましょうね。
続いてブログのポストテーブルです。idとcreatedは同じです。
必要なものは、タイトルと本文です。タイトルは50文字、本文は255文字にします。皆サンはどのくらいの長さが適当だと思いますか。自分の頭で、考えましょう。
最後になりましたが、DB名はスパイシーにSBにしましょう。
さあ、これでDBとテーブル作成できる方は、以下を見ないで作成してください。
PhpMyAdminで作成しますが、お好きな手段でどうぞ。
NetBeansでも実は出来ますが、作成時の項目の設定を行う場合などは、やはりPhpMyAdminに一日の長があるようです。余りNetBeansに固執するのも、効率を落とします。
では、DBを作成しましょう。バージョンにより多少操作は違いますから、詳細は書きませんよ。照合順序の設定を忘れずに。utf8-general-ciですよ。
そうしたら、SQL文を実行して、ユーザーテーブル作成しましょう。usersが出来上がります。
続いてテーブルの作成を行います。categoryテーブルから作成しましょう。
カラム:id、種別:int、インデックス:PRIMARY、AUTO INCREMENT:YES(チェックを入れる)
カラム:name、種別:VARCHAR、長さ:50
カラム:description、種類:VARCHAR、長さ255、ヌル:YES(チェックを入れる)
カラム:created、種別:int、デフォルト値:CURRENT_TIMESTAMP、属性:unsigned
では最後にpostテーブルです。
カラム:id、種別:int、インデックス:PRIMARY、AUTO INCREMENT:YES(チェックを入れる)
カラム:title、種別:VARCHAR、長さ:50
カラム:content、種類:VARCHAR、長さ255
カラム:created、種別:int、デフォルト値:CURRENT_TIMESTAMP、属性:unsigned
なお、アメリカンな日本人は複数形にしろとうるさいですが、日本人は気にしない(意識に無い)ので、単数形にしておきます。
DBとテーブルの作成が終了したら、設定ファイルを修正しましょう。config/development/db.phpですよ。
DB名、ユーザー名、パスワードを設定して保存します。
これで下準備が整いました。
では、分かる人はバリバリ作り始めてください。全然わからない、どうやればいいのか分からない方は、まずroutes.phpを開きましょう。そこに、作るべきものの姿がうっすらと浮かんでいます。
まず、ポストを表示するためには、作成しなくてはなりません。削除、更新するにはデータが必要です。つまり、最初に作成するのは「作成」系のページというわけです。
カテゴリーも同様ですね。
しかし、作成系のページを利用するためには、ユーザー認証ができなくてはなりません。ユーザー認証するには、ユーザーを作成しなくてはなりません。
もし、全体を設計し、それから、段々と細かく設計し、プログラムを組む前に全部の構想ができている、昔ながらの作成方法でしたら、どこから作成しても良いでしょう。作成し、テストデーターを作成し、じっくりテストし、各プログラムがきちんと動作することを確認してから、全体を統合し、テストし、完成に持って行きます。
ですが、現代風に小さいプログラムを高速に開発しようとするなら、プログラムの作成順番を考えましょう。難しい物より、簡単なものを作成し、完成させたら、使いまわせる部分は再利用です。少し組んでは、動作テストし、完成させます。そして、できるだけテストのためだけにデーターを作成する手間を省けるように、作成順をちょっと考えましょう。どう作成していけば、効率的でしょうか。
そう考えると、もうあらかた部品も揃っている、ユーザーの新規登録ページを最初に作成するのが、良いようです。続いて、ログインです。この時点でログインが出来るようになりました。では、次はログオフです。ログインだけでは、ログオフ時の動作テストができなくなります。
次にブログかカテゴリーの作成ページです。カテゴリーが存在しなければ、ブログが作れない仕様にするなら、カテゴリーが先です。もし、予め「未カテゴリー」というカテゴリーをDBで作成して置き、カテゴリーの指定がない場合は、デフォルトとして未カテゴリーにしてしまう仕様であれば、どちらが先でもかまいません。今回は後者の使用にしますが、実際、どちらにするかは後に決めましょう。
404ページはどのタイミングで作成しても良いようです。では、実務であれば、どこか短い時間があいたときに作成しましょう。今回は、説明の都合の良いタイミングで行います。
このくらい決めておけば十分です。では、作成にとりかかりましょう。ユーザー登録ページからですね。
signup.phpです。
前回コードを紹介しました。2行変えてあります。catchの中で、ログに書いておくようにコメントを書いておいたところを、実際にログに書くようにコードを入れました。
Logクラスです。エラーとして書きだしています。
もう一行は、Auth\SimpleUserUpdateExceptionです。Authを付けました。多分、これで合っています。(実際に、このエラーが起きないように組んでいるため、起きていません。)この例外は実に10種類以上の状況で起きます。登録前にきちんとバリデーションを行なっていれば、発生しないはずです。
今回はviewはフォルダー構造をもたせません。viewsの下にベタ置きです。ですから、'from/signup'を'signup'に変えてください。
login.phpです。
呼び出すビューの名前を'form/login'から'login'に変更します。
それと、もともとログインページがルートであるため、認証の成功時にメッセージをセットする他には何もしていませんでしたが、今回は成功時にルートへ転送しましょう。
ユーザー登録を作成したときに、作成したものが既にあります。login.phpとsignup.phpです。(コントローラも同じ名前です。間違えないでね。)formフォルダーの下にあります。この2つをコピーしましょう。今回はフォルダー構造なし、ベタ置きです。viewsの下にコピーしましょう。
全項目が横につながって見場が悪いです。気になる方はちょこっと直しましょう。このままでも良い方は続行しましょう。まずは、FuelPHPを使ったコーディングに集中です。
では、早速ユーザー登録のテストです。localhost/sb/public/signupへアクセスです。
間違っていなければ、ユーザー登録ページが表示されます。
では、ユーザーを登録し、ログインのテストを行なってください。ルート、つまりトップのページがまだありません。デフォルトの404ページがでます。URLがindex.phpになっているはずです。ユーザーが作成されているか、DBの内容を見て確認です。NetBeansからDBの内容を確認する方法を覚えていますか?内容の確認作業でしたら、PhpMyAdminよりも、NetBeansのほうが簡単ですよ。IDEの中で作業が収まります。
動かしてみると、動作しているようです。そして気づいたことがトップページがなくて404になること。404ページがないこと。そしてログインしましたなどのメッセージが分かりづらいことです。
login.phpとsignup.phpの両方を見てみると、共通の部分が多いですね。しかも、メッセージの表示に多少手を加えるためには、この2つに手を加える必要があります。
そして、これからビューは増えていきます。となると、これからなにか変更があるならば、そのたび全部のビューを変更しなくてはなりません。これは、馬鹿らしいですし、テストの手間もかかります。(もし、まともに単体テストを行う場合です。)
では、今のうちに手を加えましょう。共通部分を抜き出し、可変部分だけを<php? echo $content ?>で表示します。今までメッセージはセッションの'notice'という名前を付けたメッセージのみ表示してきました。これは、通知も、完全なエラーも、同じように表示されます。いただけません。
メッセージは3種類にわけます。'message'は通知メッセージ、一般のエラーや注意をひきつける必要があるメッセージは'notice'、システム的なエラーを含んでいるエラーは'error'と分けます。エラーは間違いという意味です。ユーザーが項目を入れ間違ったからって、お前はエラーしていると偉そうにしてはいけません。ユーザーを悪者にしないことです。こうしてくださいねと、教える程度にします。エラーは私(システム)が間違っている、動けない、だからごめんなさい、という時にしましょう。
では共通部分をbase.phpというビューに抜き出し、出力するメッセージを3種類に分けます。とりあえず種類ごとに、文字の色を変える程度にしておきましょう。FuelPHPに集中しましょうね。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title><?php echo $title; ?></title>
</head>
<body>
<h1><?php echo $title; ?></h1>
<?php if (Session::get_flash('error')) : ?>
<div style="color:red;">
<p><?php echo implode('</p><p>', (array) Session::get_flash('error')); ?></p>
</div>
<?php endif; ?>
<?php if (Session::get_flash('notice')) : ?>
<div style="color:darkorange;">
<p><?php echo implode('</p><p>', (array) Session::get_flash('notice')); ?></p>
</div>
<?php endif; ?>
<?php if (Session::get_flash('message')) : ?>
<div style="color:blue">
<p><?php echo implode('</p><p>', (array) Session::get_flash('message')); ?></p>
</div>
<?php endif; ?>
<?php if (true) : ?>
<div>
<?php echo $content; ?>
</div>
<?php endif; ?>
</body>
</html>
login.phpとsignup.phpから、base.phpに抜き出した部分を取り除き、シンプルにしましょう。
コントローラーに戻りましょう。
baseビューの操作は、どのページも同じです。ですから共通する部分をBaseコントローラーにまとめましょう。
今までBaseコントローラーは何もやっていませんでした。今回は働きます。
それに合わせてlogin.phpとsignup.phpを変更しましょう。最後にビューを書き出す部分がメインの変更です。他に、メッセージを3種類に分けました。'message'、'notice'、'error'を使い分けましょう。それと、Baseコントローラーを継承し忘れていたので、忘れないで継承してください。 :D
この時点で、変更のあったコードを全部載せます。Baseコントローラーにたくさんコメントが付きました。コードより多い?そうです。実際、コードよりコメントが多くなりますよ。このコードはドキュメントです。Baseコントローラ継承したlogin.phpやsignup.phpで$this->と打つと、ちゃんと継承したBaseの関数が表示され、それにカーソル移動するとドキュメントとして表示されます。ですから、きちんとコメント入れていくのは、そのコードの利用者、主に自分でしょうが、利用者のためになります。
まずはコントローラーからです。
base.php
<?php
/**
* Shootin Brake コントローラーBase抽象クラス
*/
abstract class Controller_Base extends Controller
{
/* @var $title string */
static $title = '';
/* @var $content string */
static $content = '';
/**
* before() Baseコントローラー抽象クラス
*
* 継承されたクラスが生成されたときに呼び出される
*
* @return Contllorクラスのbefore()返し値
*/
public function before()
{
return parent::before();
}
/**
* after() Baseコントローラー抽象クラス
*
* 継承されたクラスが破棄される前に呼び出される
*
* @param mix 出力内容string|View|ViewModel|parser等
* @return Contllorクラスのbefore()返し値
*/
public function after($response)
{
return parent::after($response);
}
/**
* set_content() ビュー共通部分のコンテンツを設定
*
* @param string $str
* @return \Controller_Base
*/
public function set_content($str)
{
self::$content = $str;
return $this;
}
/**
* set_title() ビュー共通部分のタイトルを設定
*
* @param string $str
* @return \Controller_Base
*/
public function set_title($str)
{
self::$title = $str;
return $this;
}
/**
* render() baseビューの出力
*
* $contentはHTML要素を含むこともあるためフィルタリングしない。
*
* @return Fuel\Core\View;
*/
public function get_view() {
/* @var $view Fuel\Core\View */
$view = View::forge('base');
$view->title = self::$title;
$view->set('content', self::$content, false);
return $view;
}
}
login.php
<?php
class Controller_Login extends Controller_Base
{
function action_index()
{
if (Input::method() == 'POST')
{
/* @var $val Fuel\Core\Validation */
$val = Validation::forge();
$val->add('username', 'ユーザー名')
->add_rule('trim')
->add_rule('required')
->add_rule('min_length', 3)
->add_rule('max_length', 20)
;
$val->add('password', 'パスワード')
->add_rule('trim')
->add_rule('required')
->add_rule('min_length', 3)
->add_rule('max_length', 20)
;
// バリデーション
if ($val->run())
{
// バリデーションOK
// 認証チェック
if (Auth::instance()->login($val->validated('username'), $val->validated('password')))
{
// 認証成功
Session::set_flash('message', 'ログインしました。');
Response::redirect('/');
}
else
{
// 認証失敗
Session::set_flash('notice', 'ユーザー名かパスワードが違っているようです。');
}
}
else
{
// バリデーションNG
Session::set_flash('notice', $val->errors());
} // バリデーション終了
} // ポストチェック終了
$view = $this->set_title('ログイン')
->set_content(View::forge('login'))
->get_view();
return $view;
}
}
signup.php
<?php
class Controller_Signup extends Controller_Base
{
function action_index()
{
if (Input::method() == 'POST')
{
$val = Validation::forge();
$val->add_callable('myvalidation');
$val->add('username', 'ユーザー名')
->add_rule('trim')
->add_rule('required')
->add_rule('min_length', 3)
->add_rule('max_length', 20)
->add_rule('unique', 'users.username')
;
$val->add('password', 'パスワード')
->add_rule('trim')
->add_rule('required')
->add_rule('min_length', 3)
->add_rule('max_length', 20)
;
$val->add('password_confirm', 'パスワード確認')
->add_rule('trim')
->add_rule('required')
->add_rule('match_field', 'password')
;
$val->add('email', 'メールアドレス')
->add_rule('trim')
->add_rule('required')
->add_rule('valid_email')
->add_rule('unique', 'users.email')
;
// バリデーション
if ($val->run())
{
// バリデーションOK
// ユーザーの登録、成功時はユーザーidが返ってくる
try
{
$user_id = Auth::instance()
->create_user($val->validated('username'), $val->validated('password'), $val->validated('email'), 1);
}
catch (Auth\SimpleUserUpdateException $e)
{
// この例外が発生した場合、バリデーションに抜けがあるということ。
// ユーザーIDかメールアドレスのダブリ(他の可能性はsimpleauth.phpを読む)
Log::error('バリデーション後、ユーザー登録失敗:バリデーションに抜けあり','Controller_Signup::action_index');
$user_id = false;
}
if ($user_id == true)
{
// 登録成功
Session::set_flash('message', 'ユーザー登録されました。');
Response::redirect('/'); // ルートへリダイレクト
}
else
{
// 登録失敗
Session::set_flash('error', 'ユーザー登録に失敗しました。');
Response::redirect('/'); // ルートへリダイレクト
}
}
else
{
// バリデーションNG
Session::set_flash('notice', $val->errors());
} // バリデーション終了
} // ポストチェック終了
$view = $this->set_title('ユーザー登録')
->set_content(View::forge('signup'))
->get_view();
return $view;
}
}
続いてビューです。baseは紹介済みですね。
login.php
<?php echo Form::open(Uri::current()); ?>
<?php echo Form::label('ユーザー名 : ', 'username'); ?>
<?php echo Form::input('username', Input::post('username') ? : '', array('size' => 30)); ?>
<?php echo Form::label('パスワード : ', 'password'); ?>
<?php echo Form::password('password', Input::post('password') ? : '', array('size' => 30)); ?>
<?php echo Form::submit('login', 'ログイン'); ?>
<?php echo Form::close(); ?>
signup.php
<?php echo Form::open(Uri::current()); ?>
<?php echo Form::label('ユーザー名 : ', 'username'); ?>
<?php echo Form::input('username', Input::post('username') ? : '', array('size' => 30)); ?>
<?php echo Form::label('パスワード : ', 'password'); ?>
<?php echo Form::password('password', Input::post('password') ? : '', array('size' => 30)); ?>
<?php echo Form::label('パスワード確認 : ', 'password_confirm'); ?>
<?php echo Form::password('password_confirm', Input::post('password_confirm') ? : '', array('size' => 30)); ?>
<?php echo Form::label('メールアドレス : ', 'email'); ?>
<?php echo Form::input('email', Input::post('email') ? : '', array('size' => 30)); ?>
<?php echo Form::submit('signup', 'ユーザー登録'); ?>
<?php echo Form::close(); ?>
| FuelBeans 28 : Shooting Brake 1< 前 | 次 >FeulBeans 30 : バリデーションエラーメッセージ |
|---|
| < 前 | 次 > |
|---|