2012年7月31日火曜日

独自のバリデーションで検証済みの値を変更する(FuelPHP)

独自のバリデーションを追加するやり方は以前にも書いたんですが
検証済みの値 $validation->validated() で取得する値を変更するやり方。

やりたかったのは日付チェック用のルールを作っておいて検証が成功した場合の戻り値を統一したい。
入力例:2012/7/31, 2012-07-31, 2012年7月31日
検証済み:2012-07-31

myvalidation.php
public static function _validation_valid_date($val) {
    // 検証を記述(省略)

    // 検証後
    // return true
    return date('Y-m-d', strtotime($val));
}

こうしておくと
// date = '2012年7月31日';

$val = Validation::forge();
$val->add_callable('myvalidation');

$val->add('date', '日付')
    ->add_rule('valid_date');

if ($val->run())
{
    echo $val->input('date');        // 結果:2012年7月31日
    echo $val->validated('date');    // 結果:2012-07-31
}


こんなやり方でいいのかな。。。
動いたからいいか

2012年7月30日月曜日

validationで日付のチェック

FuelPHP の Validation で日付のチェック

正規表現で値を分解して checkdate 関数でチェックやっただけ。
public static function _validation_valid_date($val)
{
    if (!$val)
    {
        return true;
    }
    
    $parts = array();
    // 形式はyyyy-mm-dd
    if (!preg_match('/^([0-9]{4})[\-\/\.](0?[0-9]|1[0-2])[\-\/\.]([0-2]?[0-9]|3[01])$/', $val, $parts))
    {
        return false;
    }
        
    if (checkdate($parts[2], $parts[3], $parts[1]) === true)
    {
        return true;
    }
    else
    {
        return false;
    }
}

日付の比較チェック

引数を1つ追加して比較用のフィールド名を指定
public static function _validation_compare_date($val, $field)
{
    if (!$val)
    {
        return true;
    }
    if (!Input::post($field))
    {
        return true;
    }
        
    $tmp_val_st = strtotime(Input::post($field));
    $tmp_val_ed = strtotime($val);
        
    if ($tmp_val_st > $tmp_val_ed)
    {
        return false;
    }
        
    return true;
}


もっときれいなソースをかけるようになりたい・・・



いままで正規表現するとき ereg 関数を使ってたけどこれは PHP5.3 から非推奨となってました。ereg 関数を使っている場合は preg_match 関数に置き換えないと・・・知らんかった。
PHP: ereg - Manual
PHP: preg_match - Manual




2012年7月27日金曜日

show_errorsの表示をちょっと変えてHTML5風に

題名はかなり大げさなこと書いてますが、jQueryのプラグインを久しぶりに作ってみようと思っていま勉強中のFuelPHPと合わせて適当にいじってみました。

※下に書いてあることは別にプラグインを作らなくてもCSSだけでどうにでもなります・・・ただプラグインを作ってみたかっただけです。


HTML5を使って input に required 属性を付けると

こんなかんじで格好いいです。
でもこれは HTML5 を使ってるんで validation したあとのエラーメッセージとか至って普通に表示されます。(Email アドレスの記述が間違っている場合のエラー)


これを HTML5 風に変えてみよう・・・
(いろいろめんどくさくなってきたんで途中でやめましたが・・・)

簡単にやったこと

  • Formクラスを使って input を生成
  • Validationクラスをちょっとだけ修正
  • ConfigのValidationにopen_errorを追加
  • jQueryプラグイン作って埋め込む

まず、input の生成。これは普通。(required属性はつけません)
<?php echo Form::input('name', '名前'); ?>

次に、fuel/core/classes/validation.php と fuel/core/classes/validation.error.php を修正します。

fuel/core/classes/validation.php 行560
// $output .= $options['open_error'].$e->get_message().$options['close_error'];
$output .= $e->_replace_tags($options['open_error']).$e->get_message().$options['close_error'];

open_error のタグをエラーメッセージと同じように置換するようにメソッドを呼び出します。が、_replace_tagsは protected なんで public に変更しました。

fuel/core/classes/validation/error.php 行113
// protected function _replace_tags($msg)
public function _replace_tags($msg)

これでOK。
つぎに、fuel/app/config/config.php の validation に open_error を以下のように追加。
'validation' = array(
    'open_error' => '<li class="error_:field">',
);

上の :field の部分は validation や input で指定している id になります。

これであとはjQueryのプラグインを埋め込んで実装するだけです。
jquery.validation.js を作って中身は以下
(function($){
    function Validation() {
        this._defaults = {
            form_identifier: 'form_',
            error_identifier: 'error_',
        };
        this._target = null;
        
        this.errors = {};
    }
    
    $.extend(Validation.prototype, {
        _attach: function(element) {
            this._target = element;
            this._target.css('display', 'none');
            
            this._errors = this._get_errors();
            for (i = 0; i < this._errors.length; i++) {
                this._create_message(this._errors[i]);
            }
        },
        _get_errors: function() {
            errors = [];
            
            $("li", this._target).each(function(idx, val) {
                error = {};
                error['id'] = $(val).attr('id')
                                .replace($.validation._get('error_identifier'), '');
                error['text'] = $(val).text();
                
                errors.push(error);
            });
            
            return errors;
        },
        _create_message: function(error) {
            e = $('<div>')
                    .addClass('validation_message')
                    .text(error.text)
                    .css('display', 'none');
            
            t = $('#'+this._get('form_identifier')+error.id);
            t.after(e);
            
            e.css({
                'display': 'inline',
                'position': 'absolute',
                'top': t.position().top + (t.outerHeight() - e.outerHeight()),
                'left': t.position().left + t.outerWidth() + 10,
            });
        },
        _get: function(key) {
            return this._defaults[key];
        },
    });
    
    $.fn.validation = function(options) {
        $.extend($.validation._defaults, options);
        $.validation._attach(this);
        return this;
    }
    
    $.validation= new Validation();
    window.VALIDATION = $;
    
})(jQuery);

