FuelPHPでSimpleauthを利用してアカウント登録機能を作る

# 新規登録機能の実装

FuelphpのAuthパッケージというものを利用して、新規登録の認証機能を作ってみました。
実装ではAuthパッケージにおける「Simpleauth」というものを使いました。
開発環境は以下の通りです。

- PHP 7.3
- FuelPHP 1.8.2
- MAMP


ここではざっくり以下を目標に機能の実装を行っていきます。
1. パスワード確認用フォームの作成
2. エラーメッセージの日本語設定
3. usernameの重複を許可する
4. emailの重複は許可しない

### ビューの作成
入力フォームを作成します。

```
<div class="row" style="padding: 2rem;">
  <?php echo Form::open(['action' => 'home/signup', 'method' => 'post']); ?>
  <div class="form-group">
    <?php echo Form::label('ユーザー名','username'); ?>
    <?php echo Form::input('username', null, ['id' => 'username', 'class' => 'form-controll']); ?>
  </div>
  <div class="form-group">
    <?php echo Form::label('メールアドレス','email'); ?>
    <?php echo Form::input('email', null, ['id' => 'email', 'class' => 'form-controll']); ?>
  </div>
  <div class="form-group">
    <?php echo Form::label('パスワード','password'); ?>
    <?php echo Form::password('password', null, ['id' => 'password', 'class' => 'form-controll']); ?>
  </div>
  <div class="form-group">
    <?php echo Form::label('パスワード確認用','password-confirmation'); ?>
    <?php echo Form::password('password-confirmation', null, ['id' => 'password-confirmation', 'class' => 'form-controll']); ?>
  </div>
  <?php echo Form::button('新規登録', null, ['class' => 'btn btn-primary']); ?>
  <?php echo Form::close(); ?>
</div>
```

### simpleauth.phpのコピペ、編集
`packages/auth/classes/auth/login/simpleauth.php`
をコピーして
`packages/myauth/classes/auth/login`
とフォルダを作り、ペースト
中身の`create_user()`の一部を以下のようにコメントアウトします。

```
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);
		// 	}
		// }

		$user = array(
			'username'        => (string) $username,
			'password'        => $this->hash_password((string) $password),
			'email'           => $email,
			'group'           => (int) $group,
			'profile_fields'  => serialize($profile_fields),
			'last_login'      => 0,
			'login_hash'      => '',
			'created_at'      => \Date::forge()->get_timestamp(),
		);
		$result = \DB::insert(\Config::get('simpleauth.table_name'))
			->set($user)
			->execute(\Config::get('simpleauth.db_connection'));

		return ($result[1] > 0) ? $result[0] : false;
	}
    
```
詳しい説明などは以下の記事を参照：
https://qiita.com/kazukichi/items/2a6e242091c5f485b976#auth%E8%AA%8D%E8%A8%BC%E3%81%A7%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E7%99%BB%E9%8C%B2%E3%82%92%E8%A1%8C%E3%81%86%E9%9A%9B%E3%81%ABemail%E9%87%8D%E8%A4%87%E3%82%84username%E9%87%8D%E8%A4%87%E3%82%92%E7%84%A1%E3%81%8F%E3%81%97%E3%81%9F%E3%81%84%E5%A0%B4%E5%90%88%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E6%8B%A1%E5%BC%B5

### bootstrap.phpのコピペ、編集
`packages/auth/bootstrap.php`
を
`packages/myauth/`
へコピペし

中身を以下のように編集します。

```
\Autoloader::add_core_namespace('MyAuth');

\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_Opauth'                 => __DIR__.'/classes/auth/opauth.php',

	// 'Auth\\Auth_Acl_Driver'             => __DIR__.'/classes/auth/acl/driver.php',
	// 'Auth\\Auth_Acl_Simpleacl'          => __DIR__.'/classes/auth/acl/simpleacl.php',
	// 'Auth\\Auth_Acl_Ormacl'             => __DIR__.'/classes/auth/acl/ormacl.php',

	// 'Auth\\Auth_Group_Driver'           => __DIR__.'/classes/auth/group/driver.php',
	// 'Auth\\Auth_Group_Simplegroup'      => __DIR__.'/classes/auth/group/simplegroup.php',
	// 'Auth\\Auth_Group_Ormgroup'         => __DIR__.'/classes/auth/group/ormgroup.php',

	// 'Auth\\Auth_Login_Driver'           => __DIR__.'/classes/auth/login/driver.php',
	'MyAuth\\Auth_Login_Simpleauth'       => __DIR__.'/classes/auth/login/simpleauth.php',
	// 'Auth\\Auth_Login_Ormauth'          => __DIR__.'/classes/auth/login/ormauth.php',

	// 'Auth\\SimpleUserUpdateException'   => __DIR__.'/classes/auth/exceptions.php',
	// 'Auth\\SimpleUserWrongPassword'     => __DIR__.'/classes/auth/exceptions.php',
	// 'Auth\\OpauthException'             => __DIR__.'/classes/auth/exceptions.php',

	// 'Auth\\Model\\Auth_User'            => __DIR__.'/classes/model/auth/user.php',
	// 'Auth\\Model\\Auth_Userpermission'  => __DIR__.'/classes/model/auth/userpermission.php',
	// 'Auth\\Model\\Auth_Metadata'        => __DIR__.'/classes/model/auth/metadata.php',
	// 'Auth\\Model\\Auth_Group'           => __DIR__.'/classes/model/auth/group.php',
	// 'Auth\\Model\\Auth_Grouppermission' => __DIR__.'/classes/model/auth/grouppermission.php',
	// 'Auth\\Model\\Auth_Role'            => __DIR__.'/classes/model/auth/role.php',
	// 'Auth\\Model\\Auth_Rolepermission'  => __DIR__.'/classes/model/auth/rolepermission.php',
	// 'Auth\\Model\\Auth_Permission'      => __DIR__.'/classes/model/auth/permission.php',
	// 'Auth\\Model\\Auth_Provider'        => __DIR__.'/classes/model/auth/provider.php',
));
```
### config.phpの編集
`/fuel/app/config/config.php`でmyauthを読みこんでもらうように設定します。

