2012年8月27日月曜日

bancha scaffoldを触ってみた

Scaffoldを

Banchaをググってたらフォーラムに到着して
出会いました。
見てみたら、マーケットにありますね。。 早速ソースをDLしてざっと眺めるとどうやら
Banchaの仕様に適した(サンプルをデフォルトで実装してくれる様な)
コンフィグビルダ的なプロジェクトでした。 ざっくり言うとExt.grid.Panelをオーバライドして scaffoldコンフィグに反応するようにしてあって BanchaにあったGridコンフィグ、Formコンフィグを作ってくれる 仕組みです。 独自の実装はbeforeBuildなり、afterBuildなりで調整実装します。 早速お試しを。

設置

どこに設置するのが良いのかわからなかったけど、 ひとまずextと同階層に設置することしました。
▾ banchajs/ <--(これ)
    bancha-scaffold-debug.js
    bancha-scaffold-production.js
    bancha-scaffold.js
▸ css/
▸ ext/
▸ files/
▸ img/
▸ js/
  .DS_Store
  .htaccess
  bancha.php*
  favicon.ico
  index.html
  index.php
  test.php

ロード

index.htmlに追加します。しばらくはコード見つつ使うと思うのでデバグ版です。
<script type="text/javascript" charset="utf-8" src="./banchajs/bancha-scaffold-debug.js"></script>

サンプルを改造(View)

ビルドコンフィグ内で、デフォルトの設定をしてくれているので なんもいじらんのであれば、、
// {{{ 'BE.view.Usergrid'
/**
 * @class BE.view.Usergrid
 * desctription
 */
Ext.define('BE.view.Usergrid', {
    // {{{ extend

    extend : 'Ext.grid.Panel',

    // }}}
    // {{{ alias

    alias : 'widget.usergrid',

    scaffold :'Bancha.model.User'

    // }}}
});
// }}}
これだけでOK。そう、これだけでOKです。 scaffoldにBanchaが提供するモデル名を設定します。
※このscaffold、オブジェクト指定でscaffold:{target:'Bancha.model.User'}として 実装することも可能ですが、その際はModelManagerにすでに登録されていることが前提となるので 何らかの問題で引っかかったときはこのあたりを見てみるといいかもです。
このパネルのinitConponentなりでBancha.getModel('User')とかして ModelManagerに登録しておくひつようがありますね。。

Store(サンプルを改造)

実はストアはBanchaのScaffoldがModelを利用して生成してくれるので Storeは必要ありません。削除しちゃいます。(わかりやすくコメントアウトとしておきます)

Controller(サンプルを改造)

各ボタンの実装もハンドラで直接結びつけちゃっているので この例だとコントローラも必要なくなっちゃいます。 (通常は必要になるとは思うのですけど)

実行してみる(画像が出ない)

画像が出ない。。。 そう、内部で設定される画像の設定、パスが通ってません。 icon-add、icon-save、icon-reset、icon-deleteをCSSに定義しておきます。
.icon-add {
    background-image: url(../icons/add.png) ! important;
}
.icon-delete: {
    background-image: url('../icons/delete.png') ! important;
}
.icon-save {
    background-image: url('../icons/disk.png') ! important; 
}
.icon-reset {
    background-image: url('../icons/arrow_undo.png') ! important; 
}
あと、Grid内のactioncolumnの画像はパス指定になっているので それを変更しなくてはなりません。。 なんかとりあえずの対応になってしまうけど、、
afterBuild : function (gridconfig) {

    // 削除ボタンコンフィグのreconfig
    var i;
    for (i = 0; i< gridconfig.columns.length; i++ ){
        if (gridconfig.columns[i].xtype == 'actioncolumn'){
            Ext.apply(gridconfig.columns[i].items[0],{icon:'js/src/resources/icons/delete.png'});
        }
    }
}
ひとまずxtypeで引っ掛けてパス変えちゃいました。
内部で固定設定されているメッセー文言とか、、
そとに口を引っ張りだして加工できるようにしたいですね。

userテーブルのBanchaScaffoldでの表示

現状況

▾ public_html/
  ▾ app/
    ▸ Config/
    ▸ Console/
    ▸ Controller/
    ▸ Lib/
    ▸ Locale/
    ▸ Model/
    ▸ Plugin/
    ▸ Test/
    ▸ tmp/
    ▸ Vendor/
    ▸ View/
    ▾ webroot/
      ▾ banchajs/
          bancha-scaffold-debug.js
          bancha-scaffold-production.js
          bancha-scaffold.js
      ▸ css/
      ▸ ext/
      ▸ files/
      ▸ img/
      ▾ js/
        ▾ src/
          ▾ app/
            ▾ controller/
                Main.js
                Usergrid.js
            ▾ model/
            ▾ store/
                user.js
            ▾ view/
                Usergrid.js
                Viewport.js
          ▾ resources/
            ▾ css/
                style.css
            ▾ icons/
                add.png*
                arrow_undo.png*
                delete.png*
                disk.png*
                image_add.png*
                user_edit.png*
            app.js
          empty
        .DS_Store
        .htaccess
        bancha.php*
        favicon.ico
        index.html
        index.php
        test.php
      .DS_Store
      .htaccess
      index.php
  ▸ lib/
  ▸ plugins/
  ▸ vendors/
    .gitignore
    .htaccess
    .travis.yml
    build.properties
    build.xml
    index.php
    README

