[ad#co-1]
インストール
1.クイックインストーラをインストールする
$ curl get.fuelphp.com/oil | sh
2.プロジェクト作成する
作成したいフォルダへ移動し、以下コマンド実行。
$ oil create test
※今回の場合、testプロジェクトを作成
そうするとtestフォルダが作成され、その中に色々なファイルがインストールされる
※もしdate_default_timezoneなんたら的なエラーが出たら、/etc/php.ini
ファイル(ルートにあるetcフォルダ内のやつ)のdate.timezone
部分を
date.timezone = 'Asia/Tokyo'
と変更する。(頭のセミコロン「;」は取ること!)
もしetcフォルダ内にphp.iniファイルがなかった場合
php.ini.default的なのしかなければ、下記のsudo cp
コマンドでコピーして名前をphp.iniへ変更。
$ sudo cp php.ini.default php.ini
↑php.ini.defaultファイルを新しい名前のphp.iniファイルとしてコピーするよって意味。
もし、中身を編集してもreadonlyだとエラーが出る場合は、下記のようにsudo chmod
コマンド で 権限を664に変更すれば書き込み可能になる。
$ sudo chmod 664 php.ini
フォルダ構成
覚えておけばいいところだけ抜き出した構成。
fuel/ ・・・FuelPHPのプログラムやファイル群
|- app/
| |
| |- logs/ ・・・ログファイル
| |- classes/
| | |------- controller/ ・・・コントローラファイル置き場
| | | ∟ blog.php
| | |
| | |------- model/ ・・・モデルファイル置き場
| |
| |- config/ ・・・各種設定ファイル
| | |
| | |- config.php ・・・ アプリケーション全体に関わる設定(p.32)
| | |
| | |- db.php ・・・DBの設定
| |
| |- views/ ・・・ビューファイル置き場
|
|- core/ ・・・FuelPHPが標準で提供するプログラムなど。配下はappフォルダ内の構成と似てる。
|
|- packages/ ・・・パッケージとして提供されるプログラムなど
public/ ・・・WEBサーバの公開ディレクトリ以下に配置する
ページを開いてみる
‘test’というプロジェクトを作った場合、下記のURLでアクセスするとFuelPHPのWELCOME画面が表示される。
http://localhost:8888/test/public/
URLとファイルとの対応関係
例えば、下記のようなURLがある場合、
http://localhost:8888/test/public/index.php/welcome/index
サーバーはhttp://localhost/blog/public/index.php
を呼び出し、welcome/index
がパラメータとして渡され、/
で区切られた最初のパラメータ名の「コントローラ」を呼び、次のパラメータ名の「アクション」を呼び出す。
(コントローラー名がWelcomeのwelcome.phpファイル内にある、アクション名indexを呼んでるってこと)
安全なディレクトリ構成へ変更する
WEBサーバの公開ディレクトリ(htdocs)にFuelPHPをインストールした場合、FuelPHPの設定ファイルなどもそこにインストールされるため全て公開されてしまい、あまり良くない。
そこで、htdocsと同階層に違うフォルダを作成(名前は「sample」とかなんでもOK)し、そこにFuelPHPをインストールする。
(必要なら、インストールしたFuelPHPのフォルダに対してアクセス権も設定しておくこと)
その後、httpd.confに下記のようなバーチャルホスト設定を追加する。
NameVirtualHost 127.0.0.1:80
<VirtualHost 127.0.0.1:80>
DocumentRoot /usr/local/apache/sample/public
ServerName sample.localhost <!-- ⇐sample.localhostにアクセスするとDocumentRootで設定したディレクトリを参照するようにしてる -->
<Directory /usr/local/apache/sample/public>
Order Allow,Deny
Allow from All
Options All
AllowOverride All
DirectoryIndex index.php index.html
</Directory>
</Directory>
</VirtualHost>
そして、/etc/hosts
ファイルに下記1行を追加する。
127.0.0.1 sample.localhost
※sample.localhostのホスト名は127.0.0.1のipアドレス(自分のPCになる)だよと定義してる
コントローラーからビューへの値の受け渡し方
public function action_index(){
$data = array();
$data['name'] = 'きむりおん';
return Response::forge(View::forge('welcome/test',$data));
// 「〜::forge」はオブジェクトを生成するメソッド。
// Response::forgeでresponse オブジェクトを生成してる。(ブラウザへ返すレスポンスのこと)
// responseオブジェクトを生成するときにViewオブジェクトのインスタンスを渡してる。
// View::forgeの中の第一引数でビューファイルを指定。今回だとviews/welcomeフォルダ内のビューファイルtest.phpを指定してる
// 第二引数では、指定したビューファイルへ渡す値を指定してる。(配列で渡す)
}
<html>
<head>
<title>ビューのテスト</title>
<body>
<?php echo $name; ?>
<!-- ==============================================
コントローラーから渡された配列のインデックス名を変数として指定するだけでその値が使える
=============================================== -->
</body>
</head>
</html>
他にも、オブジェクト変数で渡すこともできる
public function action_index(){
$data = new stdClass();
$data->name = 'きむりおん';
return Response::forge(View::forge('welcome/test',$data));
}
上記ケースの場合、アクセスするURLは下記のようになる。
http://localhost:8888/test/public/welcome/
ビューへの値の受け渡し方
値の受け渡し方は2通りある。
配列などで受け渡す方法とオブジェクトを作成して受け渡す方法。
引数で受け渡す
public function action_test(){
$data = array();
$data['title'] = 'テストです。';
$data['body'] = '内容です。';
return view::forge('test',$data); //第2引数に渡したい変数を指定する
}
ビューのオブジェクトを生成して受け渡す
public function action_test(){
$view = View::forge('test'); //viewオブジェクトを生成する。この時、引数に表示したいビューファイルを指定
//ビューファイル内にある変数へ値を渡したい場合はsetを使う
//第一引数に変数名、第二引数に値を指定する
$view->set('title','テストです。');
$view->set('body','内容です。');
return $view;
}
ビューを作る
fuel/app/views
内に〇〇.phpという形で作成してあげる。
もし、viewsフォルダ内に別フォルダを作って、その中にビューファイルを置いた場合、コントローラー側でのビューファイルの指定は下記のようにする。
例:viewsフォルダの中にhome
フォルダを作り、その中にビューファイルindex.php
を作った場合
<html>
<head>
<title><?php echo $title; ?></title>
</head>
<body>
Welcome, <?php echo $username; ?>.
</body>
</html>
class Controller_Home extends Controller
{
public function action_index()
{
$data = array();
$data['username'] = 'Joe14';
$data['title'] = 'Home';
//ビューへ変数と値を渡す
return View::forge('home/index', $data);
//home/indexとすれば、homeフォルダ内のindex.phpのビューという意味になる。
}
}
※ビューに渡される値は全て自動でサニタイズされる。
ビューファイルを入れ子にする
headerやfooterなど別ファイルにして、ビューファイルの中で別のビューファイルを読み込むようにする。
<html>
<head>
<?php echo $head; ?>
</head>
<body>
<?php echo $header; ?>
<?php echo $content; ?>
<?php echo $footer; ?>
</body>
</html>
<title><?php echo $title; ?></title>
fuel/app/views/header.php
<div class="logo"></div>
<div class="logo_text"><?php echo $site_title; ?></div>
<h1><?php echo $title; ?></h1>
<div class="welcome_user">Welcome <?php echo $username; ?></div>
<div class="footer">
© Copyright <?php echo date('Y');?> <?php echo $site_title; ?>
</div>
class Controller_Home extends Controller
{
public function action_index()
{
//変数としてビューを割り当てる
$view = View::forge('template/template_1colum'); //テンプレートとなるビューファイルの読込み
$view->set('head',View::forge('template/head'));
$view->set('contents',View::forge('login'));
$view->set('footer',View::forge('template/footer'));
$view->set_global('site_title','WEBsite');
//テンプレートビューの中でさらに読み込んだビューの中にある変数へ値を渡したい場合はset_globalを使う。
//テンプレートビューの中で使う変数へ値を渡すだけならsetでいい。
// レンダリングした HTML をリクエストに返す
return $view;
}
}
画像やリンク、cssの配置
画像やcss、jsに関してはプロジェクト名/public/assets
の中にそれぞれimg,js,cssでフォルダが既に作られているので、その中に置く。
そして、画像を表示させたい時やCSS、jsの読込みしたいときなどはAssetクラスのメソッドを使う。
Assetクラス
Assetクラスのメソッドを使うことで簡単にhtmlに書き出してくれる。
画像を読み込む場合
ビュー内で下記のようにファイルを指定し
<?php echo Asset::img('title.png'); ?>
公開ディレクトリ配下のassets/img/
の中にtitle.png
ファイルを配置すればOK。
属性など指定したい場合は下記のように第2引数に指定してあげる。
<?php echo Asset::img('title.png',array('width'=>'200','alt'=>'タイトル画像')); ?>
CSSを読み込む場合
ビュー内で下記のようにファイルを指定し、
<?php echo Asset::css('base.css'); ?>
公開ディレクトリ配下のassets/css/'の中に
base.css’ファイルを配置すればOK。
aタグを使う場合
<?php echo Html::anchor('welcome','トップへ'); ?>
※リンク先はコントローラ名/メソッド名の形で指定する
※第3引数は属性をつけたいときに使用する
※第4引数にtrueを指定するとhttpsで始まるリンクになる。
iconを読み込みたい場合
find_fileメソッドを使うと指定したファイルのパスが返ってくるのでそれを使う。
<link rel="icon" href="<?php Asset::find_file('icon.ico', 'img','icons/'); ?>" type="image/png">
第一引数がファイル名、第二引数がファイルタイプ(imgかjsかcss)、第三引数がフォルダ
上記の場合、assets/icons/内にある画像ファイルのicon.pngのパスを教えて!とメソッドを呼んでいる。
assetsフォルダ内にjs,css,img以外のフォルダを作ってそこのファイルを読み込みたい場合
初期状態では、Asset::jsとやればassets/js内のファイルを指定でき、Asset::cssとやればassets/css内のファイルが指定できるが、
assets/pluginsなどのフォルダ内のファイルを指定したい場合には、下記のようにAssetクラスにパスを追加してやる必要がある。
<?php Asset::add_path('assets/plugins/', 'js'); ?>
※今回はjsと指定したので、Asset::jsでファイル名を指定するとデフォルトのassets/jsフォルダ内と新たにパスを追加したassets/pluginsフォルダ内を探しに行く。
コントローラー
コントローラの基本
fuel/app/classes/controller
の中に作成する。
ファイル名は小文字。
www.test.com/example/index
というURLを指定した場合、exampleというコントローラーのindexというアクションが指定されたという意味になる。
use \Model\Welcome; //使うモデルを指定
//ファイル名の頭文字を大文字にして、Controller_をつけるのが規則
//基本はControllerを継承しておく
class Controller_Welcome extends Controller {
//action_をつけてアクション名を指定
public function action_index()
{
//cssを指定
$data['css'] = Asset::css(array('reset.css','960.css','main.css'));
//ビューへ(この場合、welcomeフォルダ内のindex.phpのビューファイルを指定してる)
return Response::forge(View::forge('welcome/index'));
}
}
リクエストされたコントローラ名が見つからないorコントローラ名が指定されてない場合
config/routes.php
の設定先に遷移する
<?php
return array(
'_root_' => 'welcome/index', //コントローラ名が指定されてない場合の遷移先
'_404_' => 'error/404', //コントローラ名、アクション名がない場合の遷移先
);
モデル・ビューへの呼び出し
<?php
class Controller_Example extends Controller{
public function action_test(){
$model = Model_Test::forge(); //モデルのインスタンスを生成して利用する
return View::forege('test_view'); //ビューの呼び出し
}
}
?>
パラメータを受け取る
URLのアクションメソッド名の後にパラメータを指定することでコントローラ側で受け取れる。
http://localhost/sample/test/param1/param2/...
------- ---- ------ ------
① ② ③ ④
①コントローラ名
②アクション名
③パラメータ1
④パラメータ2
//受け取る側のコントローラのアクションメソッドには引数を設定する
//※URLにパラメータが設定されてなかった場合、エラーになってしまうので必ずデフォルト値を設定しておくこと
public function action_test($param1 = null,$param2 = null){
・・・
}
before()メソッドでログインチェックなどの各ページ共通処理をする
before()メソッドはURLで指定されたアクションメソッドが実行される前に実行されるので、会員専用ページなどのログインチェックなどに使える。
<?php
class Controller_Sample extends Controller{
public function before(){
//認証チェックなどの処理をここに記述する
}
public function action_editpass(){
・・・
}
public function action_editinfo(){
・・・
}
}
コントローラーをまとめる
似た機能のコントローラー同士でまとめておきたいなど、classes/controller
の配下にさらにディレクトリを作ってコントローラーを配置したい場合は以下のようにする。
例:会員用機能のコントローラをmembersディレクトリ配下にまとめる場合
classes/
|-----controller/
|--------members/
|--------mypage.php
|--------editpass.php
http://localhost/members/mypage
------- --------
⬆ ⬆
ディレクトリ名 コントローラ名
class Controller_Members_Mypage extends Controller{
・・・
}
//クラス名は「Controller_ディレクトリ名_コントローラー名」にする
ベースのコントローラを作る
会員専用サイトなどで複数のコントローラで同じ処理をさせる場合、各コントローラ内に記述していくよりも、ベースのコントローラを作って、それを継承する形で各コントローラを作ってあげる。
※ベースコントローラと同階層に継承したコントローラを置いても大丈夫
classes/
|-----controller/
|------login.php ⬅ログイン画面を表示させるコントローラ
|------members.php ⬅ベースコントローラ
|--------members/
|--------mypage.php ⬅ベースコントローラを拡張して作ったコントローラ
|--------editpass.php ⬅ベースコントローラを拡張して作ったコントローラ
class Controller_Members extends Controller{
public function before(){
if(!Auth::check()){ //ログインチェック
Response::redirect('login'); //認証されてなければリダイレクト
}
}
}
作ったコントローラを継承する
オートローダのお陰で、クラス定義で名前以外のものを記述しなくても他のコントローラを継承できる。
class Controller_Example extends Controller_Welcome
{
// メソッド
}
テンプレートコントローラ
ヘッダ、フッタは共通のものを使い、コンテンツ部分だけ各ページで違う内容にしたいなどサイト全体で共通するHTMLを作成し、一部分だけを入れ替える時に使えるのが「テンプレートコントローラ」
テンプレートコントローラにはあらかじめ、$templateというプロパティが用意されているので、そいつに色々設定してやる。
テンプレートコントローラを使うにはController_Templateを継承してあげる。
class Controller_Test extends Controller_Template{
public function action_index(){
$this->template->title = 'タイトルです。'; //タイトルを設定
$this->template->content = View::forge('sample/index'); //contentにビューファイル(sample/index.php)を設定
}
}
<html>
<head>
<title><?php echo $title; ?></title>
</head>
<body>
<div id="header">
<p>へっだー</p>
</div>
<div id="contents">
<?php echo $content; ?>
</div>
<div id="footer">
<p>ふったー</p>
</div>
</body>
</html>
モデルを作る
まずDBの設定をする
/fuel/app/config/db.php
をいじる。
return array(
'active' => 'testdb',
/**
* Base config, just need to set the DSN, username and password in env. config.
*/
'default' => array(
'type' => 'pdo',
'connection' => array(
'persistent' => false,
),
'identifier' => '`',
'table_prefix' => '',
'charset' => 'utf8',
'enable_cache' => true,
'profiling' => false,
),
'redis' => array(
'default' => array(
'hostname' => '127.0.0.1',
'port' => 6379,
)
),
'testdb' => array(
'type' => 'mysqli',
'connection' => array(
'hostname' => 'localhost',
'database' => 'データベース名',
'username' => 'ユーザ名',
'password' => 'パスワード',
'persistent' => FALSE,
),
'table_prefix' => '',
'charset' => 'utf8',
'caching' => false,
'profiling' => true,
),
);
モデルを作る
Model_Crudクラスを拡張して作る
Model_CrudクラスにはCRUD(作成、検索、更新、削除)するためのメソッドが用意されてるので、簡単な操作くらいならこっちを使えばOK。
例)フォームで入力した内容をDB登録し、そのDB内容を表示する場合
<?php
class Model_Post extends \Model_Crud{
//①テーブルの名前を登録する
protected static $_table_name = 'posts';
//②テーブルの主キーを登録する
protected static $_primary_key = 'id';
}
<html>
<meta charset="UTF-8">
<body>
//送信した値の受け渡し先をaction属性で指定。この場合コントローラpostのアクションsaveへ渡る
<form action="/post/save" method="post">
<input type="text" name="title" value="" />
<textarea cols="30" rows="3" name="body" ></textarea>
<input type="submit" name="submit" value="送信" />
</form>
</body>
</html>
<?php
class Controller_Post extends Controller{
//フォームを表示させるためのアクション(http://localhost/post/formで表示される)
public funciton action_form(){
return View::forge('post/form'); //View::forege()メソッドでビューを読み込む
}
//フォームが送信された際に値を受け取り、DBへ書き込むアクション
public function action_save(){
$form = array();
$form['title'] = Input::post('title'); //フォームの内容を配列へ格納
$form['body'] = Input::post('body');
$post = Model_Post::forge(); //Model_Postクラスのオブジェクトを作成
$post->set($form); //setメソッドで、配列をpostオブジェクトに設定
$post->save(); //saveメソッドで、テーブルにレコードを書き込む
Response::redirect('post'); //投稿一覧ページへリダイレクト
}
//DBの内容を表示するアクション
public function action_index(){
$data = array();
$data['rows'] = Model_Post::find_all(); //Model_Postクラスにあるfind_allメソッドでDBを検索し、結果を配列へ格納
return View::forege('Post/list',$data); //配列をViewのlistへ渡す
}
}
namespace Model;
class Welcome extends \Model {
public static function get_results()
{
// Database interactions
}
}
フォーム内容をDBへ登録する場合
SQLを書く
SQLをそのまま書くか、クエリビルダというものを使って書くかの2通り。
$query = DB::query('SELECT * FROM users WHERE id = 5');
$query->execute()->as_array();
※as_array()を使わないと中身が取り出せないので注意!!
$query = DB::select('title','content')->from('articles')->execute()->get('title');
※クエリビルダを使えば、自動でエスケープしてくれる
$query = DB::insert('table_name', array('id', 'name'));
$query = DB::count_records('users');
// 典型的なトランザクションコードの流れ
try
{
DB::start_transaction();
// 何らかのクエリ...
DB::commit_transaction();
// クエリの結果を返す
}
catch (Exception $e)
{
// 未決のトランザクションクエリをロールバックする
DB::rollback_transaction();
throw $e;
}
コントローラーからモデルへ
use \Model\Welcome; //useはjavaのimportみたいなもの
class Controller_Welcome extends Controller
{
public function action_index()
{
$results = Welcome::get_results();
}
}
ORMを使う
テーブル同士のリレーション(多対多、1対多など)を定義しておくことで、ひとつのテーブルを更新すると自動的に関連するテーブルも更新してくれたりする。
書き方
\Orm\Model
を継承したモデルクラスを作成し、fuel/app/classes/model/
配下に置く。
<?php
class Model_Contents1 extends \Orm\Model{ //\Orm\Modelを継承する
protected static $_table_name = 'contents1'; //テーブル名を指定
protected static $_primary_key = array('id'); //プライマリーキーを指定
protected static $_propeties = array( //カラム名を指定
'id',
'created_at',
'updated_at',
'erase',
'users_id',
'category_code_id',
'name',
'company',
'zip',
'pref',
'address',
'tel',
'email',
);
}
アプリのモジュール化
アプリの規模が大きくなる際などは、アプリの各機能を独立させモジュール化(部品化)してあげた方が開発効率や保守性も高まる。(詳しくはp.52)
APPPATH/
|---- modules/
|------- user/ ⬅userモジュール
| |----- classes/
| |----- controller/
| |----- model/
| |----- view/
|------- article/ ⬅articleモジュール
|----- classes/
|----- controller/
|----- model/
|----- view/
※modulesフォルダの中に各MVCのアプリがあるイメージ
http://localhost/user/register
------- --------
⬆ ⬆
モジュール名 コントローラ名
バリデーション
方法1:add_fieldメソッドを使う
※add_fieldメソッドは文字列の検証しか出来ない略式的なもの
$val = Validation::forge();
$val->add_field('name','名前','required');
$val->add_field('email','メアド','required | valid_email');
$val->add_field('password','パスワード','required | min_length[8] | max_length[12]);
$val->add_field('password2','パスワード(確認用)','required|match_field(password)');
'
//そのほかの規則はP141参照
//名前、メアドなどはエラーメッセージの時に使われるラベル
//最大、最小などを指定してもフォームを空欄で送信されたら、チェックにひっかからないので注意(requiredを指定しておく)
$out = '';
if($val->run()){ //runメソッドで検証実行
$out = '合ってます'; //検証成功時にする処理
}else{ //検証失敗時の処理
foreach($val->error() as $error){ //エラー取り出し
$out .= $error.'<br>'; //各項目のエラーを改行で繋げる
}
}
return Response::forge($out); //エラーをビューへ渡しながら表示
}
エラーメッセージの日本語化
1.APPPATH/config/config.phpで’language’を’ja’にする
2.APPPATH/lang/ja/vallidation.phpファイル作成
3.編集する
<?php
return array(
'requred' => ':labelがにゅうりょくされていまあせん。',
'min_length' => ':labelが:param:1文字未満です',
・・・
);
※「:label」の部分には指定したラベルの名前が入る
※「:param:x」x番目のパラメータを取得する
エラーメッセージの上書き
$val->set_message(‘required’,’:labelが入力されていません’);
方法2:add()とadd_rule()を使う
文字列以外のチェックも可能。
$val->add(‘password’,’ぱすわーど’)
->add_rule(‘required’)
->add_rule(‘min_length’,8)
->add_rule(‘max_length’,12)
->add_rule(‘valid_string’,array(‘alpha’,’numeric’,’dashes’,’utf8′));
※dashesは「ー」ハイフンと「_」アンダースコアを許可。
※utf8は正規表現のまっちんぐをutf8で行うことを指定。
※valid_stringの後の第2引数を指定しないと自動でarray(‘alpha’,’utf8′);が入る。
独自の検証規則を作成する
既に登録済みのemailでないかを調べる場合など
<?php
class MyValidation {
public static function _validation_unique_email($email){ //頭に必ず「_validation_」をつける
$result = DB::select('LOWER ("email")')
->where('email', '=', strtolower($email)) //emailを条件にDB検索
->from('users')->execute(); //usersテーブル内を検索
return !($result->count() > 0); //取得件数が0より多ければfalseを返す
}
}
$val->add_callable('myvalidation'); //myvalidationクラスを規則として読み込む
$val->add('email','メールアドレス')
->add_rule('required')
->add_rule('unique_email'); //myvalidationクラスのunique_emailのfunctionを規則に使う
$val->set_message('unique_email','既に登録されています');
エラーメッセージを毎回書くのが面倒ならメソッド側に設定する
class MyValidation {
public static function _validation_unique_email($email){ Validation:active()->set_message('unique_email','既に登録されています'); //Validationクラスを呼び出し、set_massageメソッドでメッセージを設定する。
$result = DB::select('LOWER("email")')
->where('email', '=', strtolower($email))
->from('users')->execute();
return !($result->count() > 0);
}
}
認証機能
SimpleAuthを使う。
Authパッケージを使う
使うにはconfig.phpにauthを追加する
'packages' => array(
'orm',
'auth',
),
テーブルを用意する
デフォルトでは、テーブル名はusersを想定していて、
id,username,password,group,email,last_login,login_hash,profile_fields,created_at
のカラムがあることが前提。
テーブル名だけは設定すれば変更可能。
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 DEFAULT 0,
`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`
)
)
ユーザ登録
$auth = Auth::instance();
$auth->create_user($_POST['username'], &_POST['password'], $_POST['email']);
※パスワードは自動でハッシュ化されて保存される
ログイン
$auth = Auth::instance();
if($auth->login($_POST['username'], $_POST['password'])){
//ログイン成功時の処理
}else{
//ログイン失敗時の処理
}
※ログインするとusersテーブルのlast_loginにログイン時刻、login_hashにタイムスタンプを基にしたハッシュ値が保存される。
さらに、セッションにユーザ名とログインハッシュ値が保存される。
ログアウト
$auth = Auth::instance();
$auth->logout();
ログイン状態のチェック
if(!Auth::check()){
Response::redirect('login'); //ログインしてなかったら、コントローラーloginへ遷移
}else{
$data = array();
$data['username'] = Auth::get_screen_name(); //ユーザ名を取得
$data['user_id'] = Arr:get(Auth::get_user_id(),1); //ユーザIDを取得
Return Response::forge(View::forge('member/top')); //コントローラーmemberのtopアクションへ遷移
}
ログイン保持(1週間ログインを保持するなど)をする
1.simpleauth.phpをコピーする
fuel/packages/auth/config/simpleauth.php
をfuel/app/config/
内へコピペ。
2.コピペしたsimpleauth.phpを編集する
'remember_me' => array(
/**
* Whether or not remember me functionality is enabled
*/
'enabled' => true, /* falseになってるので、trueへ変更する*/
/**
* Name of the cookie used to record this functionality
*/
'cookie_name' => 'rmcookie',
/**
* Remember me expiration (default: 31 days)
*/
'expiration' => 86400 * 31, /*クッキーの期限(ログイン保持の期限)を設定する*/
),
・
・
・
/**
* Salt for the login hash
*/
'login_hash_salt' => 'tekitounimojiwoireru', /* 暗号化するためのキー文字列。適当な文字列へ変更する*/
3.コントローラ内のログイン認証でOKだった場合の中で下記を記述する
$remember = Input::post('remember'); //保持のチェックボックスの値を取得
if($remember){
//保持にチェックがついていた場合
// remember-me クッキーを作成
Auth::remember_me();
}
ユーザグループの設定
1.PKGPATH/auth/config/simpleauth.phpの「groups」セクションにグループの定義を設定する
どのグループに属しているかを調べる場合
if(Auth::member(100)){ //100はAdministratorグループ
//処理
}
ユーザ認証ページの例
・ログインフォーム
・マイページ
・管理者ページ
APPPATH/
|———classes/
| |———controller/
| |———member.php ・・・①メンバーのベースコントローラ
| |———member/
| |———admin.php ・・・②管理者のベースコントローラ
| |———admin/
|———views/
|———member/
| |———form.php
| |———index.php
| |———template.php・・・③メンバーのビューテンプレート
|———admin/
|———index.php
|———template.php・・・④管理者のビューテンプレート
<?php
class Controller_Member extends Controller_Template{
public $template = ‘member/template’; //テンプレートファイルを指定
public $is_admin = false; //管理者かどうかフラグ
public function before(){
parent::before(); //before()をオーバーライドするので、親クラスのbefore()を呼び出す
if(!Auth::check() and Request::active()->action != ‘login’){ //認証通らず、現在リクエストされてるアクションがloginでない場合にログインフォームへリダイレクト
Response::redirect(‘member/login’);
}
if(Auth:member(100)){ //ユーザが管理者なら、管理者フラグ立てる
$this->is_admin = true;
}
View::set_global('is_admin',$this->is_admin); //ビューに管理者フラグを渡す
}
public function index(){
$this->template->title = 'ようこそ'.Auth::get_screen_name().'さん';
$this->template->content = View::forge('member/index');
}
public function action_login(){
//既にログイン済みなら会員トップへリダイレクト
Auth::check() and Response::redirect('member');
//usernameとpassが送信されていたなら、認証をする
if(Input::post('username') and Input::post('password')){
$username = Input::post('username');
$pass = Input::post('password');
$auth = Auth::instance();
//認証に成功したら会員トップページにリダイレクト
if($auth->login($username, $password)){
Response::redirect('member');
}
}
//ログインフォームの表示
$this->template->title = 'ログインページ';
$this->template->content = View::forege('member/form');
}
public function action_logout(){ //ログアウト
$auth = Auth::instance();
$auth->logout();
Response::redirect('member');
}
}
member用テンプレートビュー
<body>
<h1><?php echo $title; ?></h1>
//認証が通れば、ログアウト(aタグ)を表示する。
<?php if(Auth::check()):?>
<?php echo Html::anchor('member/logout','ログアウト'); ?>
<?php endif; ?>
//管理者フラグが立っていれば、管理者ページへのボタンを表示する
<?php if($is_admin): ?>
<a href="<?php echo Url::create('member/admin'); ?>">管理者ページへ</a>
<?php endif; ?>
<?php echo $content; ?> //コンテンツ差し替え用
</body>
ログインフォームのビュー
<?php echo Form::open(); ?> //送信先は自身のコントローラなので、引数はいらない
<fieldset>
<div>
<?php echo Form::label('ユーザ名','username'); ?>
<?php echo Form::Input('username'); ?>
</div>
<div>
<?php echo Form::label('パスワード','password'); ?>
<?php echo Form::password('password'); ?>
</div>
<div>
<?php echo Form::submit('submit','ログイン'); ?>
</div>
</fieldset>
<?php echo Form::close(); ?>
管理者用ページ用ベースコントローラ
<?php
class Controller_Member_Admin extends Controller_Member{
public $template = 'member/admin/template'; //テンプレートを指定
public function before(){
parent::before(); //親クラス呼び出し
if(!$this->is_admin){ //管理者フラグが立ってなければmemberコントローラへリダイレクト
Response::redirect('member')+
}
}
public function index(){
$this->template->title = Auth::get_screen_name();
$this->template->content = View::forege('member/admin/index');
}
}
Sessionクラス
定期的にセッションIDを自動変更してくれたり、次のリクエスト時に削除されるような一時的なセッションの発行もできる。
設定ファイル
初期設定ファイルの場所:COREPATH/config/session.php
初期設定ファイルを以下の場所にコピーして使用する。
コピー先の場所:APPPATH/config/
配下
設定詳細はP.169を参照。
基本は設定ファイル内の'expiration_time' => 7200
を変更するくらい。
これは、セッションの有効期限を設定するもの。
セッションの読み書き
書き込みはset()メソッド
//文字列を保存
Session::set('username','きむりおん');
//配列を保存
Session::set('loging', array('user'=>$userid, 'hash'=>$login_hash));
読み込みはget()メソッド
$username = Sessio::get('username');
※Auth認証を使ってるなら自動的にusernameやlogin_hashがSessionに保存されてるので、キーを指定すれば読み書き可能。
削除
Session::delete('username');
フラッシュセッション
フォーム送信時のチケット発行などに使用する。
フラッシュセッションはほかのセッションと名前が衝突しないように別の名前空間で管理されている。
識別子は設定ファイルのflash_id
で設定できる。
読み書き
set_flash(),get_flash()を使う。使い方は普通のセッションと同じ。
寿命を延ばす
フラッシュセッションを読み込まれていない状態に戻すことで、次のリクエストまで寿命が延びる。引数にはセッション変数名を指定する。
php:
Session::keep_flash('ticket');
削除
Session::delete_flash('ticket');
※セッション自体(普通のセッションやフラッシュセッション全て)を破棄するならSession::destroy();
を使う
ファイルアップロード
Uploadクラスを使う。
設定
Uploadクラスを使うには設定が必要。
デフォルトの設定はアプリ名/fuel/core/config/upload.php
に書かれてるので、アプリ名/fuel/app/config/
内にコピペして、必要な箇所のみ修正する。
'max_size' => 2000000, //アップロードのファイルサイズ上限(バイトで指定)
'path' => DOCROOT.'/uploads', //ファイルのアップロード先パスを設定しておく
'mime_whitelist' => array('jpg','jpeg','gif','png'), //許可するファイル種類
パスで指定した通り、/public/uploads
フォルダを作成しておく。
使い方
コントローラでまず、Upload::process()メソッドを実行し、ファイル検証やファイル情報の取得を行う。
$config = array(
`path` => DOCROOT . 'files . DS,
'ext_whitelist' => array('jpg','jpeg','gif','png'),
);
Upload::process($config);
if(Upload::is_valid()){ //検証に成功した場合
Upload::save(); //ファイルを保存する
$files = Upload::get_files(); //ファイル情報を取得する
}
※ファイル情報はUploadしたファイル数分の連想配列の中にそれぞれのファイル情報が入った連想配列がある形で返される
upload.phpの設定ファイルでauto_process
を有効にすると上記のUpload::processメソッドは呼びださなくても自動的に実行してくれる。
Uploadクラスの検証エラーメッセージを日本語化する
以下のようなファイルをAPPPATH/lang/ja/
内にupload.php
として保存すればOK。
<?php
return array(
'error_'.\Upload::UPLOAD_ERR_OK => 'ファイルのアップロードに成功しました',
'error_'.\Upload::UPLOAD_ERR_INI_SIZE => 'アップロードファイルのサイズが php.ini の upload_max_filesize ディレクティブ設定値を超えています',
'error_'.\Upload::UPLOAD_ERR_FORM_SIZE => 'アップロードファイルのサイズが HTML フォームの MAX_FILE_SIZE ディレクティブ設定値を超えています',
'error_'.\Upload::UPLOAD_ERR_PARTIAL => 'ファイルの一部しかアップロードされませんでした',
'error_'.\Upload::UPLOAD_ERR_NO_FILE => 'ファイルはアップロードされませんでした',
'error_'.\Upload::UPLOAD_ERR_NO_TMP_DIR => 'アップロード用一時フォルダの設定がありません',
'error_'.\Upload::UPLOAD_ERR_CANT_WRITE => 'アップロードファイルがディスクに書き込めませんでした',
'error_'.\Upload::UPLOAD_ERR_EXTENSION => 'インストールされている PHP の 拡張機能 (extension) によりアップロードが防止されました',
'error_'.\Upload::UPLOAD_ERR_MAX_SIZE => 'アップロードファイルのサイズが既定値上限を超えています',
'error_'.\Upload::UPLOAD_ERR_EXT_BLACKLISTED => 'この拡張子でのファイルアップロードは許可されていません',
'error_'.\Upload::UPLOAD_ERR_EXT_NOT_WHITELISTED => 'この拡張子でのファイルアップロードは許可されていません',
'error_'.\Upload::UPLOAD_ERR_TYPE_BLACKLISTED => 'このファイルタイプでのファイルアップロードは許可されていません',
'error_'.\Upload::UPLOAD_ERR_TYPE_NOT_WHITELISTED => 'このファイルタイプでのファイルアップロードは許可されていません',
'error_'.\Upload::UPLOAD_ERR_MIME_BLACKLISTED => 'この mime タイプでのファイルアップロードは許可されていません',
'error_'.\Upload::UPLOAD_ERR_MIME_NOT_WHITELISTED => 'この mime タイプでのファイルアップロードは許可されていません',
'error_'.\Upload::UPLOAD_ERR_MAX_FILENAME_LENGTH => 'アップロードファイル名の長さが既定値上限を超えています',
'error_'.\Upload::UPLOAD_ERR_MOVE_FAILED => 'アップロードファイルを一時フォルダから指定フォルダに移動できません',
'error_'.\Upload::UPLOAD_ERR_DUPLICATE_FILE => 'アップロードファイルと同じ名前のファイルが既に存在します',
'error_'.\Upload::UPLOAD_ERR_MKDIR_FAILED => 'アップロード先の指定フォルダが作成できません',
);
ログについて
ログを出力したい場合、app/config/config.php
内の下記を変更する。
'log_threshold' => Fuel::L_DEBUG, //ログレベルを指定
'log_path' => APPPATH.'logs/', //ログ出力フォルダの指定
'log_date_format' => 'Y-m-d H:i:s', //出力されるログの日付フォーマットを指定
'date_default_timezone' => 'Asia/Tokyo' //ログの日付を日本時間にする
ログレベルは下記の5段階ある。下に行くほど多く出力される
Fuel::L_NONE //何も出力しない
Fuel::L_ERROR //phpでのエラーレベルまでのログを出力
Fuel::L_WARNING //phpでの警告レベルまでのログを出力
Fuel::L_DEBUG //Log::debugの内容まで出力
Fuel::L_ALL //全部出力
デバッグ用のログを出力したい場合
下記のメソッドをコントローラー内などで使う。(ログレベルをFuel::L_DEBUGにしてると出力される)
Log::debug('ログイン処理が成功した場合');
created_atとupdated_atをdatetimeで保存する
FuelPHPはテーブルカラムのcreated_at と updated_at をデフォルトUNIX タイムスタンプで記録するが、datetimeで保存することも可能。
mysql_timestamp のデフォルト値「false」を「true」にすればOK。
protected static $_observers = array(
'Orm\Observer_CreatedAt' => array(
'events' => array('before_insert'),
'mysql_timestamp' => true,
'property' => 'created',
),
);
protected static $_observers = array(
'Orm\Observer_UpdatedAt' => array(
'events' => array('before_save'),
'mysql_timestamp' => true,
'property' => 'updated',
),
);
FuelPHP で自作クラスを自分で作りたい場合
「extends Controller」で継承したコントローラーではなく、普通にクラスを作りたい場合は下記のようにする
①./fuel/app/classes/ 以下に自作クラスを置く
<?php
class Sample {
public static function test($var)
{
return $var . ' test.';
}
}
みたいに置けば使える。
呼び出す側の書き方は下のようにやる。
<?php
class Controller_Welcome extends Controller
{
public function action_index()
{
$cls = Sample::test('This is'); // クラス名::ファンクション名(引数)で実行
・
・
・
}
}
Auth認証でユーザー登録を行う際にEmail重複やusername重複を無くしたい場合(クラスの拡張)
ユーザー登録時、Authでやるとユーザー名は重複できない設定のため、email と passのみでユーザー登録したい場合には面倒。(username固定にしようとするとエラーになる。)
モデルを作って自前でDBへinsertするやり方だとパスワードのハッシュ化が必要になる。
自前でパスワードハッシュを作った場合、ログイン時にAuthでやった時にハッシュ値が違うのでエラーになる。
なので、Auth認証の時に呼び出されているSimpleAuthをコピペして編集し、そっちを読み込ませる方法を使う。
手順
1.simpleauth.phpのコピペ。編集。
2.bootstrap.phpのコピペ。編集。
3.config.phpの編集。
simpleauth.phpのコピペ。編集。
まず、Auth認証時に呼び出されているクラスは下記の場所にあるので、これをコピー。
/auth/classes/auth/login/simpleauth.php
下記のようにフォルダを作り、そこにペースト。
/myauth/classes/auth/login/
中身を下記のように編集。
namespace Auth;
中略
class Auth_Login_SimpleAuth extends \Auth_Login_Driver
{
中略
public function create_user($username, $password, $email, $group = 1, Array $profile_fields = array())
{
$password = trim($password);
$email = filter_var(trim($email), FILTER_VALIDATE_EMAIL);
if (empty($username) or empty($password) or empty($email))
{
throw new \SimpleUserUpdateException('Username, password or email address is not given, or email address is invalid', 1);
}
$same_users = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
->where('username', '=', $username)
->or_where('email', '=', $email)
->from(\Config::get('simpleauth.table_name'))
->execute(\Config::get('simpleauth.db_connection'));
if ($same_users->count() > 0)
{
if (in_array(strtolower($email), array_map('strtolower', $same_users->current())))
{
throw new \SimpleUserUpdateException('Email address already exists', 2);
}
else
{
throw new \SimpleUserUpdateException('Username already exists', 3);
}
}
以下省略
↓↓↓↓
namespace MyAuth;
中略
class Auth_Login_SimpleAuth extends \Auth\Auth_Login_SimpleAuth{
中略
public function create_user($username, $password, $email, $group = 1, Array $profile_fields = array())
{
$password = trim($password);
/*1カ所目:Email形式かどうかの判定なので、バリデーションクラスでやる場合は不要。削除。
$email = filter_var(trim($email), FILTER_VALIDATE_EMAIL);
*/
/*2カ所目:emptyチェックなので、バリデーションクラスでやる場合は不要。削除。
if (empty($username) or empty($password) or empty($email))
{
throw new \SimpleUserUpdateException('Username, password or email address is not given, or email address is invalid', 1);
}
*/
/*3カ所目:usernameとemailを元にDB検索し同じユーザがいたらエラーにしている。
email重複チェックをバリデーションクラスでやる場合 and username重複はOKにしたい場合は不要。削除。
$same_users = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
->where('username', '=', $username)
->or_where('email', '=', $email)
->from(\Config::get('simpleauth.table_name'))
->execute(\Config::get('simpleauth.db_connection'));
if ($same_users->count() > 0)
{
if (in_array(strtolower($email), array_map('strtolower', $same_users->current())))
{
throw new \SimpleUserUpdateException('Email address already exists', 2);
}
else
{
throw new \SimpleUserUpdateException('Username already exists', 3);
}
}
*/
以下省略
bootstrap.phpのコピペ。編集。
下記のbootstrapをコピー。
/auth/bootstrap.php
下記フォルダ内にペースト。
/myauth/
中身を下記のように編集。
Autoloader::add_core_namespace('Auth');
Autoloader::add_classes(array(
'Auth\\Auth' => __DIR__.'/classes/auth.php',
'Auth\\AuthException' => __DIR__.'/classes/auth.php',
'Auth\\Auth_Driver' => __DIR__.'/classes/auth/driver.php',
'Auth\\Auth_Acl_Driver' => __DIR__.'/classes/auth/acl/driver.php',
'Auth\\Auth_Acl_SimpleAcl' => __DIR__.'/classes/auth/acl/simpleacl.php',
'Auth\\Auth_Group_Driver' => __DIR__.'/classes/auth/group/driver.php',
'Auth\\Auth_Group_SimpleGroup' => __DIR__.'/classes/auth/group/simplegroup.php',
'Auth\\Auth_Login_Driver' => __DIR__.'/classes/auth/login/driver.php',
'Auth\\Auth_Login_SimpleAuth' => __DIR__.'/classes/auth/login/simpleauth.php',
'Auth\\SimpleUserUpdateException' => __DIR__.'/classes/auth/login/simpleauth.php',
'Auth\\SimpleUserWrongPassword' => __DIR__.'/classes/auth/login/simpleauth.php',
));
↓↓↓↓
<?php
Autoloader::add_core_namespace('MyAuth'); //1箇所目:AuthをMyAuthへ変更
Autoloader::add_classes(array( //2箇所目:arrayの中身1行を残し、ほかは削除。キー名の先頭をAuthからMyAuthへ。
'MyAuth\\Auth_Login_SimpleAuth' => __DIR__.'/classes/auth/login/simpleauth.php',
));
config.phpの編集
/fuel/app/config/config.php
にmyauthパッケージを読みこむように設定を追加する。
/**************************************************************************/
/* Always Load */
/**************************************************************************/
'always_load' => array(
/**
* These packages are loaded on Fuel's startup.
* You can specify them in the following manner:
*
* array('auth'); // This will assume the packages are in PKGPATH
*
* // Use this format to specify the path to the package explicitly
* array(
* array('auth' => PKGPATH.'auth/')
* );
*/
'packages' => array(
//'orm',
'auth',
'myauth' //⇐myauthを追加
),
よく使いそうなメソッド
ログインしてるユーザのユーザID取得
list(, $userid) = Auth::get_user_id();
$email = Auth::get_email();
Ajaxでファイルアップロード(画像アップロード)
Restコントローラを使う。
<?php
class Controller_Upld extends Controller_Rest{
public function post_img(){
Log::debug('画像アップロード');
Log::debug('======================================================== (C1) ファイルバリデーションチェック');
if(Upload::is_valid()){ //検証に成功した場合
Log::debug('=================================================== (CA) チェック成功の場合');
Log::debug('=============================================== (Ca) ファイルを保存');
Upload::save();
Log::debug('=============================================== (Cb) ファイル情報を取得');
$files = Upload::get_files();
Log::debug('=============================================== (Cc) エラーフラグに0追加');
$files += array('error_flg' => 0); //エラーフラグfalse
}else{
Log::debug('=================================================== (CB) チェック失敗の場合');
Log::debug('=============================================== (Ca) エラーメッセージ取得');
foreach (Upload::get_errors() as $file) //エラーメッセージ取得
{
if(!empty($file)){
Log::debug('==========エラーあり');
foreach($file['errors'] as $key => $val){
if($key == 'message' && !empty($val)){
Log::debug('==========エラーメッセージ取得');
$files['message'] = $val['message']; //メッセージを格納
$files += array('error_flg' => 1); //エラーフラグtrue
}
}
}
}
}
return $files;
}
}
?>
//Ajaxで画像選択時にアップロード
$('.ajax-upload').change(function(e){
var name = $(this).attr('name');
console.log('変更されたフォームのname属性:'+name);
name = '#'+name;
var file = $(this).prop('files')[0];
var img_url = file.name;
console.log('選択後ファイル名:' + img_url);
var filedata = new FormData(); //FormDataオブジェクトを使ってfileデータを送る
filedata.append("files", file);
$.ajax({
url: "<?php echo Uri::create('/upld/img.json'); ?>", //相対パスだと画面によってパスが違ってしまうので、Uri::create()を使う
type: "post",
processData: false,
contentType: false,
data: filedata,
}).done(function(json){
console.log('ok');
$(name).attr('src','<?php echo Uri::base(false).'assets/img/../uploads/';?>'+img_url);
}).fail(function(jqXHR, textStatus){
alert('Error!');
});
});
アプリ制作時に注意する点
ユーザ登録をemailとパスのみ(usernameは使わない)でやる場合
FuelPHPのSimpleAuthはデフォルトでは、usernameとpasswordでユーザ登録し、ユーザー情報もusernameとpasswordを元にDBから取得したりしているので、usernameをgestなど重複するものにして登録してしまうと、ログインした際にusernameが重複している他のユーザーのデータも書き換えてしまう&他のユーザーの情報を取得してしまったりする。
なので、’/fuel/packages/auth/classes/auth/login/simpleauth.php’内のusernameを使っているところをemailなどに置き換える必要がある。
(直で書き換えずにコピペしてやった方がいいけども)
サーバーにUPする
ロリポップレンタルサーバーにUPする例
500エラーが起きる場合
プロジェクト名/public/.htaccess
のOptions +FollowSymLinks -Indexesをコメントアウト
publicのみを公開されている場所に置き、それ以外のファイルは外部からは見えない場所におく必要があります(セキュリティのため)。
さくらのレンタルサーバーでは、www が apacheのドキュメントルートになっていて、これを変更することができません。
そのため、publicだけはwww以下において、それ以外はwww外に置きます。
まとめると以下のようになります。
- ホームディレクトリ直下にfuelphpを解凍
/home/user-name/fuelphp/fuel
- fuelphp/public ディレクトリを www以下に移動
/home/user-name/www/public
- public/index.php のパスを変更にします。
/../fuel の部分を
/../../fuelphp/fuel
に変更します。
publicが邪魔な場合
1.publicの中身を全部、publicの上層に移動させる。
2.さっきの3. public/index.php のパスを変更にします。はやらない。