コメントも何もないですがshow_errors()で表示している内容をとってきてそれぞれのinputの横に出してるだけです。(汚いソースですみません)

使うときはこんなかんじ。
<div class="error">
    <?php echo $validation->show_errors(); ?>
</div>

<?php echo Asset::js('jquery-1.7.2.min.js') ?>
<?php echo Asset::js('jquery.validation.js') ?>
<script type="text/javascript">
    $(document).ready(function() {
        $('div.error').validation();
    });
<script>


こんなかんじで表示されます。


最初にも言った通り、べつにプラグインの必要はないです。
それぞれの項目の場所で
<?php echo $valication->error('name'); ?>

とかしてこれにスタイルをつければ同じことできます・・・



2012年7月26日木曜日

チェックボックスのバリデーションや値の受け渡しで悩む(FuelPHP)

FuelPHPのFormクラスを使ってチェックボックスを使う際にちょっとハマったことを。

まず、バリデーションについてですが自前で実装しました。自前といってもいいサンプルがあったんでそれを真似してます。

FuelPHPでcheckboxのバリデーション
http://btt.hatenablog.com/entry/2012/06/14/143043


次に値の受け渡しです。

普通の input とかの場合だと
<?php echo Form::input('name', Input::post('name', isset($user) ? $user->name : '')); ?>

バリデーションでエラーを戻した場合は Input::post で入力された値を取得、post されていない場合はモデルから取得した値、何もなかったら空白。

チェックボックスの場合、リストを配列から回しながら表示していくと思います。
for ($groups as $group) {
    echo Form::checkbox('groups[]', $group->id, array('id' => 'group_'.$group->id));
    echo Form::label($group->name, 'group_'.$group->id);
}

ただ表示するだけだとこんなかんじになるのかなと・・・

これに input と同じような仕組みを付け加えると
for ($groups as $group) {
    echo Form::checkbox('groups[]', $group->id,
                            in_array($group->id, Input::post('groups', array())) ?
                            array('id' => 'group_'.$group->id, 'checked' => 'checked') :
                            array_key_exists($group->id, isset($user) ? $user->groups : array()) ?
                            array('id' => 'group_'.$group->id, 'checked' => 'checked') :
                            array('id' => 'group_'.$group->id)
                        );
    echo Form::label($group->name, 'group_'.$group->id);
}

なんか大変なことになってしまった・・・

checkbox を Input::post で受け渡しすると checkbox の value が配列の値として入ってくるけどモデルから取得した値は、キーになってる。
(中身を展開すればもちろん値としてもあるわけですが・・・)

考えてると結果こうなってしまいました。
もっとスマートな方法はないものかと・・・



2012年7月25日水曜日

モデル間のリレショーンをためしてみた(FuelPHP)

Orm(Object Relation Mapping)の利点はリレーションを意識したモデル操作ができる。
と本に書いてたんで試してみました。

試してみたのはユーザとグループを関連付けるだけ。
必要なテーブルはユーザ(users)とグループ(groups)、この2つを関連付ける中間テーブル(users_groups)の3つです。

まずは oil コマンドでモデルを作ります。
$ oil g model users name:varchar[50] login_id:varchar[50] password:string
$ oil g model groups name:varchar[50]
$ oil refine migrate

fuel/app/classes/model に user.php と group.php が作られ、
oil refine migrate を実行するとテーブルも作られると思います。

次に中間テーブル(users_groups)
CREATE users_groups (
    user_id int(11) NOT NULL,
    group_id int(11) NOT NULL,
    PRIMARY KEY (user_id, group_id)
);

これで準備はOK。

やりたいことはユーザを登録するときに合わせてグループの関連付けも登録、削除するときは関連付けも一緒に削除。

まず、モデルファイル(user.php)に以下を追加します。
protected static $_many_many = array(
    'groups' => array(
        'key_from' => 'id',
        'key_through_form' => 'user_id',
        'table_through' => 'users_groups',
        'key_through_to' => 'group_id',
        'model_to' => 'Model_Group',
        'key_to' => 'id',
        'cascade_save' => true,
        'cascade_delete' => true,
    ),
);

こんなかんじでリレーションの設定を行います。
項目の詳細はマニュアルを見るのが一番かなと。(自分もまだよくわかってないし)
http://docs.fuelphp.com/packages/orm/relations/many_many.html

次に実際にコントローラから登録をする処理です。
public function action_create()
{
    // グループ情報を取得
    $groups = Model_Group::find()->get();

    if (Input::method() == 'POST')
    {
        // Authインスタンス
        $auth = Auth::instance();

        // ユーザ
        $user = Model_User::forge();
        $user->name = Input::post('name');
        $user->login_id = Input::post('login_id');
        $user->password = $auth->hash_password(Input::post('password'));

        // 関連付け
        if (Input::post('group_id'))
        {
            for (Input::post('group_id') as $group_id)
            {
                $group = Model_Group::find($group_id);
                if ($group)
                {
                    $user->groups[] = $group;
                }
            }
        }

        // 登録
        $user->save();
    }
}

ざっくり書きましたがこんなかんじになります。
グループの関連は複数を相当して書いてみました。
これを動かすと users と users_groups のテーブルに値が書き込まれるはずです。

またこれでユーザを削除する場合も $user->delete() で users_groups も削除されますし、データを取得する場合も $user->find($id) だけで users_groups の情報も取得してきます。

