2012年11月20日火曜日

Controller_HybridのafterでTemplateかRestか

特殊な例かもしれませんが、もともとController_Templateで作っていたコントローラをController_Hybridに変えたときにちょっと手間取ったのでメモ。

サンプルとして良いのか悪いのかわからないですが、
もともとのソースは
class Controller_category extends Controller_Template
{
    public function after($response)
    {
        $this->template->title = 'カテゴリ';
        return parent::after($response);
    }

    public function action_index()
    {
        $categories = Model_Category::find()->get();

        $this->template->content = View::forge('category/index');
        $this->template->content->set('categories', $categories);
    }
}

こんなかんじ。
afterの処理でテンプレートに対して値をセットしています。

これをController_Hybridに単純に変えて実装してみました。
class Controller_category extends Controller_Hybrid
{
    public function after($response)
    {
        $this->template->title = 'カテゴリ';
        return parent::after($response);
    }

    public function action_index()
    {
        $categories = Model_Category::find()->get();

        $this->template->content = View::forge('category/index');
        $this->template->content->set('categories', $categories);
    }

    public function get_list()
    {
        $categories = Model_Category::find()->get();

        $data = array();
        foreach ($categories as $category) {
            $data[$category->id] = $category->name;
        }

        $this->response($data);
    }
}

変更したところはController_Hybridにした点とget_listというRest用のアクションメソッド。
しかし、これだとController_Templateを使うような画面は良くても、Controller_Restを使うようなRESTfulな処理で正しく出力してくれません。
というかエラーになります。

エラーの内容は
ErrorException [ Warning ]: Attempt to assign property of non-object

Controller_Restを使ってるから$this->templateなんてないよ、的なエラー。
調べてみると、ないよー的なエラーではなくController_Hybridを使ってRESTfulな処理をしている場合、$this->templateには"template"という文字列がセットされています。
(Controller_Hybridでなくてもそうかもしれません)
これでは確かにエラーになります。

afterの中でどうにかしてTemplateなのかRestなのか判定したい場合は、
public function after($response)
{
    if (! $this->is_restful())
        $this->template->title = 'カテゴリ';

    return parent::after($response);
}

これでOK。
Controller_Hybridの中でも使われているメソッドになりますが、これで処理を判定することができます。

もしくは
public function after($response)
{
    if ($this->template instanceof View)
        $this->template->title = 'カテゴリ';

    return parent::after($response);
}

これでも判定はできています。
どういうやり方がいいか、模索中ではありますがどちらが良いのでしょうかね。
というかこれでいいのかな?と思いもします。

何か良い方法あれば教えてください。


2012年11月18日日曜日

routes.phpの設定(メモ)

ほんとうにメモ程度に。
ルーティングの設定でroutes.phpをさわってみました。


<掲示板の例>
app/classes/controller/bbs.php
class Controller_Bbs extends Controller_Template
{
    // 掲示板一覧
    public function action_index() { }
}

app/classes/controller/bbs/article.php
class Controller_Bbs_Article extends Controller_Template
{
    // 記事一覧
    public function action_index($bbs_id = null) { }

    // 記事書き込み
    public function action_post($bbs_id = null) { }
}

まず掲示板一覧のアドレス
http://localhost/fuelphp/bbs/

これはいい。
けど、記事一覧のアドレス
http://localhost/fuelphp/bbs/article/<bbs_id>

掲示板のIDを渡すようにしたいけどなんか格好わるい。。。
掲示板のIDというより記事のIDに見えるし。

さらに記事投稿のアドレス
http://localhost/fuelphp/bbs/article/post/<bbs_id>

変だ。
毎回、$bbs_idをチェックするのも面倒だし。


そこでapp/config/routes.phpに以下を追加
'bbs/:bbs_id/article(/:any)?' => 'bbs/article$2',
app/classes/controller/bbs/article.php
class Controller_Bbs_Article extends Controller_Template
{
    protected $bbs_id = null;

    public function before()
    {
        parent::before();
        $this->bbs_id = $this->param('bbs_id');
    }

    // 記事一覧
    public function action_index() { }

    // 記事書き込み
    public function action_post() { }
}


これで、記事一覧のアドレス
http://localhost/fuelphp/bbs/<bbs_id>/article/

記事投稿のアドレス
http://localhost/fuelphp/bbs/<bbs_id>/article/post/


コードもURLも最初に比べるとすっきりしたかな。



2012年11月16日金曜日

FuelPHPのfieldsetでボタンやINPUTを並べて表示

まず参考記事

ほとんど見よう見まねですが自分なりにFieldsetを拡張してみました。
やりたかったのは1つでボタンとかINPUTを並べて表示したい。てだけです。

最終的なイメージはこんなかんじ