```
	'always_load' => array(
		'packages' => array(
			'auth',
			'myauth', 追加
		),
	),
```

ここまでで、Simpleauthによるバリデーションを無効化し
自分で設定するバリデーションを使う準備が整ったので

以下では自分でバリデーションを設定していきます。

ちなみにバリデーションはどこへ書いていいのかわかりません。
Railsですとモデルのところに書くのですが、モデルの書き方をまだよく理解していないので
ひとまずコントローラーに書くことにします。

### myvalidation.phpの設定
`fuel/app/classes/myvalidation.php`を作成し、以下のように記述します。

```
<?php
class MyValidation {
    public static function _validation_unique_email($email){ 
      Validation:active()->set_message('unique_email','既に登録されています');
        $result = DB::select("email")
                ->where('email', '=', strtolower($email)) //emailを条件にDB検索
                ->from('users')->execute(); //usersテーブル内を検索
        return !($result->count() > 0); //取得件数が0より多ければfalseを返す
    }
}
```
### コントローラ内でのバリデーション
新規アカウント登録用コントローラーで以下のように記述します。

```
<?php
class Controller_Home extends Controller_Template
{
  public function action_signup()
  {
    $this->template->title = '新規登録';
    $this->template->content = View::forge('home/signup'); 

    if(Input::post()){
      $val = Validation::forge();
      $val->add_callable('myvalidation');

      $val->add('username', 'ユーザー名')
          ->add_rule('required');

      $val->add('email', 'メールアドレス')
          ->add_rule('required')
          ->add_rule('valid_email')
          ->add_rule('unique_email');

      $val->add('password', 'パスワード')
          ->add_rule('required')
          ->add_rule('min_length', 8);
      
      $val->add('password-confirmation', '確認用のもの')
          ->add_rule('required')
          ->add_rule('match_field', 'password');

      if($val->run()) {
        Auth::create_user(
          $_POST['username'],
          $_POST['password'],
          $_POST['email'],
          1
        );
      } else {
        $data["subnav"] = array('signup'=> 'active' );
        $this->template->content = View::forge('home/signup', $data);
        foreach($val->error() as $key => $value) {
          $flash[] = $value->get_message()."</br>";
        }
        Session::set_flash('message', implode($flash));
      }
    }
  }
}
```

### エラーメッセージの日本語化
`/fuel/app/config/config.php`を以下のように編集します。

```
	'language' => 'ja',

	/**
	 *  Fallback language when file isn't available for default language.
	 */

	'language_fallback' => 'en',

	/**
	 *  PHP set_locale() setting. Use null to not set.
	 */

	'locale' => 'en_JP.utf8',

	/**
	 * -------------------------------------------------------------------------
	 *  Internal string encoding charset
	 * -------------------------------------------------------------------------
	 */

	'encoding' => 'UTF-8',
```
その後、`app/lang/ja/validation.php`フォルダとファイルを作成し、
そこへ以下のように記述します。(適当に拾ってきたものを利用しました。)

```
<?php
return array(
    'required'        => ':label を入力してください。',
    'min_length'      => ':label は :param:1 文字以上で入力して下さい。',
    'max_length'      => ':label は :param:1 文字以下で入力して下さい。',
    'exact_length'    => ':label は :param:1 文字で入力して下さい。',
    'match_value'     => ':label は使用できない文字が含まれています。使用可能な文字 [:param:1] ',
    'match_pattern'   => ':label は :param:2 で入力して下さい。',
    'match_field'     => ':label は :param:1 と異なっています。',
    'valid_email'     => ':label は正しいメールアドレスではありません。',
    'valid_emails'    => ':label に不正なメールアドレスが含まれています。',
    'valid_url'       => ':label は正しいURLではありません。',
    'valid_ip'        => ':label は正しいIPアドレスではありません。',
    'numeric_min'     => ':label は :param:1 より大きい値を入力してください。',
    'numeric_max'     => ':label は :param:1 より小さい値を入力してください。',
    'numeric_between' => ':label は :param:1 以上 :param:2 以内の値を入力してください。',
    'valid_string'    => ':label は :param:1 で入力してください。',
    'required_with'   => ':label は :param:1 を1つ以上選択する必要があります。',
    'valid_date'      => ':label は 日付として正しくありません。',
);
```

至らない点等あればご指摘いただければ幸いです。
ありがとうございました。

参照記事：
https://qiita.com/kazukichi/items/2a6e242091c5f485b976
https://helog.jp/framework/fuelphp-auth
https://helog.jp/framework/fuelphp-validation/
https://qiita.com/Anderiens/items/3ee740065a1c725ad4b1
https://www.youtube.com/watch?v=46Hp9T1uNH0&t=325s