いろいろ関連ついてくると複雑になりそうなんで簡単ものから徐々に。

※書籍には「中間テーブルに対応するモデルを作成することで、_many_manyを使わず_has_manyを使う」と書いていたんですが・・・
なんかうまくいかなかったです。時間見つけたまたトライしてみます。



2012年7月23日月曜日

メモ:Sassのmixinでオプション引数をつける

mixin で関数作ってたんですけどオプション引数のやり方。

@mixin border($top, $right, $bottom, $left, $color: #666) {
    border-style: solid;
    border-color: $color;
    border-width: $top $right $botom $left;
}


上のように $color: #666 と記述します。
必死に $color = #666 と書いてました。。。(もちろんコンパイルエラーです)




SimpleAuthを参考にいじってみました(FuelPHP)

FuelPHPを使ってログイン機能を実装するのに SimpleAuth で実装しようとしてたんですが SimpleAuth の username, email, password は変更できないという指摘を受けましたんで、自分なり SimpleAuth を参考に Auth のドライバを作ってみました。




コードは恥ずかしいので載せませんがやったこと箇条書き。

  1. fuel/packages/auth/classes/auth/login/simpleauth.php をコピー(名前は適当に easyauth.php としました)
  2. easyauth.php を開き、クラス名を Auth_Login_EasyAuth に変更
  3. ここで一度動かしてみようと思ったらエラーで動きませんでした。
    Class 'Auth_Login_EasyAuth' not found
  4. この Auth_Login_EasyAuth を認識させるために bootstrap.php にも記述が必要みたいなので fuel/packages/auth/classes/auth/bootstrap.php に追記します。
    'Auth\\Auth_Login_EasyAuth' => __DIR__.'/classes/auth/login/easyauth.php',
    
  5. これでもう一回動かしてみると、not found のエラーが消えました
  6. 次に fuel/packages/auth/config/auth.php と simpleauth.php を fuel/app/config にコピーして simpleauth.php を easyauth.php に名前を変更しておきます
  7. あとは fuel/packages/auth/classes/login/easyauth.php を変更していきました

ざっくりです。
躓いたところは bootstrap.php に追記するっていうのくらいですかね。
あとは auth が抽象メソッドを持ってるのでそれさえ消さずに書き直すと動作すると思います。



2012年7月21日土曜日

FuelPHPのインストール

前にこんな記事書きました。
http://teru2-bo2.blogspot.jp/2012/06/fuelphp.html

が、インストールについて

$ curl get.fuelphp.com/oil | sh

$ Sites/
$ oil create fuelphp-sample

これでOK。
これだけでFuelPHPを使うことができます。


簡単に仕組みを説明すると
$ curl get.fuelphp.com/oil | sh
/usr/binに oil というファイルが作成されます。
これで oil コマンドが使えるようになるんですがこれはあくまでインストール専用の oil コマンドみたいです。中身を見てみると create 以外はエラーになるようになってます。


そして create の中身も簡単です github から最新の FuelPHP を clone しています。
ということで git は必須?になるのかな。。。

clone したあとは下のコマンドを実行。
$php ./fuelphp-sample/oil refine install

ここでは cache, logs, tmp, config の4つのディレクトリに必要な権限を与えているみたいです。

ということは。。。
サイトからFuelPHPをダウンロードしてきて上のコマンドを実行すると同じことですね。


ただまぁ oil create だけで終わるんで oil create 覚えたが楽です。




2012年7月19日木曜日

FuelPHPのバリデーション

簡単にですがバリデーションを使ってみました。

書き方っていうかどこに書くのかコントローラなのかモデルなのか。
業務のほうではモデルに書いてみましたがここではコントローラにそのまま書いた例を。
コードはこんなかんじです。

public function action_create()
{
    $auth = Auth::instance();

    if (Input::method() == 'POST')
    {
        $val = Validation::forge();

        $val->add('num', 'Number')
            ->add_rule('required')
            ->add_rule('valid_string', array('numeric'));

        $val->add('name', 'Name')
            ->add_rule('required');

        $val->add('login_id', 'LoginID')
            ->add_rule('required')
            ->add_rule('min_length', '4');

        $val->add('password', 'Password')
            ->add_rule('required')
            ->add_rule('min_length', '6');

        $val->add('password_conf', 'PasswordConf')
            ->add_rule('match_field', 'password');

        if ($val->run())
        {
            // バリデーションOKの場合
            $user = Model_User::forge(array(
                        'num' => $val->validated('num'),
                        'name' => $val->validated('name'),
                        'login_id' => $val->validated('login_id'),
                        'password' => $auth->hash_password($val->vaildated('password')),
                    ));
            if ($user->save())
            {
                Response::redirect('user');
            }
        }
        else
        {
            // バリデーションNGの場合
            Session::set_flash('error', $val->show_errors());
        }
    }

    $this->template->title = '新規作成';
    $this->template->content = View::forge('user/edit');
}

バリデーションの部分ですが Validation::forge() で返ってきたオブジェクトをもとにルールを追加していきます。
ここで使っているのは required(必須)、valid_string[numeric](数値チェック)、min_length(最小文字数)、match_field(指定したフィールドと合っているか)
$val->add や $val->add_field で一行で書いてしまうこともできますが、ルールが増えた場合見づらいのかなって思ってこの方法を選びました。


バリデーションの種類は他にもいろいろありますが、そこはマニュアルを参照してください。
http://docs.fuelphp.com/classes/validation/validation.html#rules



次に独自にバリデーションを作りたい場合ですね。
良い例が思いつかないのですが valid_string を使うことで数字や英字などのチェックをすることは可能です。しかしながらちょっと面倒だとかんじるひとやエラーメッセージで区別がつきづらいというひともいるのではないかと思います。

