FuelPHPメモ

#バリデーションの表示言語を日本語に設定する
1.`classes/lang/`以下に`jaというフォルダを作成する`
2.`ja`の中に`validation.php`というファイルを作成する
3.`fuel/app/config/config.php`でバリデーションの文字を日本語に設定

```php:fuel/app/config/config.php
/**
 * Localization & internationalization settings
 */
'language'           => 'ja', // ここがデフォルトではenなのでjaに設定(1.で作ったフォルダ名と同じならじゃなくても良い)
// 'language_fallback'  => 'en', // Fallback language when file isn't available for default language
// 'locale'             => 'en_US', // PHP set_locale() setting, null to not set
```

4.以下のように出力したいバリデーションメッセージを設定

```php:classes/lang/ja/validation.php
<?php

return array(
	'required'        => ' :label 欄は必須です。',
	'min_length'      => ' :label 欄は :param:1 文字以上にしてください。',
	'max_length'      => ' :label 欄は :param:1 文字を超えないようにしてください',
	'exact_length'    => 'The field :label must contain exactly :param:1 characters.',
	'match_value'     => 'The field :label must contain the value :param:1.',
	'match_pattern'   => 'The field :label must match the pattern :param:1.',
	'match_field'     => 'The field :label must match the field :param:1.',
	'valid_email'     => 'The field :label must contain a valid email address.',
	'valid_emails'    => 'The field :label must contain a list of valid email addresses.',
	'valid_url'       => 'The field :label must contain a valid URL.',
	'valid_ip'        => 'The field :label must contain a valid IP address.',
	'numeric_min'     => 'The minimum numeric value of :label must be :param:1',
	'numeric_max'     => 'The maximum numeric value of :label must be :param:1',
	'numeric_between' => 'The field :label must contain a numeric value between :param:1 and :param:2',
	'valid_string'    => 'The valid string rule :rule(:param:1) failed for field :label',
	'required_with'   => 'The field :label must contain a value if :param:1 contains a value.',
	'valid_date'      => 'The field :label must contain a valid formatted date.',
);
```

#CSRF対策
データを送信するformに以下を記述する

```
<?php echo Form::csrf(); ?>
```

POST値取得側でtokenのチェックを行う

```
if ( !Security::check_token() ) {
    echo "ページ遷移が不正です";
}
```

#例外を自分で設定する
FuelPHPには404 Not Found用 HttpNotFoundExceptionクラスと500エラー用のHttpServerErrorExceptionクラスがある
自作のエラー処理クラスを作成したい場合はHttpExceptionクラスを継承する

1.自作エラー処理関数を作成

```php:classes/myexception.php
class MyHttpException extends HttpException
{
    public function response()
    {
        return \Request::forge('error/invalid')->execute([$this->getMessage()])->response();
    }
}
```

2.エラー表示画面の作成

```php:classes/controller/error.php
class Controller_Error extends \Controller_Template
{
    public function action_invalid($message = null)
    {
        if ( $message === null ) {
            return '不正なデータです';
        } else {
            return e($message);
        }
    }
}
```

3.自作エラー処理を使う

```php:どこかのController
    throw new MyHttpException('正しいページ遷移で来てください');
```

これはFuelPHPのHMVCという機能を使用したパターンです
自分のアクション以外のアクション結果を返せます
1.のforgeで使用するページを指定します ('error/invalid') invalidへのルーティングが必要な時は追加しておきましょう
executeで'正しいページ遷移で来てください'というメッセージを引数にとってinvalidにリクエストを送ります
その結果をresponseで取得し表示します



#modules以下のclassesのパスについて

```
modules/user/classes/controller
                    /model
                    /view
modules/user/classes/
```

一番下に記述しているclasses直下にファイルを作成した時
checkvalidate.phpとかを作成した場合

テストに記述するパスは
`user\CheckValidateになる`


#異なるフォームの値が同じかどうかのバリデーションを掛けたい時
add_ruleでmatch_fieldを使う