参考記事と同様にパッケージ化してみました。Githubに置いてるのでバグとかあれば教えてください。


使い方は簡単です。
まず、Githubからパッケージをダウンロードしてpackagesフォルダに配置します。
https://github.com/hayashida/Fieldsetplus

つぎに、app/config/config.phpを修正
'always_load' => array(
    'packages' => array(
        'fieldsetplus',
    ),
),

準備はこれだけです。
あとはController側。
public function action_create()
{
    $fieldset = Fieldsetplus::forge();

    $fieldset->add_group(
        'name',     // name
        '名前',     // label
        array(
            array('name1', '姓', array(), array(array('required'))),
            array('name2', '名', array(), array(array('required'))),
        )
    );

    $fieldset->add('created_at', '作成日')
            ->add_rule('required')
            ->add_rule('valid_date');

    $fieldset->add_group(
        'buttons',  // name
        '',  // label
        array(
            array('submit', '', array('type' => 'submit', 'value' => '登録')),
            array('reset', '', array('type' => 'reset', 'value' => 'やり直し')),
        )
    );

    $this->template->title = 'Create';
    $this->template->content = View::forge('user/create');
    $this->template->content->set_safe('fieldset', $fieldset->build());
}

並べたい項目を add_group を使って定義します。
引数は順に、グループ名・ラベル・フィールド情報となっています。
ラベルを空白に設定すると1行(<td colspan="2">)として表示します。
フィールド情報は一見、難しそうに見えますが Fieldset の add と同じです。
Fieldset - Classes - FuelPHP Documentation

View側のソースは従来のFieldsetと同じ
<?php echo $fieldset; ?>

これでOK。
出力した結果はページの上に貼ってる通りに表示されるはずです。
自動生成されたソースはこんなかんじになります。
<form action="" accept-charset="utf-8" method="post">
<table>
    <tr>
        <td class="">名前*</td>
        <td class=""><label for="form_name1">姓</label> <input type="text" required="required" id="form_name1" name="name1" /> <label for="form_name2">名</label> <input type="text" required="required" id="form_name2" name="name2" />  <span></span> </td>
    </tr>
    <tr>
        <td class=""><label for="form_created_at">作成日</label>*</td>
        <td class=""><input type="text" required="required" id="form_created_at" name="created_at" /> <span></span> </td>
    </tr>
    <tr>
        <td class="field_groups " colspan="2">
            <input type="submit" value="登録" id="form_submit" name="submit" /> <input type="reset" value="やり直し" id="form_reset" name="reset" /> 
        </td>
    </tr>
</table>
</form>




2012年11月13日火曜日

FuelPHP1.4のインストール

自分の環境だけ?
oilコマンドを使用してFuelPHPをインストールしようとするとエラーになりました。
$ php oil create <project_name>

あれ?と思ってサイトからダウンロードして今度は
$ cd <project_name>
$ php oil refine install

でも結果は同じ。。。
エラー結果を見てみると
Fatal error: Uncaught exception 'Fuel\Core\PhpErrorException' with message 'date(): It is not safe to rely on the system's timezone settings.

timezoneが設定されてない的なエラー。

要はこれみたいですね。
madroom project: FuelPHP1.4とconfigファイル

1.4になってからconfigの初期設定がcoreのほうに移動してるみたいです。
さらにdefault_timezoneがnullとなっています。これが原因?みたいです。

以下、config.phpの違い(抜粋:コメント部分が1.3)
/**
 * index_file - The name of the main bootstrap file.
 */
'index_file' => false, // 'index.php',

/**
 * DateTime settings
 */
'default_timezone' => null, // 'UTC'

'security' => array(
    /**
     * Encoding mechanism to use on htmlentities()
     */
    'htmlentities_flags' => ENT_QUOTES, // 追加

    /** 
     * Wether to encode HTML entities as well
     */
    'htmlentities_double_encode' => false, // 追加
),

/**
 * Controller class prefix
 */
'controller_prefix' => 'Controller_', // 追加

'routing' => array(
    /**
     * Wether to strip the extension
     */
    'strip_extension' => true, // 追加
),

ただ書き出しただけですが、追加を除くと変更となっているのは index_file と default_timezone だけみたいです。

追加されてる内容は追々調べてみようかなと思います。

気になるは 'controller_prefix' 。。。
どういう用途なんだろう。


2012年11月10日土曜日

iOS Developer Programの更新について

昨年、iOS Developer Programを購入して今回はじめての更新を行いました。
来年もきっと悩んでもとりあえずメモ程度に。
(スクリーンショットをとってたと思ったけど、誤って消してるみたい。。。)

まず、更新の手順。

1.iOS Dev Centerにアクセスし、ログインします。

2.有効期限が近づいていたら画面上のほうにメッセージが表示されそこからiOS Developer Programを購入します。