簡単な例ですが fuel/app/classes/myvalidation.php を作成します。
class MyValidation
{
    // 数字チェック
    public function _validation_valid_numeric($val)
    {
        return (preg_match('/^[0-9]+$/', $val) > 0);
    }

    // アルファベットチェック
    public function _valition_valid_alpha($val)
    {
        return (preg_match('/^[a-zA-Z]+$/', $val) > 0);
    }
}

メソッドの中身は大したことありません。メソッド名の先頭に _validation がつくくらいです。

これを実際に使う場合は
$val = Validation::forge();
$val->add_callable('MyValidation');

$val->add('num', 'Number')
    ->add_rule('required')
    ->add_rule('valid_numeric');

こんなかんじ。
add_callable で独自のバリデーションクラスを読み込ませ、あとは一緒です。
簡単ですね。

ぜひ試してみてください。




2012年7月18日水曜日

FuelPHPでリンクを指定

これは完全にメモです。。。

アンカーリンクを作成する
<?php echo Html:anchor('welcome/index', 'リンクする'); ?>

第1引数にはアドレス、第2引数にはリンクする文字列を指定します。


アドレスのみを作成する
<?php echo Uri:create('welcome/index'); ?>


CSSファイルの読み込み
<?php echo Asset:css('layout.css'); ?>

これはCSSに限った話ではないですがFuelPHPには予めpublic/assets内に css , img , js の3つのフォルダが用意されています。
基本的にはここにそれぞれ対応するファイルを配置し、Assetクラスを用いて読み込みをするみたいです。



FuelPHPのテンプレートコントローラ

テンプレートコントローラを使うことで1つのページを複数のファイルに分割し、部品化できます。

まず、全体レイアウトを定義するビューファイル fuel/app/views/template.php を作成します。
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <title><?php echo $title; ?></title>
</head>
<body>
    <div id="contents">
        <?php echo $content; ?>
    </div>
    <div id="footer">
        <?php echo $footer; ?>
    </div>
</body>

</html>

変数が $title , $content , $footer 3つあることがわかります。
この3つの変数にそれぞれ値をセットして1ページを作っていきます。

まずは $content の部分に読み込ませるビューファイル fuel/app/views/layout/index.php を作成します。
<h2><?php echo $title; ?></h2>
<p>Hello, <php echo $user; ?>!</p>

次に $footer の部分に読み込ませるビューファイル fuel/app/views/footer.php を作成します。
<p>Copyright&copy;2012</p>


ビューにはこの3つのファイルを作成します。
説明はあとにしてコントローラ fuel/app/classes/controller/layout.php を作成します。
<?php
class Controller_Layout extends Controller_Template
{
    public function action_index()
    {
        $data = array();
        $data['user'] = 'hayashida';

        $this->template->set_global('title', 'テンプレートコントローラの練習');
        $this->template->content = View::forge('layout/index', $data);
        $this->template->footer = View::forge('layout/footer');
    }
}

こんなかんじになります。

てことで簡単に説明。

変数 $title は template.php と layout/index.php の2つのビューファイルで使われています。
複数のビューファイルで同じ変数を使用する場合 set_global メソッドを使います。

変数 $content と $footer はビューファイルの中身が入ります。
それぞれのビューファイルで変数を使用する場合は、第2引数に値を入れてあげます。

http://127.0.0.1/fuelphp/public/index.php/layout


また、今回はテンプレートとなるビューファイル fuel/app/views/template.php としましたがこれはデフォルトです。
もちろん変更することも可能です。
テンプレートファイルを変更する場合は
<php
class Controller_Layout extends Controller_Template
{
    public $template = 'template_view';

    public function action_index()
    {

    }
}
こんなかんじで変数 $template にテンプレートファイルを指定するだけです。
ただ、テンプレートはコントローラ単位でしかできないみたいです。
(メソッド単位では変更できない)


参考までドキュメント
http://docs.fuelphp.com/general/controllers/template.html


2012年7月11日水曜日

完全にメモ。CentOS 6.2 に ruby をインストール

Ruby のインストールの前に gcc , yaml , zlib のインストール
(これをインストールしておかないとあとあと面倒です)
# yum -y install gcc

# wget http://pyyaml.org/download/libyaml/yaml-0.1.4.tar.gz
# tar zxvf yaml-0.1.4.tar.gz
# cd yaml-0.1.4.tar.gz
# ./configure
# make
# make install

# yum -y install zlib-devel


Ruby のインストール
# wget ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p194.tar.gz
# tar zxvf ruby-1.9.3-p194.tar.gz
# cd ruby-1.9.3-p194
# ./configure
# make
# make install
# ruby -v
ruby 1.9.3p194

Gem のアップデート
# gem update --system
# gem -v
1.8.24

これでOK。

試しに Rails もインストール
# gem install rails

file 'lib' not found

あれ。。。
RDocとかいうのを入れておかないとだめみたい。
ということで
# gem install rdoc

# gem install rails

# rails -v
Rails 3.2.6

これでOK。



2012年7月10日火曜日

PHPを開発するのに環境を整えた

いろいろ試しながらプラグインいれたりなんだりしてたんでこの度きれいにしました。

開発ツールは Eclipse。
なんだかんだしてこれが一番自分には合ってるような気がしました。

インストールしたものは順に

  1. Eclipse Classic 3.7.2
  2. PDT(PHP Development Tools 3.0)
  3. Zen Coding for Eclipse
  4. EGit
  5. WTP(Web Developer Tools 3.3)

設定は画面

1.エンコードと改行コード

2.Sass を使うんでCSSに *.scss を追加