2012年8月19日日曜日

banchaを触ってみた4

疎通させてみる

Bancha使ってみたくて
cakeをざっと流し読みしたけど、
まだ疎通できてないので疎通させてみようと思います。
お題はBanchaのサンプルをMVCに再配置、と、
テスト的にコンポーネントからイベント発火させて
Mainコントローラでディスパッチするようにしてみました。
A.View(何らかの操作)

Aコントローラ(ビューをコンポーネントクエリで引っ掛けて)
Ext既存イベントの処理→A.fireEventで独自イベント発行

Mainコントローラ(ビューをコンポーネントクエリで引っ掛けて)
独自インベントのディスパッチ
AコントローラをgetControllerで取得してAコントローラ.実装処理を呼び出し
なかんじで複数コントローラ時な感じを想定して見ました。

アプリの作成

ここからはExtJS4の話になります。
xframeworkであればwebapp下にsrcとか掘ってアプリを設置するんですが、
Cakeで言うところだと、、、
webroot/js/srcとかになるのでしょうかね。
あとは株式会社ゼノフィさんの技術ページ(code:x)か@martini3ozさんの
ページを見ながら実践開発ガイドのクラスローダ部分を読み返せばスタートアップ設置はOKです。
大掛かりな設置をすると大変なので
ビューポートにGridのみ入れて
Banchaのサンプルを切り出しながら再整形して
CRUDの確認だけしてみます。

example.htmlについて

やることは
  • extのロード
  • Banchaの準備
ということ。
<head>
    <title>Bancha Example</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
    <meta http-equiv="Content-Style-Type" content="text/css" /> 
    <meta http-equiv="Content-Script-Type" content="text/javascript" /> 

    <link rel="stylesheet" type="text/css" href="./ext/resources/css/ext-all.css" />

    <script type="text/javascript" src="./ext/ext-all-debug.js"></script>
    <script type="text/javascript" src="./ext/locale/ext-lang-ja.js"></script>

    <!-- include Bancha and the remote API -->
    <script type="text/javascript" src="./Bancha/js/Bancha.js"></script>
    <!-- for the github version use /Bancha/js/Bancha.js -->
    <script type="text/javascript" src="./bancha-api/models/all.js"></script>

    <!-- ExampleApp の設置 -->
    <script type="text/javascript" src="./js/src/app.js"></script>

</head> 
<body> 
</body> 
</html>
こんな感じになるのでしょうか。 404帰ってきてないこと確認しておきます。

app.jsとviwportの設置

  • addProviderの関係上、先行してExt.OnReadyが走る必要があり、
    これはBancha.OnModelReady内でExt.OnRadyしている。  
ので、OnModelReadyのコールバックでExt.applicationしてやることに
してみました。
設置については、、
* snippetsでひな形配置して
* namespace決めて
* app名決めて
* launchにalertとか設置して動作確認。
* 最初はcontrollersプロパティをコメントアウトして表示される
のを確認してもいいかもしれないです(ブランク開くと表示すら躓くのでひとつづ表示させながら)
で、
こんなかんじになると思います。

  • app.js
アプリケーションについてはローダーの設定とか重要なところになります。
これは実践開発ガイドの77ページ目のローダの説明を見ると理解が深まります。
Ext.ns('BE');

// Ext.Loader有効化
Ext.Loader.setConfig({
    enabled: true,
    paths: {
        'Ext': './ext/src',
        'BE': './js/src'
    }
});