```php
// メールアドレス入力
$val->add('email', 'email')
    ->add_rule('required')
    ->add_rule('match_field', 'confirm_email');

// 確認用メールアドレス入力
$val->add('confirm_email', 'confirm_email')
    ->add_rule('required');
```

emailとconfirm_emailの入力が一致していない場合は警告が出る


#カスタム(独自)バリデーションの作成
作成手順
1. カスタム(独自)バリデーションクラスの作成
2. Validation::forgeのadd_rulesメソッドでカスタムバリデーションを追加
3. 2.で追加したバリデーションにaddcallableメソッドでカスタムバリデーションクラスを追加
4. `lang/~/validation.php`にバリデーション追加
の手順

1.カスタムバリデーションクラスの作成
規約としてバリデーションメソッド名は「_validation_」で始めないといけません

```php:custom_validation.php
class CustomValidation
{
    // 値が5の時だけバリデーションを通す
    public static function _validation_number_five($value)
    {
        if ( $value === 5 ) return true;
        else                return false;
    }
}
```


2.Validation::forgeのadd_rulesメソッドでカスタムバリデーションを追加

```php:form.php
    public function action_input_five()
    {
        $val = \Validation::forge();
        $val->add('number', '数字')
            ->add_rule('required')
            ->add_rules('number_five'); // バリデーション追加
    }
```

3.2.で追加したバリデーションにaddcallableメソッドでカスタムバリデーションクラスを追加

```php:form.php
    public function action_input_five()
    {
        $val = \Validation::forge();
        $val->add('number', '数字')
            ->add_rule('required')
            ->add_rules('number_five')
            ->addcallable('CustomValidation'); // カスタムバリデーションクラスを追加 ※多分出来る
    }
```

4.`lang/~/validation.php`にバリデーション追加

```php:lang/~/validation.php
<?php

return array(
	'required'     => ':labelは必須です。',
	'min_length'   => ':labelは:param:1文字以上にしてください。',
	'number_five'  => ':labelは5しか認めません。', // バリデーション名とメッセージ追加
);
```



#パッケージを読み込みたいとき
パッケージ名はpackages/ 下のフォルダ名だと思う

```php
\Package::load('パッケージ名');
```


#コンフィグを読み込みたいとき

```
\Config::load('ファイル名',true);
```


#oil generate adminで管理画面を作成し、生成されたcontrollerをmodules以下のディレクトリに移してにlogin画面を開こうとした場合にリダイレクトループになる現象を回避する方法