3.実行可能ファイルでPHPを設定

4.Zend DebuggerからXDebugに変更

5.Zen Coding の文字コードを ja に変更

6.HTMLとか検証設定



ざっとですがこんなかんじです。
他にもショートカットキーとか設定してますがそれを好みに合わせて。。。




EclipseでSass+Compassを試してみました

参考にしたページ
上のサイトを参考にすればEclipseでSassを試すことはできますが
私がしたこともメモ程度に。

まず、EclipseでSassを使うにあたって Ant が必要です。
私の環境には Ant が入ってなかったのでまずインストールをしました。
Ant のインストールというか J2EE をインストールすることでOK。

また予め Sass と Compass をインストールしておいてください。

まず、新規にPHPプロジェクトを作成したあとプロジェクト直下に build.xml を作成します。

この build.xml を Ant ビルドすることで Sass をコンパイルするように設定します。
中身は以下
<?xml version="1.0" encoding="utf-8" ?>
<project name="test_sass" default="all" basedir=".">

    <property name="ruby.bin" value="/usr/bin" />

    <target name="all" depends="init,compass" />

    <condition property="done.setup">
        <available file="./assets/config.rb" />
    </condition>

    <target name="init" unless="done.setup">
        <mkdir dir="assets" />
        <exec executable="${ruby.bin}/ruby" dir="assets">
            <arg value="${ruby.bin}/compass" />
            <arg value="create" />
            <arg value="--sass-dir" />
            <arg value="sass" />
            <arg value="--css-dir" />
            <arg value="css" />
        </exec>
    </target>

    <target name="compass">
        <exec executable="${ruby.bin}/ruby" dir="assets">
            <arg value="${ruby.bin}/compass" />
            <arg value="compile" />
            <arg value="-s" />
            <arg value="expanded" />
        </exec>
    </target>

</project>

init で assets フォルダを作成してその中に compass のプロジェクトを生成します。
その後、compass を実行してコンパイルをするというかんじです。

init は初回だけなんでコマンドから実行してもいいでせっかくなんで Ant から実行します。

これで生成された assets/sass に適当にファイルを作成して build.xml をAntビルド とすると assets/css にファイルが生成されます。

毎回、Antビルドするのが面倒だという方はAntビルダーの設定をしておけば便利かなと思います。

プロジェクトを右クリップ > プロパティ > 新規 > Antビルダー で画面を開きます。

メインタブでビルドファイルと基底ディレクトリを選択します。

リフレッシュタブでAntビルド後、各リソースを更新するように設定します。

ターゲットタブで自動ビルドにあるターゲットの設定で実行するターゲットを選択します。

これでOK。
ただし、こうするとSassファイルに関係なく保存されたときに勝手にAntビルドが走ります。
なんか回避方法があるのかな。。。




Zen-Codingでちょっとだけ遊んでみました

Coda と Eclipse に Zen-Coding をインストールしていろいろと試してみました。

簡単に入力でいっきに展開してくれるのでけっこう面白かったです。
(今まで知らなかったのが恥ずかしいくらい。。。)

Zen Coding For Eclipse
https://github.com/sergeche/eclipse-zencoding
インストール
http://teru2-bo2.blogspot.jp/2012/07/eclipsezen-coding.html


Zen Coding(Coda)
http://code.google.com/p/zen-coding/downloads/list
インストール
http://webcake.no003.info/webdesign/tea-for-coda.html?utm_source=rss&utm_medium=rss&utm_campaign=tea-for-coda



Zen Coding のチートシートも公開されてます。
http://code.google.com/p/zen-coding/downloads/detail?name=ZenCodingCheatSheet.pdf&can=2&q=


Eclipse でも Coda でも同じような展開が実装できます。
チートシートに載ってないところ。
div#header>h1
<div id="header">
    <h1></h1>
</div>

div#navbar>ul.topmenu>li*5
<div id="navbar"
    <ul class="topmenu">
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
    </ul>
</div>


などなど
インストールも簡単なんでオススメです!



2012年7月9日月曜日

EclipseでZen-Coding

忘れないようにメモ。

インストールは簡単です。
Eclipseを起動して

  1. ヘルプ > 新規ソフトウェアのインストール
  2. アップデートサイトにhttp://zen-coding.ru/eclipse/updates/またはhttp://media.chikuyonok.ru/eclipse/updates/を追加
  3. Zen Coding For Eclipseにチェック
  4. インストール
  5. Eclipseの再起動

これでOK。

div#header>h1 と入力してCommand+Eキーを押すと以下のようになります。
<div id="header">
    <h1></h1>
</div>



2012年7月4日水曜日

iOSでQRコードを読み込む(ZXing 2.0)

ちょっと前に Android で Zxing を使ってQRコードを読み込んでました。
http://teru2-bo2.blogspot.jp/2012/06/androidqrzxing.html

ZXing はiOS用のライブラリも持っているので今回もZXingを使ってQRコードを読み込んでみました。
READMEもサンプルもあるんでそれをそのまま動かせばなんとなく動きはわかると思いますが自分も忘れないように。

とりあえずライブラリをダウンロードして解凍しておきます。
http://code.google.com/p/zxing/downloads/list

Xcodeを起動してプロジェクトを作成しましょう。(ここではZxingTestとしました)
「zxing-2.0/iphone/README」があるんでそれを抜粋しながらZXingを使用する準備をします。

1. Locate the "ZXingWidget.xcodeproj" file under "`zxing/iphone/ZXingWidget/`". Drag ZXingWidget.xcodeproj and drop it onto the root of your Xcode project's "Groups and Files" sidebar. A dialog will appear -- make sure "Copy items" is unchecked and "Reference Type" is "Relative to Project" before clicking "Add". Alternatively you can right-click on you project navigator and select 'Add files to "MyProject"'

