マスタを止めないMySQLのレプリケーション設定

いまさらながら、マスタDBを稼働させたまま、レプリケーションのスレーブを追加する為のメモ。

前提条件は以下になります。

  • マスタのmy.cnfにて、『server-id』が設定されている
  • マスタのmy.cnfにて、『log-bin』が設定されている
  • ストーレジエンジンに『InnoDB』を使用している

レプリケーション用ユーザーの作成(マスタ)

マスタDBにて、MySQLレプリケーション用のユーザーを作成します。

$ mysql -u root -p

mysqlにrootで接続し、レプリケーション用ユーザーを作成します。

GRANT FILE, REPLICATION SLAVE ON *.* TO {レプリケーション用ユーザー}@"{スレーブIPaddr or ネットワーク}" IDENTIFIED BY "{パスワード}"

例)
GRANT FILE, REPLICATION SLAVE ON *.* TO repluser@"192.168.1.2/255.255.255.0" IDENTIFIED BY "password"

my.cnf設定(スレーブ)

スレーブサーバにて、レプリケーションに必要なmy.cnfの設定を行います。

server-id = {ネットワークで一意の番号}
log-bin = mysql-bin
report-host = {スレーブサーバホスト名}

例)
server-id = 101
log-bin = mysql-bin
report-host = db02

データのダンプ(マスタ)

$ mysqldump -u root -p -e --single-transaction --master-data=2 --hex-blob --default-character-set=utf8 --all-databases > db_all.dmp

マスタサーバにて、上記コマンドを実行し、全データベースのダンプを取得後、scp等を使用し、ダンプファイルをスレーブサーバへ転送します。

データの投入(スレーブ)

マスタで取得したダンプを、スレーブDBに流し込みます。

$ mysql -u root -p < db_all.dmp

ログファイルとポジションの確認

次に、ダンプファイルに記述してあるログファイルとポジションを確認します。
(該当行は『-- CHANGE MASTER TO...』で始まる行)

$ head -30 db_all.dmp

該当行の以下のそれぞれの値を確認します。

  • MASTER_LOG_FILE
  • MASTER_LOG_POS

レプリケーション設定(スレーブ)

$ mysql -u root -p

スレーブサーバにrootユーザーで接続し、以下のレプリケーション設定を行います。

CHANGE MASTER TO MASTER_HOST='{DBマスタIPaddr}', MASTER_USER='{レプリケーション用ユーザー}', MASTER_PASSWORD='{レプリケーション用ユーザーパスワード}', MASTER_LOG_FILE='{確認したログファイル}', MASTER_LOG_POS={確認したポジション};
START SLAVE;

例)
CHANGE MASTER TO MASTER_HOST='192.168.1.2', MASTER_USER='repluser', MASTER_PASSWORD='password', MASTER_LOG_FILE='mysql-log.000036', MASTER_LOG_POS=935242503;
START SLAVE;

確認(スレーブ)

$ mysql -u root -p

スレーブサーバにrootユーザーで接続し、以下のコマンドの実行結果が、
『Slave_IO_Running』『Slave_SQL_Running』それぞれが『Yes』になっていれば、設定完了です。

SHOW SLAVE STATUS \G

FuelPHPで複数データベースの切り替え

FuelPHPで複数データベースを切り替える方法

FuelPHPでは複数データベースの切り替えが、CI同様、簡単に設定出来ます。

参考)
http://docs.fuelphp.com/classes/database/usage.html

fuel/app/config/db.php

まずは、DBのベース設定に対し、新しいデータベースグループを追加します。