3.購入後、入金が確認されたあと「Thank you for purchasing an Apple Developer Program」というメールが届きます。

4.iTunes ConnectのContracts, Tax, and Bankingの画面を開いてExpiration Dateの欄が更新されていればOK。


次に、証明書の作成。

1.Macのキーチェンアクセスを起動します。

2.画面左上のアップルメニューから「キーチェーンアクセス」→「証明書アシスタント」→「認証局に証明書を要求」を選択します。

3.証明書アシスタントが開いたら次の情報を入力します。
ユーザのメールアドレス
AppleID登録時のメールアドレス
通称
AppleID登録時の名前を英語表記で入力
CAのメールアドレス
空白のまま
要求の処理
ディスクに保存にチェックをつけ、あわせて鍵ペア情報を指定にもチェック
4.続けるボタンを押して証明書の保存先を保存します。(どこでもいいです)

5.鍵ペアの情報を入力します。
鍵のサイズ
2048ビット
アルゴリズム
RSA
6.完了すると、CertificateSignningRequest.certSignningRequestというファイルが生成されます。


次に、生成された証明書を登録。
(今回、有効期限がきれたあとに更新を行ったので多少表示が違うかもしれません)
(そしてこのあたりスクリーンショットをとってたはずなんですが。。。)

1.iOS Provisioning Portalへアクセスします。

2.左のメニューからCertificatesを開き、一覧のAction欄にある「Request Certificate」を選択します。

3.画面下にあるファイル選択から先ほど作成した証明書を選択し、「submit」ボタンを押します。

4.一覧に戻りしばらくすると証明書がダウンロードできるようになるので証明書をダウンロードします。

5.ダウンロードした証明書をキーチェーンアクセスに登録します。


これでOK。
ではありません。

最後にプロビジョニングファイルの更新。
実機でデバッグする場合は必須ですね。

1.再度、iOS Provisioning Portalへアクセスします。

2.左のメニューからProvisioningを開き、該当するプロビジョニングのEdit→Modifyをクリックします。

3.Certificates欄のチェックが外れてるのでチェックをつけて「submit」ボタンを押します。

4.一覧に戻りしばらくするとプロビジョニングファイルをダウンロードできるようになるのでダウンロードをします。

5.Xcodeのオーガナイズでダウンロードしたプロビジョニングファイルを登録します。


これで完了です。
あとは古いプロビジョニングを消して実機をつなぐと実機のほうのプロファイルも更新されます。





2012年11月9日金曜日

Fieldsetクラスを久しぶりに #fuelphp

はじめに参考記事
上の記事を参考にするとある程度わかるとは思いますが、自分なりに試してみた事を。


まずFieldset クラスを使うことで何ができるかというとFormのHTMLを自動生成してくれます。それだけではなくバリデーションも一緒に定義できるし、バリデーションエラーがあった場合、元の入力画面に戻る処理とかも簡潔にかけるようになります。

適当に新規作成の例
以下はコントローラのソース
public function action_create()
{
    // カテゴリの定義
    $categories = array(
                '機能' => '機能',
                'バグ' => 'バグ',
                'その他' => 'その他',
            );

    // Fieldsetの定義
    $fieldset = Fieldset::forge();

    $fieldset->add('title', '件名')
            ->add_rule('required');

    $fieldset->add('description', '概要',
            array(
                'type' => 'textarea',
                'cols' => '80',
                'rows' => '5',
            ));

    $fieldset->add('category', 'カテゴリ',
            array(
                'type' => 'select',
                'options' => $categories,
            ))
            ->add_rule('in_array', $categories);

    $fieldset->add('limited', '期限日',
            array(
                'class' => 'datepicker',
            ));

    $fieldset->add('progress', '進捗率')
            ->add_rule('required')
            ->add_rule('valid_string', array('numeric'))
            ->add_rule('numeric_min', '0')
            ->add_rule('numeric_max', '100');

    $fieldset->add('submit', '', array('type' => 'submit', 'value' => '登録'));

    // POSTの場合は登録処理を行う
    if (Input::method() === 'POST') {
        // バリデーションチェック
        $val = $fieldset->validation();
        if ($val->run()) {
            $fields = $val->validated();
            unset($fields['submit']);

            $ticket = Model_Ticket::forge();
            $ticket->set($fields);
            if ($ticket->save()) {
                Response::redirect('ticket/index');
            }
        } else {
            // 入力エラーがある場合は元の入力画面にもどるため
            // 入力した内容をそのまま引き継ぐ
            $fieldset->repopulate();
        }
    }

    $this->template->title = 'Create';
    $this->template->content = View::forge('ticket/create');
    $this->template->content->set('fieldset', $fieldset->build(), false);
}