「zxing-2.0/iphone/ZXingWidget/ZXingWidget.xcodeproj」を作成したプロジェクトにドラッグ&ドロップします。

2. Now you need to link the ZXingWidget static library to your project. To do that,
 a. select you project file in the project navigator
 b. In the second column, select your _target_ and not the project itself
 c. Go to the 'build phases' tab, expand the 'link binary with libraries' section,
 d. Click the add button A dialog will appear and you should see libZXingWidget.a in the very first possibilities

作成したプロジェクト >> TARGETS(ZXingTest) >> Build Phases >> Link Binary With Libraries に libZXingWidget.a を追加します。

3. Now you need to add ZXingWidget as a dependency of your project, so Xcode compiles it whenever you compile your project.
 a. like in substep c. of previous step, you nedd to do that in the 'build phases' tab of your target
 b. Expand the 'Target Dependencies' section
 c. Click the add Button and a dialog will appear select ZXingWidget target

先ほどの画面上にある Target Dependencies に ZXingWidget を追加します。

4. Headers search path 1: you need to tell your project where to find the ZXingWidget headers. Select your project in the project navigator, and the select your target and go to the "Build Settings" tab. Look for "Header Search Paths" and double-click it. Add the relative path from your project's directory to the "zxing/iphone/ZXingWidget/Classes" directory. Make sure you click the checkbox "recursive path" !

5. Headers search path 2: You need to add zxing cpp headers to your headers search path, do this similarly as previous step to point the path to cpp/core/src/ where the 'zxing' directory is. Do not check the "recursive path" option for this path.

作成したプロジェクト >> PROJECT(ZXingTest) >> Build Settings >> Search Paths >> Header Search Paths に 「zxing-2.0/cpp/core/src」と「zxing-2.0/iphone/ZXingWidget/Classes」を追加します。

6. Import the following iOS frameworks:
 a. AVFoundation
 b. AudioToolbox
 c. CoreVideo
 d. CoreMedia
 e. libiconv
 f. AddressBook
 g. AddressBookUI

 This must be done by adding them in the 'Link Libraries with Binary' just like step 2.c.

これはそのままですね。上のフレームワークを追加します。

これで前準備はOK。
なかなか面倒です。

次は画面ですが特に説明することもないので Assistant Editor と合わせて以下のようになります。 

次にコードです。

ViewController.h
#import <UIKit/UIKit.h>
#import "ZXingWidgetController.h"

@interface ViewController : UIViewController
<
    ZXingDelegate
>

- (IBAction)scanPressed:(id)sender;

@property (retain, nonatomic) IBOutlet UITextView *resultsView;
@property (copy, nonatomic) NSString *resultsString;

@end

ViewController.mm
#import "ViewController.h"
#import "QRCodeReader.h"

@interface ViewController ()

@end

@implementation ViewController
@synthesize resultsView;
@synthesize resultsString;

- (void)viewDidLoad
{
    [super viewDidLoad];
 // Do any additional setup after loading the view, typically from a nib.
}

- (void)viewDidUnload
{
    [self setResultsString:nil];
    [self setResultsView:nil];
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}
- (void)dealloc {
    [resultsString release];
    [resultsView release];
    [super dealloc];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (IBAction)scanPressed:(id)sender {
    ZXingWidgetController *zwc = [[ZXingWidgetController alloc] initWithDelegate:self showCancel:YES OneDMode:NO];
    QRCodeReader *qrcodeReader = [[QRCodeReader alloc] init];
    NSSet *readers = [[NSSet alloc] initWithObjects:qrcodeReader, nil];
    zwc.readers = readers;
    [self presentModalViewController:zwc animated:YES];
    
    [qrcodeReader release];
    [readers release];
    [zwc release];
}

- (void)zxingController:(ZXingWidgetController *)controller didScanResult:(NSString *)result
{
    self.resultsString= result;
    if (self.isViewLoaded)
    {
        [resultsView setText:resultsString];
        [resultsView setNeedsDisplay];
    }
    
    [self dismissModalViewControllerAnimated:YES];
}

- (void)zxingControllerDidCancel:(ZXingWidgetController *)controller
{
    [self dismissModalViewControllerAnimated:YES];
}

@end

コード自体は難しいものではないので問題ないかと思います。
ZXingWidgetController がいいかんじに何でもやってくれます。

一点注意する点はコードのファイル名。
プロジェクトを作成した段階では「ViewContrller.m」ですが、ここでは「ViewController.mm」となります。
.m のままビルドすると「iostream file not found」とかいうエラーが出ると思います。

.m ではなく .mm にするのは簡単に言うと Objective-C と C++ を混在させて動作させることができるようにするため。
ZXing がC++ とかでラップされてるんでしょうね。きっと。

これで動作すると思います。
実機で試してみてください。

SCAN をタップすると以下の画面が立ち上がります。この白い枠内にQRコードをも

認識すると以下のように内容が表示されると思います。





2012年7月3日火曜日

Sassでメディアクエリを使ってみたけど変数が使えない?

Sass でメディアクエリを記述してみました。

普通に書くと
#header {
    background-color: #ccc;

    h1 {
        float: left;

        @media screen and (max-width: 320px) {
            float: none;
        }
    }
}

コンパイル後は
#header {
  background-color: #ccc;
}
#header h1 {
  float: left;
}
@media screen and (max-width: 320px) {
  #header h1 {
    float: none;
  }
}