ここのソースです
[controllers/admin.php](https://github.com/fuel/oil/blob/1.8/develop/views/admin/crud/controllers/admin.php#L11 "controllers/admin.php")

```php:admin.php
Request::active()->controller !== 'Controller_Admin' or ! in_array(Request::active()->action, array('login', 'logout'))
```

このコードの前で`\Request::active()->controller`と`\Request::active()->action`を出力して値を確認しましょう

もしファイルを`app/modules/mydirectory/classes/controller`ディレクトリを移した場合


本来`Controller_Admin`という値が取得されるはずが`mydirectory\Controller_Admin`となっていてif文の条件で無限にfalseと判定され続ける
ので

```php
\Request::active()->controller !== 'Controller_Admin'
```

を

```php
\Request::active()->controller !== 'mydirectory\Controller_Admin'
```

みたいに変更すればログイン画面に遷移できるはず



#modulesの中のモデルのリレーションを設定したい時に、別モデルを指定する

`model_to`の中を単純にモデル名だけ指定すると、そんなモデルは見つからないと怒られます

```
'model_to' => 'Model_Category',
```

ので以下のように`namespace\モデル名`とする

```php:モデル.php

    protected static $_belongs_to = array(
        'trn_faq_category' => array(
            'key_from' => 'faq_category_id',
            'model_to' => 'backend\Model_FaqCategory',
            'key_to' => 'id',
            'cascade_save' => true,
            'cascade_delete' => false,
        )
    );
```


#モデルのリレーションにbelongを追加するときは対応するモデルにhas_oneを追加しなければいけない



#モデル全体で物理削除をしたくないときはOrm\Modelを継承するのではなくOrm\Model_Softを継承する

[ソフトモデル](http://fuelphp.jp/docs/1.7/packages/orm/model/soft.html "ソフトモデル")


#ORMでクエリを発行した際のクエリを表示するメソッド

```php
DB::last_query();
```


#ORMのrelatedでjoinsしたテーブルの条件を検索条件に使う場合

```php
 ->where('リレーション.カラム名', $val)
```

#relationで取得したオブジェクトからjoinしたデータを取得する

```php
$related_user = ->related('game')
$related_user->game->score;
```

#ORMで取得するカラムを指定する場合

```php
->select('id', 'status_type')
```

#指定条件で1件のみデータを取得する

```php
public static function get_first_user_by_search_criteria($search_data = [])
{
    $user_info = self::query()
                      ->related('user_status')
                      ->where($search_data)
                      ->get_one();
    
    return !empty($user_info) ? $user_info : [];
}
```

#ORMqueryのwhereに連想配列で検索条件のカラムを入れる時のrelationしているテーブルのカラム指定方法

例えば以下の関係(親テーブルはuserとする)の時に

```php
    $user_info = self::query()
                      ->related('user_status')
                      ->where($search_data)
                      ->get_one();
```

whereの条件を`user_status.birthday`としたい場合
$search_dataに入れる時の記述方法は

```
    [
        `user_status.birthday` => $date
    ]
```

となる

もしuser_statusに関連するuser_detailのようなテーブルがあった場合
その指定方法は

```
    [
        `user_status.user_detail.hoby` => $hoby
    ]
```

のように
`リレーションテーブル.その先のリレーションテーブル.カラム`

となる
リレーションが深くなっても`.`で繋げば条件を指定できる

#コアクラスを拡張する

[コアクラスの拡張](http://fuelphp.jp/docs/1.7/general/extending_core.html "コアクラスの拡張")

コアクラスを継承する時は同じクラス名で継承した後、`fuel/app/bootstrap.php`のAutoloaderに継承したファイルを追記しなければならない

例えば`Crypt`クラスを拡張したいとして

`fuel/app/classes/mycrypt.php`を作成したとする

```php:fuel/app/classes/mycrypt.php
<?php

// Cryptという同じ名前で継承
class Crypt extends \Fuel\Core\Crypt
{

    // 追加した関数
    public static function ee($str)
    {
        echo $str . "\n";
    }

}
```

eeというメソッドを追加しました(適当
クラスの拡張は済んだので次は`bootstrap.php`の編集

```php:fuel/app/bootstrap.php
\Autoloader::add_classes(array(
	// Add classes you want to override here
	// Example: 'View' => APPPATH.'classes/view.php',
	
    // ここに追記
    // '拡張クラス名' => 
    'Crypt' => APPPATH.'classes/mycrypt.php',
));
```


これで

```php
Crypt::ee("拡張成功");
```

とかすればeeメソッドが使用できると思います

元の`Crypt`クラスを使いたい場合は

```
\Fuel\Core\Crypt::encode("~~");
```

みたいにパスを指定してあげれば使えます




#ViewでCSSや画像の読み込み

Assetクラスを使う
`public/asset`以下のファイルを読み込む場合は

```php
<?php echo Asset::css("bootstrap.min.css"); ?>
```

のように`Asset::css`を使う

`public/css/css2/`以下のファイルを読み込む場合は

```php
<?php echo Asset::css("css2/bootstrap.min.css"); ?>
```

ディレクトリを追加しましょう



#Authコマンドの実行

```php
oil console
>>> Auth::create_user('admin', 'password', 'example@co.jp', 100);
```


#既にログインしている状態なので未ログイン時に見せるページを表示させたくない場合
例えばログインしていない時に見せたいページ`https://myservice/notlogin`にログインしているユーザが遷移してきた場合、ログイン状態の初期ページに飛ばしたい
`https://myservice/top`とか

```php
// redirectでtopに飛ばす
\Auth::check() and \Response::redirect('top');
```


#Modelでsaveした時の挙動

```php
$user = Model::forge();
$user->name = 'バイバイまさや選手権';

$res = $user->save()

var_dump($res);
var_dump($user);
```

ここの`$res`はbool型でinsertに成功しかたの結果を返します。
次に`$user`はそのモデルのプロパティが入ります、`id`がauto_incrementだったら勝手にidが入ってくれます。


#DBトランザクションを実現する

```php
try
{
    DB::start_transaction();

    // 何らかのクエリ...

    DB::commit_transaction();
    // クエリの結果を返す
}
catch (Exception $e)
{
    // 未決のトランザクションクエリをロールバックする
    DB::rollback_transaction();
    throw $e;
}
```

#トレイトを使用する

手順は
1. トレイトを定義する
2. bootstrap.phpにロードするファイルを追記
3. 実際に使う

modules下とかclasses下のモデルで定義する場合によって微妙に変わるかもしれませんが
今回は`fuel/app/classes/model/model_base.php`にトレイトクラスを定義しました



1.トレイトを定義する

```php:fuel/app/classes/model/model_base.php
<?php

trait ModelBase
{

    public function getValue($column)
    {
        return $this->$column;
    }

    public function setValue($column, $value)
    {
        $this->$column = $value;
    }
    
}
```

2.bootstrap.phpにロードするファイルを追記

```php:fuel/app/bootstrap.php
<?php
// Bootstrap the framework DO NOT edit this
require COREPATH.'bootstrap.php';

\Autoloader::add_classes(array(
	// Add classes you want to override here
));

// Register the autoloader
\Autoloader::register();

\Fuel::load(APPPATH.DS.'classes/model/model_base.php');
```

3.実際に使う

トレイトのメソッドを使いたいクラス内で使えるように宣言します

```php:fuel/app/classes/model/hogemodel.php
<?php

class Model_Hoge extends \Orm\Model
{

    // 使うトレイトを宣言
    use \ModelBase;

}

$hoge = Hoge()::forge();
$hoge->getValue('name');
```

#moduleのconfig下にコンフィグファイルを置いてそれを読み込みたい場合の設定

`fuel/app/config/config.php`の`always_load`の`config`に読み込ませたいmoduleとconfigファイルを指定する

```php:fuel/app/config/config.php
'always_load'  => array(
    'config' => array(
        'app',
        'const',
        'CustomModule::customauth' => 'customauth', // ここがコンフィグ読み込ませる設定
    ),
),
```

`\Config::get`で使う場合は

```php
\Config::get('customauth.~~~')
```

のように使える

[FuelPHP で modules 利用時の config 設定について整理](http://qiita.com/hmukaida/items/0c2ce570a7ef0a1a6c67 "FuelPHP で modules 利用時の config 設定について整理")


#Sessionの有効期限を設定しているファイルと変数

まずファイルは
`fuel/core/config/session.php`

```php:fuel/core/config/session.php
// 変数はこれ
    // session expiration time, <= 0 means 2 years! (optional, default = 2 hours)
    'expiration_time' => 7200,
```

秒単位での設定なのでセッションを3秒で切りたいときは3、1時間に設定したいときは3600とかに設定しましょう


#ローカルでは動作していたが、検証用サーバーに持っていくとクラスが見つからないという旨のメッセージを受けた場合
その呼び出し元PHPファイル名に大文字が入っていないか見る

FuelPHPのファイル名は全て小文字だが、大文字小文字を解決できるファイルシステムだと大文字でも通ってしまって本番などにあげたときに動かなくなることがある

#Undefined variable: titleのようなエラー
上記のようなエラーが出て、エラー箇所が$this->template->titleみたいなテンプレート関連だったら
まずはそのコントローラがController_Templateなどを継承しているか確認する
templateが使えないので使えるようにしていけば良い

Controller_Templateを継承したクラスを継承してコントローラを実装した場合before()などでparent::before();などをするように


#1つのルーティングされているアクションで2つのルーティングされていないアクションを呼び出す

例えば1つのルーティングだけどログイン状態によって表示したいViewや処理を変更したい時に、全ての処理を1つのアクション内に突っ込むのはダサいので違うアクションを呼んで表示項目を変更しようという目的

ルーティングの設定

```php:routing.php
    '_root_' => 'top/index'
```

ユーザーがアクセスするルーティングされているアクション

```php:Controller_Top.php
    public action_index()
    {
        if ( self::isLogin() ) {
            // forgeにfalseをつけることでルーティングに設定されていないアクションを呼び出せる
            // その時の指定パスはルーティング定義の'' => 'こっちに書く方' を指定する
            $this->template->content = \Request::forge('top/top', false)->execute()->response();
        } else {
            $this->template->content = \Request::forge('guest/top', false)->execute()->response();
        }
    }
```

2つのルーティングされていないアクション

```php:Controller_Top.php
    public function action_top()
    {
        $values = [
            'posts' => [],
            'errors' => [],
        ];
        $this->template->title = 'あなたはログインしていますね。';
        $this->template->content = \View::forge('top/index', $values);
    }
```

```php:Controller_Guest.php
    public function action_top()
    {
        $values = [
            'posts' => [],
            'errors' => [],
        ];
        $this->template->title = 'あなたはログインしていません。';
        $this->template->content = \View::forge('guest/index', $values);
    }
```


1つ目のアクションは別々のアクションを呼び出すための振分け役で、他のルーティングされていないアクションが表示する本体
ルーティングされていないアクションはURLからはアクセス出来ない

URLからアクセスさせないようにするのはルーティングに書かないのが一番いいが、`\Request::is_hmvc()`という関数を使えばURLからアクセスされた場合とアクションから呼び出された場合の処理を振分けることが出来る

```php
    if(\Request::is_hmvc())
    {
        // HMVCだった場合の処理
    }
    else
    {
        // それ以外
    }
```

#TESTを実行するときに`Could not find asset: bootstrap.css`のようなエラーを吐く場合の対策

解決策としては`fuel/app/config/`以下に`asset.php`を配置して`'fail_silently' => true,`を記述する

※module用の設定ファイルは`fuel/app/モジュール名/asset.php`


```php:fuel/app/config/asset.php
return [
    'fail_silently' => true, // Test時のファイル名解決できない問題を解消する
];
```


#TESTクラスに自作のメソッドをtraitで追加したい時

1. Traitを作成
2. クラスの外でuse宣言
3. テストメソッド内でtraitのメソッドを使う

2.の宣言方法はこんな感じ
useをクラス内で宣言出来なかった

```
use path\MyTestTrait as MyTestTrait;
class ~~~~_Test extends \TestCase
{
    public function test_myprocedure()
    {
        MyTestTrait::originalMethods();
    {
}
```


#Fuel標準ではないモジュールをcomposerインストールした時のクラスの使い方

composer updateした後にvendor下に指定のライブラリが入っていることを確認する
基本的に外部ライブラリは使うときにuseなどで読み込む必要な無い

`new \GuzzleHttp\Client([`みたいに


#fuel/coreを削除したコミットをして、その後にcomposer.phar updateをかけるが上手くファイルが落ちてこない時の確認項目とその後のインストール

まず`fuel/core`や`fuel/package`下の各ディレクトリが空になっていないか確認する
ここにディレクトリが存在しているとcomposer.phar updateなどをした時にディレクトリが存在すると勘違いされその下のファイルが入ってこない
ので
```
rm -rm fuel/core
rm -rm fuel/package/~~~
```

を実行してから
ファイルを取得するために

```
php ./composer.phar install
```

これで無くなったファイルが再び入ってくる