ビューのソースは
<?php echo $fieldset; ?>

これでFormのHTMLの自動生成およびバリデーションの実行・表示、登録が出来上がります。
ビューのソースは設定にあるので変更する場合は、fuel/core/Config/form.phpをfuel/app/Config/form.phpにコピーして修正すると出力方法を変更することができます。


もうひとつFieldsetの使い方としてモデルのファイルに定義をするやり方もあります。
モデルのソースは
    protected static $_properties = array(
        'id',
        'title' => array(
                    'data_type' => 'varchar',
                    'label' => '件名',
                    'validation' => array(
                                'required'
                            ),
                    'form' => array(
                                'type' => 'text',
                            ),
                ),
        'description' => array(
                    'data_tpe' => 'text',
                    'label' => '概要',
                    'form' => array(
                                'type' => 'textarea',
                                'cols' => '80',
                                'rows' => '5'
                            ),
                ),
        'category' => array(
                    'data_type' => 'varchar',
                    'label' => 'カテゴリ',
                    'form' => array(
                                'type' => 'select',
                            ),
                ),
        'limited' => array(
                    'data_type' => 'datetime',
                    'label' => '期限日',
                    'form' => array(
                                'type' => 'text',
                            ),
                ),
        'progress' => array(
                    'data_type' => 'int',
                    'label' => '進捗率',
                    'validation' => array(
                                'required',
                                'valid_string' => array('numeric'),
                                'numeric_min' => array('1'),
                                'numeric_max' => array('100'),
                            ),
                    'form' => array(
                                'type' => 'text'
                            ),
                ),
        'created_at' => array(
                    'form' => array(
                                'type' => false,
                            ),
                ),
        'updated_at' => array(
                    'form' => array(
                                'type' => false,
                            ),
                ),
    );
テーブルのフィールド情報はあらかじめ定義されていると思うのでそこにフィールドの型やラベル、バリデーションを追加していくようなイメージです。

これを使った場合のコントローラのソースは
    public function action_create()
    {
        // カテゴリの定義
        $categories = array(
                    '機能' => '機能',
                    'バグ' => 'バグ',
                    'その他' => 'その他',
                );
        
         $fieldset = Fieldset::forge();

//--- 以下、コメントアウト ---
//         $fieldset->add('title', '件名')
//                 ->add_rule('required');

//         $fieldset->add('description', '概要',
//                 array(
//                     'type' => 'textarea',
//                     'cols' => '80',
//                     'rows' => '5',
//                 ));
        
//         $fieldset->add('category', 'カテゴリ',
//                 array(
//                     'type' => 'select',
//                     'options' => $categories,
//                 ))
//                 ->add_rule('in_array', $categories);
        
//         $fieldset->add('limited', '期限日',
//                 array(
//                     'class' => 'datepicker',
//                 ));
        
//         $fieldset->add('progress', '進捗率')
//                 ->add_rule('required')
//                 ->add_rule('valid_string', array('numeric'))
//                 ->add_rule('numeric_min', '0')
//                 ->add_rule('numeric_max', '100');
//--- ここまで ---
        
//--- モデルを使う場合 ---
        $ticket = Model_Ticket::forge();
        
        $fieldset->add_model($ticket);
        $fieldset->field('category')
            ->set_options($categories)
            ->add_rule('in_array', $categories);
//--- ここまで ---
        
        $fieldset->add('submit', '', array('type' => 'submit', 'value' => '登録'));
        
        // POSTの場合は登録処理を行う
        if (Input::method() === 'POST') {
            // バリデーションチェック
            $val = $fieldset->validation();
            if ($val->run()) {
                $fields = $val->validated();
                unset($fields['submit']);
                
                $ticket = Model_Ticket::forge();
                $ticket->set($fields);
                if ($ticket->save()) {
                    Response::redirect('ticket/index');
                }
            } else {
                // 入力エラーがある場合は元の入力画面にもどるため
                // 入力した内容をそのまま引き継ぐ
                $fieldset->repopulate();
            }
        }
        
        $this->template->title = 'Create';
        $this->template->content = View::forge('ticket/create');
        $this->template->content->set_global('fieldset', $fieldset->build(), false);
    }

コメントしている部分が変更した部分です。
基本的にはモデル情報をadd_modelとしてやるだけです。
今回のサンプルではカテゴリのセレクトとしたため、その情報を追加してます(set_optionsとadd_ruleの部分)がそれ以外は特に難しいことはないと思います。

どちらがいいかはその時々だとは思いますが、実際に使ってみると思ったよりも便利だしビューのソースも普通にできます。(設定で変えれるというのもあって)

中のコードの説明とかははじめに書いた参考記事に詳しくかいてますのでそちらを見てもらえればと思います。