こんなかんじで至って問題ありません。
まぁ確かにこれでいいんですが max-width の部分を変数にしようと思ったんですがコンパイルエラーになりました。
/* 変数宣言 */
$mobile-width:320px;
#header {
    background-color: #ccc;

    h1 {
        float: left;

        /* 変数を使用 */
        @media screen and (max-width: $mobile-width) {
            float: none;
        }
    }
}
Syntax error: Invalid CSS after "...nd (max-width: ": expected expression (e.g. 1px, bold), was "$mobile-width) {")

んーよくわかりません。
普通に変数を使う分は問題ないんだけど。。。単純にこういうところに変数使ったらだめなんかな。。。

誰か詳しいひと教えてください!!!



ユーザエージェントを判別する(FuelPHP)

これも最近少しずつさわってるFuelPHPですが
ドキュメント見てたら Agent とあったんで気になってさわってみました。

PHPでユーザエージェントを判別する場合
$agent = $_SERVER['HTTP_USER_AGENT'];

これでユーザエージェントを取得することができます。
しかし、これから各ブラウザ、またデバイスを仕分けるのはなかなか面倒な作業だったと思いますが、これが一発で判別できます。
// 従来のユーザエージェント取得
$data['ua'] = $_SERVER['HTTP_USER_AGENT'];

// ブラウザ名
$data['browser'] = Agent::browser();
// バージョン
$data['version'] = Agent::version();
// プラットフォーム名
$data['platform'] = Agent::platform();
// モバイル判別
$data['mobile'] = 'false';
if (Agent::is_mobiledevice())
{
    $data['mobile'] = 'true';
}

// クローラ判別
$data['robot'] = 'false';
if (Agent::is_robot())
{
    $data['robot'] = 'true';
}

これをViewに渡して表示すると
PC画面

 iPod touch

iPad

上のコードではそれぞれ個別にデータを情報を取得しましたが一発ですべてのデータも取得できます。
// ブラウザのすべてのプロパティを取得
$agent = Agent::properties();

$data['browser'] = $agent['browser'];
$data['version'] = $agent['version'];
$data['platform'] = $agent['platform'];
$data['mobile'] = 'false';
if ($agent['ismobiledevice'])
{
    $data['mobile'] = 'true';
}
$data['robot'] = 'false';
if ($agent['crawler'])
{
    $data['robot'] = 'true';
}

これでしても結果は同じです。
ほかにもあるのでその辺りはマニュアルを参照してください。
http://docs.fuelphp.com/classes/agent/usage.html

※2012.7.4
iPadって mobile ?て聞かれたんで試してみました。
iPadは mobile です!


Sassの基本的な使い方

Sassのインストールについては先ほど書きましたが
まず簡単な使い方。GUIツールとかもあるみたいですがまずはコマンドを使って試してみます。

ターミナル(Windowsならコマンドプロンプト)を起動して任意のディレクトリに移動して以下のコマンドを実行します。
$ sass --watch .:.
>>> Sass is watching for changes. Press Ctrl-C to stop.

これでOK。
あとはそのディレクトリにscssファイルを生成してコーディングすると自動的にコンパイルしcssファイルに変換してくれます。

試しに以下のような sample.scss を作成してみます。
#container {
    width: 100%;
    height: 50px;

    h1 {
        font-size: 1.5em;
    }
}

そうすると自動的に sample.css ができたことがわかると思います。
#container {
  width: 100%;
  height: 50px; }
  #container h1 {
    font-size: 1.5em; }

これが sass です。

cssのコーディングをしているとセレクタの指定はほぼ必須。
同じものを毎回記述するのはけっこう面倒なことです。
sass では親セレクタにネストすることでこの面倒な手間を省略することができます。
これだけでもかなりいいです。
慣れてしまえば今までの css より直感的に操作できるのではないかと思います。


次によく使うであろうアンカーなどの:hoverや:activeなどの疑似クラス。
これも簡単です。
sample.scss
a {
    font-size: .9em;
    &:hover {
        text-decoration: underline;
    }
    &:visited {
        text-decoration: none;
    }
}

sample.css
a {
  font-size: .9em; }
  a:hover {
    text-decoration: underline; }
  a:visited {
    text-decoration: none; }

scss の特殊文字 & を使うと親セレクタと & を置換してくれます。
簡単です。

ほかにも sass には変数や関数を作ることもできますし、@mixinやsingleton といった機能も備えています。
このあたりは追々。


sass の コマンドについてちょっとメモ。

< --watch >
フォルダの監視
--watch input/stylesheets:output/stylesheets
ファイルの監視
--watch input.scss:output.css

< --style >
nested / compact / compressed / expanded

--watch と --style は組み合わせて使うことができます。
時間があればそれぞれ試してみてください。
$ sass --style compact --watch .:.
#container { width: 100%; height: 50px; }
#container h1 { font-size: 1.5em; }