//Bancha 初期化
Bancha.onModelReady('User',function(){

    //alert('hoge');

    // アプリケーション設定
    Ext.application({

        // Viewport自動生成
        autoCreateViewport: true,

        // アプリケーション名
        name: 'BE',

        // アプリケーションフォルダパス
        appFolder: './js/src/app',

        // 使用コントローラー定義
        controllers: [
        'Main',
        'Usergrid'
        ],

        // アプリケーション起動時イベントハンドラ
        launch: function() {

            //alert('fuga');

        }

    });
});
サーバサイドのモデルが増えたら、、
onModelReadyの第一パラメタも増やしていきます。
Bancha.onModelReady(['User','hoge1','hoge2'],function(){
みたいにね。

  • viewport.js
ビューポートはCenterにグリッドのみ設置。xtypeで指定します。
// {{{ BE.view.Viewport

Ext.define('BE.view.Viewport', {

    // {{{ requires

    requires: [
        'BE.view.Usergrid'
    ],

    // }}}
    // {{{ extend

    extend: 'Ext.container.Viewport',

    // }}}
    // {{{ layout

    layout: {
        type: 'border',
        padding: 5
    },

    // }}}
    // {{{ items

    items: [{

        region: 'center',
        //html: 'hoge'
        xtype: 'usergrid'

    }]

    // }}}

});

// }}}

Controllerの設置

上記のcontrollersをコメントインして(最初はコメントアウトしてhogeだけ表示させてた)Main,Usergridのコントローラを作成。
* MainControllerの設置をします。
* UsergridControllerの設置をします。
* これもスニペットで作成。設置のみ。ほとんどコメントアウト状態。以下、例。

  • Main.js
メインコントローラはコンポーネントからの独自イベントをディスパッチするように しておきます。
// {{{ 'BE.controller.Main',
/**
 * @class BE.controller.Main
 * メインコントローラ
 */
Ext.define('BE.controller.Main', {
    // {{{ extend

    extend : 'Ext.app.Controller',

    // }}}
    // {{{ refs インスタンス化されたViewへの参照提供 get([R]efname)
    /*
    refs: [{
        selector: 'xtype..',ref: 'localname'
    }],
    */
    /*
    // }}}
    // {{{ views クラスへの参照提供 get(Xxxx)View

    views:[
        'Views'
    ],

    // }}}
    // {{{ models クラスへの参照提供 get(Xxxx)Model

    models:[
        'Models'
    ],

    // }}}
    // {{{ stores インスタンスへの参照提供 get(Xxxx)Store

    stores: [
        'Store'
    ],

    // }}}*/
    // {{{ init

    //アプリケーション起動時にコールされる特別なメソッド
    //ApplicationのLaunch前にコールされる
    init: function() {

        //alert('hoge');
        var me = this;
        me.control(me.bindItem);

    },
    // {{{ bindItem
    bindItem: {

        // 作成
        'usergrid' : {
            create: function(){
                var me = this;
                alert('create dispatch');
                me.getController('Usergrid').onCreate();
            },
            reset: function(){
                var me = this;
                alert('reset dispatch');
                me.getController('Usergrid').onReset();
            },
            save: function(){
                var me = this;
                alert('save dispatch');
                me.getController('Usergrid').onSave();
            },
            del: function(rowIndex){
                var me = this;
                alert('save dispatch');
                me.getController('Usergrid').onDelete(rowIndex);
            }

            /*
            hoge: function(){
                var me = this;
                me.getController('Hoge').onHoge();
            },*/
        }

    }
/*
    // }}}
    // }}}
    // {{{ onXXXX

    onXXXX: function() {
        console.log('hoge');
    }
*/
    // }}}

});
// }}}

  • Usergrid.js
refとか汎用の名前にしたり、onXXとして実装と分離して抽象クラスとして
沈み込ませてそれを継承させたらすっきりするかなぁ。とおもってます。
// {{{ 'BE.controller.Usergrid',
/**
 * @class BE.controller.Usergrid
 * ユーザグリッドコントローラ
 */
Ext.define('BE.controller.Usergrid', {
    // {{{ extend

    extend : 'Ext.app.Controller',

    // }}}
    // {{{ refs インスタンス化されたViewへの参照提供 get([R]efname)

    refs: [{
        selector: 'usergrid',ref: 'crudgrid'
    }],

    // }}}
    // {{{ views クラスへの参照提供 get(Xxxx)View

    views:[
    ],

    // }}}
    // {{{ models クラスへの参照提供 get(Xxxx)Model
    // }}}
    // {{{ stores インスタンスへの参照提供 get(Xxxx)Store

    stores: [
        'User'
    ],

    // }}}
    // {{{ init

    //アプリケーション起動時にコールされる特別なメソッド
    //ApplicationのLaunch前にコールされる
    init: function() {

        var me = this;
        me.control(me.bindItem);

    },

    // }}}
    // {{{ bindItem
    bindItem: {

        // 作成
        'usergrid button[action=create]' : {
            click: function(){
                var me = this;
                alert('create');
                me.getCrudgrid().fireEvent('create');
            }
        },
        // リセット
        'usergrid button[action=reset]' : {
            click: function(){
                var me = this;
                alert('reset');
                me.getCrudgrid().fireEvent('reset');
            }
        },
        // 保存
        'usergrid button[action=save]' : {
            click: function(){
                var me = this;
                alert('save');
                me.getCrudgrid().fireEvent('save');
            }
        },
        // 削除
        'usergrid actioncolumn' : {
            click: function(view,cell,row,col,e){
                var me = this;
                alert('delete');
                me.getCrudgrid().fireEvent('del',row);
            }
        }

    },
    // }}}
    // {{{
    onCreate: function() {
        var me = this;
        me.create();
    },
    onSave: function() {
        var me = this;
        me.save();
    },
    onDelete: function(rowIndex) {
        var me = this;
        me.del(rowIndex);
    },
    onReset: function() {
        var me = this;
        me.reset();
    },
    // }}}
    // {{{
    create: function() { // scope is a config object
        var me = this,
            grid = me.getCrudgrid(),
            edit = grid.getPlugin('cellplugin'),
            store = grid.getStore(),
            model = store.getProxy().getModel(),
            rec;

        // Cancel any active editing.
        edit.cancelEdit();

        // create new entry
        rec = Ext.create(Ext.ClassManager.getName(model),{});

        // add entry
        store.insert(0, rec);

        // start editing
        edit.startEditByPosition({
            row: 0,
            column: 0
        });
    },
    save: function() { // scope is the store
        var me = this,
            valid = true,
            msg = "",
            name,
            grid = me.getCrudgrid(),
            store = grid.getStore();

        // check if all changes are valid
        store.each(function(el) {
            if(!el.isValid()) {
                valid = false;
                name = el.get('name') || el.get('title') || (el.phantom ? "New entry" : el.getId());
                msg += "<br><br><b>"+name+":</b>";
                el.validate().each(function(error) {
                    msg += "<br>&nbsp;&nbsp;&nbsp;"+error.field+" "+error.message;
                });
            }
        });

        if(!valid) {
            Ext.MessageBox.show({
                title: 'Invalid Data',
                msg: '<div style="text-align:left; padding-left:50px;">There are errors in your data:'+msg+"</div>",
                icon: Ext.MessageBox.ERROR,
                buttons: Ext.Msg.OK
            });
        } else {
            // commit create and update
            store.sync();
        }
    },
    del: function (rowIndex) {
        var me = this,
            grid = me.getCrudgrid(),
            store = grid.getStore();
        rec = store.getAt(rowIndex),
        name = Ext.getClassName(rec);

        // instantly remove vom ui
        store.remove(rec);

        // sync to server
        // for before-ExtJS 4.1 the callbacks will be ignored,
        // since they were added in 4.1
        store.sync({
            success: function(record,operation) {

                Ext.MessageBox.show({
                    title: name + ' record deleted',
                    msg: name + ' record was successfully deleted.',
                    icon: Ext.MessageBox.INFO,
                    buttons: Ext.Msg.OK
                });
            },
            failure: function(record,operation) {

                // since it couldn't be deleted, add again
                store.add(rec);

                // inform user
                Ext.MessageBox.show({
                    title: name + ' record could not be deleted',
                    msg: operation.getError() || (name + ' record could not be deleted.'),
                    icon: Ext.MessageBox.ERROR,
                    buttons: Ext.Msg.OK
                });
            }
        }); //eo store sync
    },
    reset: function() { // scope is the store
        // reject all changes
        var me = this,
            grid = me.getCrudgrid(),
            store = grid.getStore();

        store.each(function(rec) {
            if (rec.modified) {
                rec.reject();
            }
            if(rec.phantom) {
                store.remove(rec);
            }
        });
    }
});
// }}}

storeの設置

ストアの設置です。 で、いままでProxyの設定はStoreでやってたんですけど、、 Banchaが提供するModelにはすでにCakeにあわせたProxyの設定が 盛り込まれたModelを返却するのでストアはBanchaの提供するモデルを 設置するのみです。
// {{{ 'BE.store.User',
/**
 * @class BE.store.User
 * desctription
 */
Ext.define('BE.store.User', {
    // {{{ extend

    extend : 'Ext.data.Store',

    // }}}
    // {{{ model

    model: Bancha.getModel('User'),
    autoLoad: true

    // }}}

});
// }}}

modelの設置

モデルは要らないっす。 ModelはBanchaが提供してくれますので。 Bancha.getModel(‘モデル名’); で取得可能です。
または、Bancha.model.モデル名ですかね。。。

これで全部載せたかな。。。

all.jsからの返却

rpc用のメソッド一覧等の情報がちゃんとできているか見てみます。 all.jsのレスポスンスをJavaScriptBeautifierで整形してみました。 よさそうです。
Ext.ns('Bancha');
Bancha.REMOTE_API = {
    "url": "\/~banchatest\/bancha.php",
    "namespace": "Bancha.RemoteStubs",
    "type": "remoting",
    "metadata": {
        "User": {
            "idProperty": "id",
            "fields": [{
                "name": "id",
                "type": "int"
            }, {
                "name": "username",
                "type": "string"
            }, {
                "name": "password",
                "type": "string"
            }, {
                "name": "first_name",
                "type": "string"
            }, {
                "name": "last_name",
                "type": "string"
            }, {
                "name": "created",
                "type": "date",
                "dateFormat": "Y-m-d H:i:s"
            }, {
                "name": "modified",
                "type": "date",
                "dateFormat": "Y-m-d H:i:s"
            }],
            "validations": [],
            "associations": [],
            "sorters": []
            },
        "_UID": "5030537f74c0a675541393"
    },
    "actions": {
        "User": [{
            "name": "getAll",
            "len": 0
        }, {
            "name": "read",
            "len": 1
        }, {
            "name": "create",
            "len": 1
        }, {
            "name": "update",
            "len": 1
        }, {
            "name": "destroy",
            "len": 1
        }, {
            "name": "submit",
            "len": 1,
            "formHandler": true
        }],
        "Bancha": [{
            "name": "loadMetaData",
            "len": 1
        }]
        }
}
※注目すべきはxframeworkでもハマった
"formHandler": true
これ。これは.formのAPIに設定する用のハンドラにはこれをつけます。 xframeworkの時はドキュメントコメントに記載すると フレームワークがパースしてこのフラグを立ててくれてました。 超ハマったのでよく覚えてます。

実行してみる。





まとめ

で、、 プロトタイプを作成しよう!となると、
  1. UserDirの設置
  2. cake2.1.5の設置
  3. htaccessの変更
  4. Bancha0.9.5の設置(cake配下のPlugin下)
  5. bake実施(モデルとコントローラ)
  6. bakeで生成したモデルに追記
    public $actsAs = array(‘Bancha.BanchaRemotable’);
  7. Extjsの設置(cake配下app/webroot/js/配下)
  8. ExtJsMVCの設置
こんなかんじかな。
cake→banchaと夜な夜な帰宅後にやったわりには理解は思ったより早くてすっきりしてきた。 これを業務にフィードバックできればいいんだけど、
あとは、banchascaffoldだね。

感想

久々のMVCの設置、スニペットがあるのと無いのでは雲泥の差だし、
何より先の技術サイトのように一気通貫で説明があると助かるなぁ。。
と思いこのエントリも自分用に再度まとめ直したし、サンプルも作り替えた。
疲れた。。。

2012年8月5日日曜日

banchaを触ってみた3

概要の概要

出来上がったコントローラ見ると、 リクエスト情報の内、isBaanchaとかをみてSaveAndXXXとか 呼び出している。。

のはいいのだけど、

最初のおおまかな流れがつかめない。 気になるのがRPC用のメソッド一覧の取得契機。

ほんの少しだけ見てみた。 もうほんとこまかなロジックはすっ飛ばして 「どこでやってんのかしらね。。」 程度です。

いつものaddProviderはどこ?

まず、 Plugin/Bancha/webroot/js/Bancha.js

initメソッドで

Ext.direct.Manager.addProvider()

しているところがありました。

で、こいつに渡しているのがメソッド一覧のオブジェクトになるから、 上を追うとgetRemoteApiがある これはthis.remoteapi(Bancha.REMOTEAPI)となる。 このBancha.REMOTEAPIを事前にリクエストしておくひつようがあるわけで。

initはonModelReadyでExt.OnReadyをコールし、そのなかでinitとされている。

つまり

Bancha.onModelReady => Ext.OnReady => init => addProvider

となることはわかった。

Bancha.REMOTE_APIはどこで生成?

上記で事前に取得しておくBancha.REMOTEAPIについてざっと見てみる。 そもそもこのBancha.REMOTEAPIという命名はWikiにもあるように Config設定にある(grep結果)

/UserDir/banchatest/public_html/app/Plugin/Bancha/Config/bootstrap.php|23 col 30| Configure::write('Bancha.Api.remoteApiNamespace','Bancha.REMOTE_API');

で、この設定が利用されているのは

/app/Plugin/Bancha/Controller/BanchaController.php|86 col 78| $this->response->body(sprintf("Ext.ns('Bancha');\n%s=%s", Configure::read('Bancha.Api.remoteApiNamespace'), json_encode($api)));

のこの部分。

で/app/Plugin/Bancha/Lib/Bancha/BanchaApi.php でモデル一覧取得して、 モデル名を引数にコントローラへアクセスしてCrud用のAction名を取得している。

このあたりはCake特有の命名規則でルーティングしてクラス名をつくりだしたりしているので 深追いせずに次行っちゃいます。

つまり、

モデル名一覧 => モデル名からコントローラ => コントローラのCrud用メソッド一覧。

それを連想配列でredirect。

Ext.ns('Bancha'); Bancha.REMOTEAPI = jsonencodeした上記連想配列。

をredirect->bodyしているわけですね。

概要だけでも少し追うと安心できました。

そのBancha.REMOTE_APIはいつのリクエストで生成?

で、 このBancha.REMOTE_APIは、じゃぁ、いつのリクエストで生成されて ロードされているか。となるんだけど、、、 これは、bancha-api.jsを呼んで、 Banchaのルーティング設定でBanchaControllerのindexアクションが呼び出される 設定になってるっぽい(routes.php)。。(これはちょっと迷子になった。)

hoge.html内で

<!-- include Bancha and the remote API -->
<script type="text/javascript" src="Bancha/js/Bancha.js"></script>

※ここでBancha利用用のBanchaクラス群ロード


<script type="text/javascript" src="bancha-api.js"></script>

※これでサーバサイドでルーティングされてBanchaControllerのindexアクションコール
  返却でurl(RPC時のルーティングするurl)やメソッド一覧のJSON返却

このあとは各App用JSでOnModelReadyを実装してそのなかでaddProvider
ということはこのコールバック内に今までのエントリポイントとなる
メソッドを設置することになるでしょうかね。。

次は疎通。疎通~。

現状のフォルダ構成

▾ banchatest/
  ▾ public_html/
    ▾ app/
      ▸ Config/
      ▸ Console/
      ▸ Controller/
      ▸ Lib/
      ▸ Locale/
      ▸ Model/
      ▾ Plugin/
        ▾ Bancha/
          ▸ _app/
          ▾ Config/
              bancha-dispatcher-bootstrap.php
              bootstrap.php
              routes.php・・・・・・・・ここ
          ▸ Console/
          ▾ Controller/
              BanchaAppController.php
              BanchaController.php・・・・・・・・ここ
          ▾ Lib/
            ▾ Bancha/
              ▸ Exception/
              ▸ ExceptionHandler/
              ▸ Network/
              ▸ Routing/
                BanchaApi.php・・・・・・・・ここ
          ▸ Model/
          ▸ Test/
          ▾ webroot/
            ▾ js/
                Bancha.js*・・・・・・・・ここ
              setup-check.html
            .gitignore
            gpl-v3.txt
            license.txt
            README.md
            UpdateFromBeta3.txt
            VERSION.properties*
          empty*
      ▸ Test/
      ▸ tmp/
      ▸ Vendor/
      ▸ View/
      ▸ webroot/
        .htaccess*
        index.php*
    ▸ lib/
    ▾ plugins/
        empty*
    ▸ vendors/
      .gitignore*
      .htaccess*
      .travis.yml*
      build.properties*
      build.xml*
      index.php*
      README*

banchaを触ってみた2


bakeでBancha用のモデルとコントローラの作成

bakeで自動生成します。 まず、カレントディレクトリをappにして

cd UserDir/public_html/app

./Console/cake bake

を実行。

DB設定

Welcome to CakePHP v2.2.0 Console
---------------------------------------------------------------
App : app
Path: /Volumes/DataDrive/UserDir/banchatest2/public_html/app/
---------------------------------------------------------------
Interactive Bake Shell
---------------------------------------------------------------
[D]atabase Configuration
[M]odel
[V]iew
[C]ontroller
[P]roject
[F]ixture
[T]est case
[Q]uit
What would you like to Bake? (D/M/V/C/P/F/T/Q) 
> D
---------------------------------------------------------------
Database Configuration:
---------------------------------------------------------------
Name:  
[default] > 
Datasource: (Mysql/Postgres/Sqlite/Sqlserver) 
[Mysql] > 
Persistent Connection? (y/n) 
[n] > 
Database Host:  
[localhost] > 
Port?  
[n] > 
User:  
[root] > web
Password:  
> ************************ 
Database Name:  
[cake] > tasks
Table Prefix?  
[n] > 
Table encoding?  
[n] > UTF8

---------------------------------------------------------------
The following database configuration will be created:
---------------------------------------------------------------
Name:         default
Datasource:       Mysql
Persistent:   false
Host:         localhost
User:         web
Pass:         ******
Database:     tasks
Encoding:     UTF8
---------------------------------------------------------------
Look okay? (y/n) 
[y] > y
Do you wish to add another database configuration?  
[n] > n

Creating file /Users/froggugugugu/UserDir/banchatest/public_html/app/Config/database.php
Wrote `/Users/froggugugugu/UserDir/banchatest/public_html/app/Config/database.php`

次にモデル。

Welcome to CakePHP v2.2.0 Console
---------------------------------------------------------------
App : app
Path: /Users/froggugugugu/UserDir/cakephpTest/public_html/app/
---------------------------------------------------------------
Interactive Bake Shell
---------------------------------------------------------------
[D]atabase Configuration
[M]odel
[V]iew
[C]ontroller
[P]roject
[F]ixture
[T]est case
[Q]uit
What would you like to Bake? (D/M/V/C/P/F/T/Q) 
> M
---------------------------------------------------------------
Bake Model
Path: /Users/froggugugugu/UserDir/cakephpTest/public_html/app/Model/
---------------------------------------------------------------
Possible Models based on your current database:
1. TblPlan
2. TblProject
3. TblUser
Enter a number from the list above,
type in the name of another model, or 'q' to exit  
[q] > 3
What is the primaryKey?  
[Id] > 

.....略..................

.....途中でBancha用のテンプレート選択..................

Associations:
---------------------------------------------------------------
Look okay? (y/n) 
[y] > y
---------------------------------------------------------------
You have more than one set of templates installed.
Please choose the template set you wish to use:
---------------------------------------------------------------
1. bancha
2. default
Which bake theme would you like to use? (1/2) 
[1] > 1

Baking model class for TblUser...

Creating file /Users/froggugugugu/UserDir/banchatest/public_html/app/Model/TblUser.php
Wrote `/Users/froggugugugu/UserDir/banchatest/public_html/app/Model/TblUser.php`

で、モデル出来上がり。

次にコントローラ

What would you like to Bake? (D/M/V/C/P/F/T/Q) 
> C
---------------------------------------------------------------
Bake Controller
Path: /Users/froggugugugu/UserDir/banchatest/public_html/app/Controller/
---------------------------------------------------------------
Possible Controllers based on your current database:
---------------------------------------------------------------
 1. TblPlans
 2. TblProjects
 3. TblUsers
Enter a number from the list above,
type in the name of another controller, or 'q' to exit  
[q] > 3
---------------------------------------------------------------
Baking TblUsersController
---------------------------------------------------------------
Would you like to build your controller interactively? (y/n) 
[y] > 
Would you like to use dynamic scaffolding? (y/n) 
[n] > 
Would you like to create some basic class methods 
(index(), add(), view(), edit())? (y/n) 
[n] > y
Would you like to create the basic class methods for admin routing? (y/n) 
[n] > 
Would you like this controller to use other helpers
besides HtmlHelper and FormHelper? (y/n) 
[n] > 
Would you like this controller to use any components? (y/n) 
[n] > 
Would you like to use Session flash messages? (y/n) 
[y] > 

---------------------------------------------------------------
The following controller will be created:
---------------------------------------------------------------
Controller Name:
    TblUsers
---------------------------------------------------------------
Look okay? (y/n) 
[y] > y

Baking controller class for TblUsers...

Creating file /Users/froggugugugu/UserDir/banchatest/public_html/app/Controller/TblUsersController.php
Wrote `/Users/froggugugugu/UserDir/banchatest/public_html/app/Controller/TblUsersController.php`

ひとまず完了

さっそく出来上がったものを見て見ます。

banchaを触ってみた


banchaを使ってみる

banchaのサイトは

こちらから

インストールから

まずはCakeの設置。

(bancha:0.9.5はcake2.1.X(現在は2.1.5)に対応しています。
Dispacherの実装が2.2.Xだとことなるため、BanchaのDispatcherの実装が合いません::つまづきました。。)

設置といってもUserDirに配置して

Config/database.php

を適宜に設定。 ユーザディレクトリ利用しているのであれば、

public_html下の.htaccessに

RewriteBase    /~banchatest/

public_html/app下の.htaccessに

RewriteBase    /~banchatest/app/

public_html/app/webrot下の.htaccessに

RewriteBase    /~banchatest/app/webroot/

を追記してひとまず完了。

Banchaの設置

ダウンロードしてきたフォルダをそのまま

public_html/app/Plugin/Bancha

として設置。

bootstrap.phpに追記

public_html/app/Config/bootstrap.phpに

CakePlugin::load(array('Bancha' => array('routes' => true, 'bootstrap' => true))); 

を追記。

bancha.phpファイルコピー

/Users/froggugugugu/UserDir/banchatest/public_html/app/Plugin/Bancha/_app/webroot/bancha.php

/Users/froggugugugu/UserDir/banchatest/public_html/app/webroot/bancha.php

にコピー。

シンボリックリンクってのもあるんだろうけど、、、

$ sudo ln -s /Users/froggugugugu/UserDir/banchatest/public_html/app/Plugin/Bancha/_app/webroot/bancha.php /Users/froggugugugu/UserDir/banchatest/public_html/app/webroot/bancha.php

これだと内部のファイルパス取得部分でPlugin配下を参照するようになってる?? みたいだったのでもうやめてファイルコピーで対応。

public_html/app/Config/core.php

末尾に

/**
 * Configure the cache for Banchas Remote API.
 */
Cache::config('_bancha_api_', array(
    'engine' => $engine,
    'prefix' => $prefix . 'bancha_api_',
    'path' => CACHE . 'models' . DS,
    'serialize' => ($engine === 'File'),
    'duration' => $duration
));

を追記。

設置できたかの確認。

http://localhost/~banchatest/Bancha/setup-check.html

OK..

Cakeでつかう(Bakeでひな形作成)

Bachaでクライアントサイドに公開する方法として モデルの保有するメソッドの公開とコントローラが保有するメソッドの 公開があるんだけど、一般的にはモデル公開。 このあたりはxFrameworkPXでもやったことあるので理解は楽。

モデルの公開方法

bakeで用意します。 こちら

cakeを触ってみた2

前回の続き。

CakePHP2.0

こちらををベースに写経していきます。

Viewについて

ビューはctpという拡張子で
Viewフォルダ配下にアプリケーション名のフォルダを作成して
その配下に、アクション名のctpファイルを作ればいいんだろう。

でデータのバインドは
コントローラ側で
$this->set(‘変数名’,変数);
という具合でできる。

ビュー側では
そいつを<?php echo $変数名 ?>とかで出力する。

次。
モデルに行きます。

DB

MySQLでDB作ります。
よくあるtbl_Userとかのユーザテーブルにしました。
CakeではXXXdatasっていうテーブル名にするみたい。
でもマスタはmstリソース、イベント系はtblとかに
する事が多いのでここはtbl_Userとしておきます。

と思いきや、
cakeの特有の命名規約があるみたい。
テーブルにはsがついた複数形として
モデルはsがつかない。
というルールにしておけばよさそう。。

tbl_Users

とすれば

TblUser

というモデル名となります。
※このあとでfind(‘list’);ってやった時にフィールド名にデフォルトのモデル名が利用されてSQLエラーが
発生したけど、エラー情報からオブジェクトにaliasを設定すればよさそうなことがわかり、対応。

  • controller = 機能要件に沿ったアプリケーション名として
  • model ≒ DBテーブル名 ≒ 実世界のデータ・モデル名に近い命名

DB接続情報

/app/Config/.database.php.defaultがあるので  
/app/Config/.database.php
としてコピーして利用します。 で、$testは今のところ利用しないので削除しちゃいます。 ひとまず勉強のためにやってますが、これはあとでbakeで対話式に 設定可能。

modelについて

DBの説明でもあったように命名に癖がある。

クラス定義

Controllerクラスの定義はまず、

App::uses('AppController','Controller');  

として利用できるように定義、
AppControllerクラスを継承して定義するみたい。

これも結局あとでbakeで生成されるけど、いちどはやっとかんとね。

ひとまず、写経してどのフォルダに何入れるか。。
ってのと命名規則のさわりだけはOKとした。

時間ないのでCakeについてはここまで。

次はBancha。

2012年8月1日水曜日

cakeを触ってみた

先日、cakeを試してみようとおもってて、、
でもExtJSとの絡みを考えるとDirect使えんしなぁ
と呟いてたら@martini3ozさんがBanchaってのがそれっぽいよ。
と教えてくれました。
見てみます!と返信したもののCake自体が
チンプンカンプンなので入門の入門を触ってみました。

CakePHP2.0

事始め
こちらををベースに写経していきます。

ダウンロード~動かすまで

  1. DL
    2012/7/6現在の最新版のstableである2.2.0を使用してみます。
    こちらのTOP右側のリンクからDL可能。

※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
前のエントリでも書きましたが、Bancha0.9.5は2.2.Xシリーズには
対応しておらんようです。
※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※

  1. 設置
    UserDir有効のためUsrDir/cakephpTest/public_html配下にcakephpTestとして配置してみた。
    で、このままだとnotfoundになるのでpublick_html配下の.htaccessに

    RewriteBase /~cakephpTest/

必然的にその配下の関連する.htaccessも書きかえました。

public_html/app配下の.htaccess

RewriteBase /~cakephpTest/app/  

public_html/app/webroot配下の.htaccess

RewriteBase /~cakephpTest/app/webroot/  

を追記。

  1. 確認

    localhost:8080/~cakephpTest/

アクセスして確認。
* DBの設定
* セキュリティソルト、セキュリティシードはあと。で設定。

Controllerについて

コントローラは/app/Controller配下に作る。
コントローラは(アプリケーション)ごとに設置と理解。
で、コントローラ名は(アプリケーション名:パスカルケースにして)+Controller.phpとするらしい。
ex)hogehogeというアプリケーション→HogehogeController.php
コントローラ(アプリケーション)は複数のアクションをまとめたもの?として理解。

ファイル命名規則は、パスカルケースで、

XxxxxxController.php  

として作成すること。
クラスの命名規則も同様に。

クラス定義

Controllerクラスの定義はまず、

App::uses('AppController','Controller');  

として利用できるように定義、
AppControllerクラスを継承して定義するみたい。

アクション定義

publicメソッドがアクションとして認識されるようですね。
メソッド≒アクション。と。
基本、アクションが1URLに割り当てられる見たい。

アクション間遷移

  • リダイレクト

    public function index()
    {
    $this->autoRender = false;
    $this->redirect(‘./other’);/そのコントローラ(アプリケーション)内のotherメソッド(アクション)にリダイレクト/
    }

  • フォワード
    public function index()
    {
    $this->autoRender = false;
    $this->setAction(‘other’);/そのコントローラ(アプリケーション)内のotherメソッド(アクション)にフォワード(URLの変更は発生せず)/
    }

  • URLパラメタによる値渡し

    $this->redirect(‘./other/hoge/fuga/piyo’);

Modelの利用

モデル名がコントローラ名と一致した際に自動的に利用可能になるようです。
ただ、明示的に設定することも可能で、
var $uses = array(‘Recipe’, ‘User’);
のように$usesプロパティに指定することでインスタンス化されるようです。


お試しコード

<?php  

App::uses('AppController', 'Controller');  

// {{{ sampleController  
/**  
 * sampleController Class  
 *  
 * sample  
 *  
 * @category   CategoryName  
 * @package    PackageName  
 * @copyright  2011-2011  
 * @license    http://www.opensource.org/licenses/mit-license.html MIT License  
 * @version    Release: sample  
 */  
class SampleController  
extends AppController  
{  
    // {{{ const  
    // }}}  
        // {{{ props  
    // }}}  
    // {{{ __construct  
    /**  
     * コンストラクタ  
     *  
     * @param type argname discription  
     **/  
//    function __construct($argname)  
//    {  
//        # code...  
//    }  
//    // }}}  
//    // {{{ __destruct  
//    /**  
//     * デストラクタ  
//     **/  
//    function __destruct()  
//    {  
//        # code...  
//    }  
    // }}}  
    // {{{ index  
    /**  
    *  
    *  
    * @param type $argname discription  
    * @return type discription  
    */  
    public function index()  
    {  
        $this->autoRender = false;  
        //var_dump('index');  
        //$this->redirect('./other/hoge/fuga/piyo');  
        //$this->setAction('other','hoge','fuga','piyo');  
        $params = array(  
            'hoge' => array(  
                'hoge1' => 'hogehoge1',  
                'hoge2' => 'hogehoge2',  
                'hoge3' => 'hogehoge3',  
            ),  
            'fuga' => array(  
                'fuga1' => 'fugafuga1',  
                'fuga2' => 'fugafuga2',  
                'fuga3' => 'fugafuga3',  
            )  

        );  
        //$this->setAction('other','hoge','fuga','piyo');  
        $this->setAction('other',$params);  
    }  

    //public function other($a,$b,$c){  
    public function other($params){  
        $this->autoRender = false;  
        //var_dump('other');  
        //var_dump($a);  
        //var_dump($b);  
        //var_dump($c);  
        var_dump($params);  
    }  

    // }}}  
}  
// }}}