<?php
return array(
    'active' => 'default',

    'default' => array(
        ...
    ),

    // 追加DB
    'another_db' => array(
        'type'        => 'pdo',
        'connection'  => array(
            'persistent' => false,
        ),
        'identifier'   => '`',
        'table_prefix' => '',
        'charset'      => 'utf8',
        'enable_cache' => true,
        'profiling'    => false,
    ),

fuel/app/config/development/db.php

次に、新しいデータベースグループの接続情報を設定します。
(productionを使用する場合は『fuel/app/config/production/db.php』に記述)

<?php
return array(
    'default' => array(
        ...
    ),
    'another_db' => array(
        'connection'  => array(
        'dsn'        => 'mysql:host=localhost;dbname=another_db',
        'username'   => 'dbuser',
        'password'   => 'dbuserpassword',
    ),
)

クエリ実行

execute時に、上で設定したデータベースグループ名を指定すれば、指定のデータベースへクエリを発行します。
当然、何も指定しない場合はactiveで指定しているデータベースグループが使用されます。

DB::query('SELECT * FROM users')->execute();
DB::query('SELECT * FROM users')->execute('another_db');


※Model_Crudを使っている場合、『$_connection』の設定だけで行けるみたいです
FuelPHPで1サイトを作ってみて気が付いた点など
ErogameScape -エロゲー批評空間- Blog : FuelPHPのmodel_CRUDで接続するDBを切り替える方法

最近のFWは全部こんな感じ?

FuelPHPでログイン機能をサクっと実装

SimpleAuthを使えば、簡単にログイン処理が実装出来てしまいます。
今回は手っ取り早く、Fieldsetを使用し、実装してみます。

オートロード設定

まずはauthパッケージを自動的に読み込むよう設定を行います。

fuel/app/config/config.php
'always_load' => array(
    'packages' => array(
        'auth',
        ...
    ),
    ...
),

設定ファイルのコピー

次にSimpleAuthに必要な設定ファイルをコピー後、saltの値を編集します。

$ cp fuel/packages/auth/config/auth.php fuel/app/config/
$ cp fuel/packages/auth/config/simpleauth.php fuel/app/config/
fuel/app/config/auth.php
<?php
return array(
    'driver' => 'SimpleAuth',
    'verify_multiple_logins' => false,
    'salt' => '任意の文字列'
);
fuel/app/config/simpleauth.php
<?php
return array(
    ...
    'login_hash_salt' => '任意の文字列',
    ...
);

認証用テーブルの作成

Database tableに従い、認証用のテーブルを作成します。

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`
    )
)

ログインユーザーの作成

初期状態ではログインユーザーがいない為、コマンドラインにて、ユーザー作成を行います。

$ oil console
>>> Auth::create_user('ユーザー名', 'パスワード', 'メールアドレス');

例)
>>> Auth::create_user('admin', 'password', 'hoge@hoge.com');

Controller

Controllerでは単純に、『$auth->login』を実行し、ログイン成功/失敗の判断を行っています。
今回は簡易的なログイン機能の為、ログイン用のControllerの中でログアウト処理(『Auth::logout()』)を行っており、ログアウトさせる際には、ログインページにリダイレクトする事になります。

<?php
class Controller_Login extends Controller
{
    public function action_index()
    {
        $error = null;

        $view = View::forge('login/index');

        $form = Fieldset::forge();

        $form->add('username', 'アカウント', array('maxlength' => 8))
            ->add_rule('required')
            ->add_rule('max_length', 8);

        $form->add('password', 'パスワード', array('type' => 'password'))
            ->add_rule('required')
            ->add_rule('max_length', 8);
        $form->add('submit', '', array('type' => 'submit', 'value' => 'ログイン'));

        $form->repopulate();

        $auth = Auth::instance();

        Auth::logout();

        if (Input::post()) {
            if ($form->validation()->run()) {
                if ($auth->login(Input::post('username'), Input::post('password'))) {
                    // ログイン成功時
                    Response::redirect('welcome/index');
                }
                $error = 'ログイン失敗に失敗しました';
            } else {
                $error = 'ログイン失敗に失敗しました';
            }
        }

        $view->set_safe('form', $form->build(Uri::create('login/index')));
        $view->set('error', $error);

        return $view;
    }
}

View

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>

<?php if (isset($error)): ?>
<?php echo $error ?>
<?php endif ?>

<?php echo $form ?>

</body>
</html>

ログインチェック

ログイン済みユーザーだけに表示させるページの場合、Controller側で以下の処理を追加します。

<?php
public function before()
{
    parent::before();

    if (Auth::check()) {
    } else {
        // 未ログイン時はログインページへリダイレクト
        Response::redirect('login/index');
    }
}

これで完了です。

FuelPHPのエラーメッセージ日本語化

エラーメッセージ日本語化

前回作成したフォームで、FuelPHPの入力チェック時のエラーメッセージが、初期では英語の為、日本語化を行う。

以下のサイトを参考にさせて頂いた。

設定

まずは『fuel/app/config/config.php』にて日本語を設定する。

'language'          => 'ja',
'language_fallback' => 'en',
'locale'            => 'ja_JP.UTF-8'

日本語ファイル

次にバリデーション用日本語ファイルを『fuel/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:1』と一致しません',
        '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以内で入力してください',
        'valid_string'     => '『:label』は『:rule』で入力する必要があります',
        'checkbox_require' => '『:label』は:param:1つ以上選択する必要があります'
);

上記の設定を行うと、以下のようなエラー文言が出力される。

f:id:BTT:20120615002137p:plain


文字列チェックのエラーは『半角英数で入力してください』やら『半角英数と_(アンダースコア)、-(ハイフン)で入力してください』というような感じで出力したいんだけど、可能なんだろうか。

FuelPHPでフォーム作成 - Formクラス

Fieldsetは、デザイン的な部分で融通が利かない可能性があるので、Formクラスでの実装を試してみる。

以下のサイトを参考にさせて頂いた。

Controller

ControllerでFieldsetの場合と違うのは以下の点。

  1. 『Fieldset::forge()』 が 『Validation::forge()』 に
  2. 『type』や『options』の設定が不要
<?php
class Controller_Myform extends Controller
{
    public function action_index()
    {
        $view = View::forge('myform/index');

        $validation = Validation::forge();

        // input
        $validation->add('name', 'お名前')
                   ->add_rule('required')
                   ->add_rule('valid_string', array('alpha', 'numeric', 'utf8'));

        // password
        $validation->add('password', 'パスワード')
                   ->add_rule('required')
                   ->add_rule('valid_string', array('alpha', 'numeric', 'utf8'));

        // selectbox
        $select_list = array(
                           1 => 'タイプ1',
                           2 => 'タイプ2'
                       );
        $view->set('select_list', $select_list);
        $validation->add('type', 'タイプ')
                   ->add_rule('required')
                   ->add_rule('array_key_exists', $select_list);

        // radio
        $radio_list = array(
                          1 => '男性',
                          2 => '女性'
                      );
        $view->set('radio_list', $radio_list);
        $validation->add('gender', '性別')
                   ->add_rule('required')
                   ->add_rule('array_key_exists', $radio_list);

        // checkbox
        $check_list = array(
                          1 => '映画鑑賞',
                          2 => '散歩',
                          3 => 'その他'
                      );
        $view->set('check_list', $check_list);
        $validation->add_callable(new MyValidation());
        $validation->add('hobby', '趣味')
                   ->add_rule('checkbox_val', $check_list)
                   ->add_rule('checkbox_require', 2);

        if (Input::post()) {
            if ($validation->run() && Security::check_token()) {
                // バリデーションOK時の処理
            } else {
                $view->set('errors', $validation->error());
            }
        }

        return $view;
    }
}

View

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>

<?php if (isset($errors)): ?>
<ul>
<?php foreach ($errors as $error): ?>
<li><?php echo $error ?></li>
<?php endforeach ?>
</ul>
<?php endif ?>

<?php echo Form::open() ?>

<?php echo Form::hidden(Config::get('security.csrf_token_key'), Security::fetch_token()) ?>

お名前:<?php echo Form::input('name', Input::post('name'), array('required' => 'required')) ?><br />

パスワード:<?php echo Form::input('password', Input::post('password'), array('type' => 'password', 'required' => 'required')) ?><br />

タイプ:<?php echo Form::select('type', Input::post('type'), $select_list) ?><br />

性別:
<?php foreach ($radio_list as $key => $val): ?>
<?php echo Form::radio('gender', $key, Input::post('gender') == $key) ?>
<?php echo Form::label($val, 'gender') ?>
<?php endforeach ?>
<br />

趣味:
<?php foreach ($check_list as $key => $val): ?>
<?php echo Form::checkbox('hobby[]', $key, Input::post('hobby') && in_array($key, Input::post('hobby'))) ?>
<?php echo Form::label($val, 'hobby') ?><br />
<?php endforeach ?>

<?php echo Form::submit('submit_btn', '送信') ?>
<?php echo Form::close() ?>

</body>
</html>

Form関数は主なものを以下に抜粋

Form::input('名前', '値', '属性');
Form::select('名前', '選択値', 'options配列');
Form::radio('名前', '値', 'checkedフラグ');
Form::check('名前', '値', 'checkedフラグ');

Form Classにて詳細とサンプルが確認可能です。

FuelPHPでcheckboxのバリデーション

checkboxのバリデーション

公式サイトのValidationを参考に、checkboxのバリデーションを行ってみた。

流れ

  1. バリデーション用クラスを用意
  2. コントローラ側でバリデーション用クラスを呼び出し(add_callable)
  3. 該当のフォームにセット(add_rule)

fuel/app/classes/myvalidation.php

まずは、『classes』の直下にカスタムバリデーションクラスを用意。
『checkbox_val』では、選択された値の正当性をチェック、
『checkbox_require』では、選択項目の数をチェックする。

<?php
class MyValidation
{
    /**
     * 値の正当性チェック
     */
    public static function _validation_checkbox_val($val, $options)
    {
        if ($val) {
            if (!is_array($val)) {
                return false;
            }
            foreach ($val as $v) {
                if (!array_key_exists($v, $options)) return false;
            }
        }
        return true;
    }

    /**
     * 必須チェック
     *
     * $minで最低チェック数を指定
     */
    public static function _validation_checkbox_require($val, $min = null)
    {
        if (!$val || !is_array($val)) {
            return false;
        }

        $min_count = $min ? $min : 1;
        
        return count($val) >= $min_count;
    }
}

Controller

checkbox部分のみ抜粋

<?php
...
// checkbox
$check_list = array(
                  1 => '映画鑑賞',
                  2 => '散歩',
                  3 => 'その他'
              );
$form->validation()->add_callable(new MyValidation());
$form->add('hobby', '趣味', array('type' => 'checkbox', 'options' => $check_list))
     ->add_rule('checkbox_val', $check_list)
     ->add_rule('checkbox_require', 2);

これでチェックボックスの入力チェックのベースが出来た。