a { color: #888; font-size: .9em; }
a:hover { text-decoration: underline; }
a:visited { text-decoration: none; }



Sassを試してみました(インストール)

Sass
CSSを拡張したメタ言語。CSSのコーディング規約とかを作っててもかんじてた部分ではありますが、ほかの言語に比べ再利用とか難しいしいくら規約を作ってもどうしても煩雑になってしまいがち。
Sassを使って効率的にできないかとさわってみました。

Sassは、scssファイルをコンパイルすることでcssファイルに変換してくれます。
コンパイルするにはRubyがまず必要。
MacにはRubyが入ってるんで問題ありませんがWindowsではまずRubyをインストールします。

http://rubyinstaller.org/
ここからインストーラをダウンロードします。
(現時点での最新はrubyinstaller-1.9.3-p194.exe)
注意するところはパスを追加するくらいですがインストーラを使用すればこのあたりも自動的にやってくれます。
(以下、Add Ruby/executables to your PATHにチェックをつけます)


完了後、バージョンを確認します。
ruby -v

次にSassのインストール
gem install sass

これOK。
これもバージョンを確認します。
sass -v

使い方とかも追々書いていきます。


Sassについて参考にしたページ



2012年7月2日月曜日

FuelPHPでファイルアップロード

ファイルアップロードを試してみました。
まず設定ファイルをCore/config/upload.phpからApp/configにコピーします。
<?php
return array(
    /**
     * global configuration
    */

    // if true, the $_FILES array will be processed when the class is loaded
    'auto_process' => true,

    /**
     * file validation settings
    */

    // maximum size of the uploaded file in bytes. 0 = no maximum
    'max_size' => 0,

    // list of file extensions that a user is allowed to upload
    'ext_whitelist' => array(),

    // list of file extensions that a user is NOT allowed to upload
    'ext_blacklist' => array(),

    // list of file types that a user is allowed to upload
    // ( type is the part of the mime-type, before the slash )
    'type_whitelist' => array(),

    // list of file types that a user is NOT allowed to upload
    'type_blacklist' => array(),

    // list of file mime-types that a user is allowed to upload
    'mime_whitelist' => array(),

    // list of file mime-types that a user is NOT allowed to upload
    'mime_blacklist' => array(),

    /**
     * file save settings
    */

    // prefix given to every file when saved
    'prefix' => '',

    // suffix given to every file when saved
    'suffix' => '',

    // replace the extension of the uploaded file by this extension
    'extension' => '',

    // default path the uploaded files will be saved to
    'path' => DOCROOT.'assets/upload',

    // create the path if it doesn't exist
    'create_path' => true,

    // permissions to be set on the path after creation
    'path_chmod' => 0777,

    // permissions to be set on the uploaded file after being saved
    'file_chmod' => 0666,

    // if true, add a number suffix to the file if the file already exists
    'auto_rename' => true,

    // if true, overwrite the file if it already exists (only if auto_rename = false)
    'overwrite' => false,

    // if true, generate a random filename for the file being saved
    'randomize' => false,

    // if true, normalize the filename (convert to ASCII, replace spaces by underscores)
    'normalize' => false,

    // valid values are 'upper', 'lower', and false. case will be changed after all other transformations
    'change_case' => false,

    // maximum lengh of the filename, after all name modifications have been made. 0 = no maximum
    'max_length' => 0
);

変えたところは path くらいです。

次にコントローラ。
<?php

class Controller_Upload extends Controller_Template
{
    public function action_index()
    {
        $data = array();

        $errors = array();
        $files = array();

        // POSTを確認
        if (Input::method() == 'POST')
        {
            Upload::process();
            if (Upload::is_valid())
            {
                // アップロード
                Upload::save();

                // エラーメッセージを取得する
                foreach (Upload::get_errors() as $file)
                {
                    foreach ($file['errors'] as $error)
                    {
                        $errors[] = $error['message'];
                    }
                }
            }
        }

        // config/upload.phpの読み込み
        Config::load('upload', true);
        $upload_config = Config::get('upload');

        // ファイル一覧を取得する
        $files = File::read_dir($upload_config['path']);
  
        $data['errors'] = $errors;
        $data['files'] = $files;

        $this->template->title = 'ファイルアップロード';
        $this->template->content = View::forge('upload/form', $data);
    }
}

やっていることとしてはファイルのアップロードとアップロードディレクトリからファイル一覧を取得してるくらいです。
といいながらもファイル一覧で結構悩みました。。。

最後にViewです。
<-- errors -->
<?php if (count($errors) > 0) : ?>
<ul>
<?php foreach ($errors as $error) : ?>
    <li><?php echo $error; ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>

<?php echo Form::open(array('class'=>'form-stacked', 'enctype'=>'multipart/form-data')); ?>
<div class="clearfix">
    <?php echo Form::label('画像ファイル', 'upload-file'); ?>
    <div class="field">
        <?php echo Form::file('upload-file'); ?>
    </div>
</div>
<div class="actions">
    <?php echo Form::submit('submit', 'アップロード', array('class' => 'btn primary')); ?>
</div>
<?php echo Form::close(); ?>

<-- files -->
<ul>
<?php foreach ($files as $file) : ?>
<li><?php echo $file; ?></li>
<?php endforeach; ?>
</ul>

真ん中にフォームが上と下にそれぞれエラーメッセージとファイル一覧を表示するようにしています。
form タグの指定が通常とは違うんで注意するところはそれぐらいかなと思います。





XCode4のブレークポイント

先日ある勉強会に参加させて頂きました。
そのなかでこれ使えるなって思った機能をひとつ。

件名の通りブレークポイントなんですが通常は実行中の処理をとめてブレークポイントを指定した地点での変数情報や動作状態を確認するために使用すると思います。

<設定した場合>

<有効/無効の切替>
有効時

無効時

ブレークポイントを削除する場合は、どこか適当な場所にドラッグする。


これ使えるなって思った機能ですが、ブレークポイントのマークをControl+クリックすることでいろんな設定ができるみたいです。

<右クリック>

実際にControl+クリックすると上のような画面が表示されます。
Condition
ブレークポイントが実行される条件(入力しなくてもOK)
Ignore
無視する回数
Action
ブレークポイントが実行されたときの動作
Options
Automatically continue after evaluatingをONにするとブレークポイントは実行されますがそこで動作が一時停止せず処理を続行する
Actionは複数設定できるんでいろんな組み合わせでデバッグができそうです。
いままでNSLogをコード上に記述していた部分も少なくなるのではないしょうか。

<実際に設定した画面>


こんなかんじで「Log Message」を設定した場合は上のようなイメージで出力されます。
是非。