2012年12月7日金曜日

BanchaとBanchaScaffoldを利用した簡単なCRUD実装のまとめ

BanchaとBanchaScaffoldを利用した簡単なCRUD実装のまとめ



この記事は、Sencha Advent Calendar 2012
の7日目の記事です。
皆さんも是非、是非、参加してください。後半、まだまだ参加枠ありますよ!!そこのあなた!!



数カ月前にBanchaを試した時の備忘録を残してあったんですが、

一つのテーブルに対するCRUD実装を通して--
Scaffoldを利用するまでをまとめて紹介してみます。

ほとんど過去の備忘録の焼き直しですけど、、 


Banchaってなんだろうか



  • PHPのフレームワークであるCakePHP2.0に対応したExtJs用のプラグイン

  • ExtDirect用のモデル(Proxy設定含む)を生成してくれる

というのが主なところだと思います。 

ExtDirectについては@martini3ozさんのブログでわかりやすい
解説がされているのでそちらを参照して下さい。
他にExtDirectに対応するPHPフレームワークだと株式会社ゼノフィが
作成しているxFrameworkPXがあります。
Directについて理解を深めたいのであればそちらを利用し、コードを
眺めてみると仕組みを深く理解できると思います。

サーバサイドはCake2.Xで組みたい。

フロントエンドはExtJs作りこみたい。

ExtJsDirectを気軽に使いたい。

そんな方にはオススメできるかもしれません。 

BanchaScaffoldってなんだろうか


ExtJsにおけるデータの入出力の基本的な機能として、Grid、Formがあります。

ともに、ExtDirectを利用する際には、Model(Proxy含む)やStoreを設定したり、

サーバサイドのメソッド名を設定する必要があります。

このあたりを吸収して、「ある程度」の雛形を提供してくれるのが

BanchaScaffoldです。 

Grid、Formをオーバーライドして特定のパラメータに

反応するように作成されています。

ですので構造さえ知ってしまえば細かな変更などができると思うので

便利な機能になるんじゃないかと思っています。


Banchaの情報


http://banchaproject.org/resources.html

このページがすべてのリソースが集約されててお勧めかもです。 

CRUD実装の大きな流れ


このお話の大きな流れは、

1:Cake2.Xの準備

2:ExtJsの準備

3:MySQLの準備

4:Banchaの準備

5:Bakeの実行

6:Scaffoldの利用

7:動作確認 

になります。

前提


MAMPでUserDir配下で試してます。

今回は最新版を試している時間がないので、以前試したことのある

* Bancha1.0.0

* BanchaScaffold0.5.6

* Cake2.1.5

* ExtJs4.1.1a

の組み合わせでお話をすすめます。 

今日(2012/12/7)現在、Banchaは1.2.3がBanchaScaffoldは0.6.0が最新のようです。  
CakeのDispacharの実装が2.1系と2.2系で異なるため、BanchaとCakeの組み合わせには  
注意が必要です。


1:Cake2.0の準備





ユーザディレクトリの配置


私の場合、ユーザ配下のUserDirがユーザディレクトリのルートになっているので

その下にadventcalendarとでも作っておきましょう。 

[ユーザ]/UserDir/adventcalendar  

で、ルートを public_html としてあるので、それも作っておきます。 

[ユーザ]/UserDir/adventcalendar/public_html  

Cake2.XのDL


2.1.5をDLしてきます。

過去分なので、githubからタグされたバージョンで持ってきちゃいました。

こちらから

解凍して public_html 配下にまるっとコピーしておきます。 

▾ public_html/
  ▸ app/
  ▸ lib/
  ▸ plugins/
  ▸ vendors/
    .gitignore
    .htaccess
    .travis.yml
    build.properties
    build.xml
    index.php
    README

ユーザディレクトリ使ってるのでCakeの階層にしたがってRewriteBaseしてあげます。

このあたりはご利用になっている環境にあわせて適宜に設定してみて下さい。

過去の備忘録 

saltとseedの設定もしておきましょね。


2:ExtJsの準備





ExtJs4.1.1aのDL


ExtJsをDLしてきます。

解凍して適宜に配置します。 

ExtJSのスタートアップはゼノフィの技術情報ページや  
@martini3ozさんのページ、  
本家のチュートリアルを参照することをおすすめします。  

今回は public_html/app/webroot 配下に ext として配置しました。 



▾ public_html/
  ▾ app/
    ▸ Config/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~
    ▸ View/
    ▾ webroot/
      ▸ css/
      ▸ ext/    <--ここ
      ▸ files/
      ▸ img/
      ▸ js/
        .htaccess
        favicon.ico
        index.php
        test.php
      .htaccess
      index.php
  ▸ lib/
  ▸ plugins/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~



設置用のindex.htmlの配置


MVC構成にするわけじゃなくて単なる表示確認です。

とりあえずパスが通ってることだけ確認しておきます。 



▾ public_html/
  ▾ app/
    ▸ Config/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~
    ▸ View/
    ▾ webroot/
      ▸ css/
      ▸ ext/
        ~~~~~~~~~~~~~~~
        ~~~~~~~~~~~~~~~
      ▸ files/
      ▸ img/
      ▸ js/
        .htaccess
        bancha.php
        favicon.ico
        index.html  <--ここ
        index.php
        ~~~~~~~~~~~~~~~
        ~~~~~~~~~~~~~~~



404が出てないことだけ確認しておきましょ。

設置用のindex.htmlの内容


index.html  
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title id="page-title">adventcalendar</title>
        <link rel="stylesheet" type="text/css" href="./ext/resources/css/ext-all.css"/>
        <script type="text/javascript" charset="utf-8" src="./ext/ext-all.js"></script>
    </head>
    <body>
        <p>hoge</p>
    </body>
</html>


3:MySQLの準備





DB、テーブル作成


次にDBを作ってMySQLにサンプル用のテーブルを作ってみましょう。 

DB名:adventcalendar
CREATE TABLE `calender` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `date` date NOT NULL,
  `acount` varchar(255) NOT NULL,
  `comment` text NOT NULL,
  `created` date NOT NULL,
  `modified` date NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

こんな具合でしょうか。 

Cakeへの設定


Cakeに利用するDB情報を設定します。

/app/Config/.database.php.defaultがあるので

/app/Config/.database.php

とコピーしてから、中身を書き換えます。 

public $default = array(
    'datasource' => 'Database/Mysql',
    'persistent' => false,
    'host' => 'localhost',
    'login' => '*****',
    'password' => '*****',
    'database' => 'adventcalendar',
    'encoding' => 'utf8',
);



▾ public_html/
  ▾ app/
    ▾ Config/
      ▸ Schema/
        acl.ini.php
        acl.php
        bootstrap.php
        core.php
        database.php  <-- ここ
        database.php.default




4:Banchaの準備





Bancha1.0.0をDLしてきます。

過去分なので、githubからタグされたバージョンで持ってきます。

こちらから。 

Banchaの設置


解凍して public_html/app/Plugin/Bancha に配置します。 



▾ public_html/
  ▾ app/
    ▸ Config/
    ~~~~~~~~~~~~~~~
    略
    ~~~~~~~~~~~~~~~
    ▸ Model/
    ▾ Plugin/
      ▾ Bancha/  <--ここ
        ▸ _app/
        ▸ Config/
        ~~~~~~~~~~~~~~~
        略
        ~~~~~~~~~~~~~~~
    ▸ Test/
    ▸ tmp/
    ~~~~~~~~~~~~~~~
    略
    ~~~~~~~~~~~~~~~



プラグインの設定


プラグインの設定はこちらを参考に行います。 

/public_html/app/Config/bootstrap.phpに 

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

/publichtml/app/Plugin/Bancha/app/webroot/bancha.php



/public_html/app/webroot/bancha.php

にコピー。 

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/~adventcalendar/Bancha/setup-check.html

こんな具合になればOKです。 

bancha疎通確認

5:Bakeの実行





ここまで来るとあとは実装が残るだけとなります。

Bakeというバッチを使用してCakePHPのModel,Contollerを作成します。 


長くなってしまうので省略しますが、ヤルことは

こちらとほぼ一緒です。 

コンソールからappフォルダ配下に移動してbakeを実行します。 

cd UserDir/public_html/app
./Console/cake bake

DBの接続情報を作る


(cake設定時に作成しているのであればここでは作らなくてもOK)

Modelを作る[M]


(作ったあとに、モデルに下記[<--ここ]を追加して下さい。これによりモデルが公開されることになります。)
Calendar.php
<?php
App::uses('AppModel', 'Model');
/**
 * Calendar Model
 *
 */
class Calendar extends AppModel {

    /**
     * Bancha behaviour
     */
    public $actsAs = array('Bancha.BanchaRemotable');  <-- ここ
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~

Controllerを作る[C]


(Would you like to create some basic class methods  
(index(), add(), view(), edit())? (y/n) 
と聞かれた時に「y」として下さい。)  
(templateの選択を問われますので「bancha」を選択して下さい)



▾ public_html/
  ▾ app/
    ▸ Config/
    ▸ Console/
    ▾ Controller/
      ▸ Component/
        AppController.php
        CalendarsController.php  <--ここ
        ContollersController.php
        PagesController.php
    ▸ Lib/
    ▸ Locale/
    ▾ Model/
      ▸ Behavior/
      ▸ Datasource/
        AppModel.php
        Calendar.php  <--ここ
    ▸ Plugin/
    ▸ Test/
    ▸ tmp/
    ▸ Vendor/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~



ここまででサーバサイドの設定などの準備は完了です。


6:Scaffoldの利用





Scaffoldの準備


ScaffoldをDLしてきます(0.5.6)。

webroot配下に設置しました。 



▾ public_html/
  ▾ app/
    ▸ Config/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~
    ▸ View/
    ▾ webroot/
      ▾ bancha/ <-- ここの配下のこと
          bancha-scaffold-debug.js*
          bancha-scaffold-production.js*
          bancha-scaffold.js*
      ▸ css/
      ▸ ext/
        ~~~~~~~~~~~~~~~
        ~~~~~~~~~~~~~~~



Extjsの準備


MVC構成としてのフォルダ配置をしておきます。

フォルダ構成の話とかだと、、手っ取り早いのは

ゼノフィの技術者ブログか 

ExtJsのサンプル:

public_html/app/webroot/ext/examples/MVC/pandora/ 

を持ってきてもいいかと思います。ちょっとここは省略。



▾ public_html/
  ▾ app/
    ▸ Config/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~
    ▾ webroot/
      ▸ css/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~
      ▾ js/
        ▾ app/  <--ここの配下のことです。
          ▾ controller/
          ▾ model/
          ▾ store/
          ▸ view/
            app.js
          empty
        .htaccess



index.htmlへの準備


Banchaを利用するため


  • Bancha.jsを読み込みます

  • bancha-api.jsを読み込みます

BanchaScaffoldを利用するため


  • bancha-scaffold.jsを読みます



<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title id="page-title">adventcalendar</title>
        <link rel="stylesheet" type="text/css" href="./ext/resources/css/ext-all.css"/>
        <script type="text/javascript" charset="utf-8" src="./ext/ext-all.js"></script>
        <!-- include Bancha and the remote API -->
        <script type="text/javascript" charset="utf-8" src="Bancha/js/Bancha.js"></script>      <--ここ
        <script type="text/javascript" charset="utf-8" src="bancha-api.js"></script>            <--ここ
        <!-- include Bancha and the remote API -->
        <script type="text/javascript" charset="utf-8" src="bancha/bancha-scaffold.js"></script><--ここ
        <script type="text/javascript" charset="utf-8" src="bancha-api.js"></script>
        <script type="text/javascript" src="./js/app/app.js"></script>
    </head>
    <body>
    </body>
</html>



いよいよScaffoldを利用します


Calendar用のGridを一つ作成します。 

Calendargrid.js

Ext.define('ADV.view.Calendargrid', {
    extend : 'Ext.grid.Panel',
    alias  : 'widget.calendargrid',
    scaffold :'Bancha.model.Calendar'  <-- ここがみそ。
});

Viewportに設置します。

Viewport.js

Ext.define('ADV.view.Viewport', {
    extend: 'Ext.container.Viewport',
    requires: [
        'ADV.view.Calendargrid',
    ],
    initComponent: function() {
        this.items = [{
            xtype:'calendargrid'
        }];
        this.callParent();
    }
});

CSSファイル作って削除用ボタンのパスを通しておきます。

style.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;
}

index.html

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title id="page-title">adventcalendar</title>
        <link rel="stylesheet" type="text/css" href="./ext/resources/css/ext-all.css"/>
        <link rel="stylesheet" type="text/css" href="./js/resources/css/style.css"/>  <--ここ
        ~~~~~~~~~~~~~~~
        ~~~~~~~~~~~~~~~

あと、Grid内のactioncolumnの画像はパス指定になっているので
それも変更します。(Installationに載ってました。。)

app.js

Ext.Loader.setConfig({
    enabled: true,
    paths: {
        'Ext': './ext/src',
        'ADV': './js/app'
    }
});
Bancha.onModelReady([
    // ここにモデル名指定
    'Calendar'
        ],function(){
    // {{{ Ext.application
    Ext.application({
        name: 'ADV',
        appFolder: './js/app',
        autoCreateViewport: true,
        launch: function() {
            alert('app launch!!');
        }
    });
});
Bancha.scaffold.Grid.destroyButtonConfig.items[0].icon =
                  './js/resources/icons/delete.png';



▾ public_html/
  ▾ app/
    ▸ Config/
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~
    ▸ View/
    ▾ webroot/
      ▸ bancha/
        ~~~~~~~~~~~~~~~
        ~~~~~~~~~~~~~~~
      ▾ js/
        ▾ app/
          ▸ controller/
          ▸ model/
          ▸ store/
          ▾ view/
              Calendargrid.js   <-- ここ
              Viewport.js       <-- ここ
            app.js              <-- ここ
        ▾ resources/
          ▾ css/
              style.css         <-- ここ
          ▾ icons/              <-- ここ
              add.png           
              arrow_undo.png
              delete.png
              disk.png
          empty
        .htaccess
        bancha.php
        favicon.ico
        index.html              <-- ここ
    ~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~




7:確認





こんな感じになると思います。

実行画面


画面はまだ簡素ですが、

これでサーバとの通信ができることになります。

とっても単純なCRUD実装ですが、

独自のカスタマイズができるように

基底クラスを作成できれば使いやすくなりますね。 


scaffoldはformにも対応しているので

さっと作りたいときなどは重宝するのではないでしょうか。 


あとは慣れてきたら

APIドキュメント見ながらソースと照らし合わせながら

遊んでみると面白いと思います。。

http://scaffold.banchaproject.org/

http://scaffold.banchaproject.org/docs/# 



駆け足でしたが

こんな具合でBanchaはお手軽に導入できます。

っということで@martini3ozさんからのフリがあったのでつないでみました。 


このBanchaは先の@marini3ozさんの記事、Proxyについてを

読んでからみるとなかなかおもしろいと思います。 


ほんとは、このあとBanchaScaffoldをいじってカスタマイズする記事を

書きたかったのですが、今、高熱出してまして続きが書けませんでした。。

もうむりっす。。 ごめんなさい。


さてさて、残すは青さんかな(笑 

2012年9月18日火曜日

bancha scaffoldで独自のボタンを追加するときのscope設定(this)

Scaffoldのbuttonsに独自のボタンを設定するとき、 scope:'scaffold-scope-me'を指定。 するとthisはgetPanel(),getForm()を有するオブジェクトを返すから、 そっからfireEventしてContorllerで引っ掛ける。
buttons:[{
    text: 'search',
    scope: 'scaffold-scope-me',
    handler : function(){
        var me = this;
        me.getPanel().fireEvent('search');
    }
}],
Scaffoldで検索用のフォームとか、独自のボタンをくっつけるときのこと。

2012年9月8日土曜日

markdown2impressを触ってみた。

社内でちょっとしたことをやるのでパワポ使ってもいいんですがせっかくだし、Markdown紹介がてら。。なぁ。。。

とおもって少しだけ変わったことでもしようかと。

markdownをimpressjs化する方法が こちらや、 こちらで紹介されていたのでこれをやってみようかと思います。 参考にさせていただきました。ありがとうございます!

両サイトを参考にさせてもらいつつ、試してみます。

環境

perlって入ってるんだっけか?

まずはそこなんだけど、ゴリゴリ入れます。

$sudo port install perl5

環境入れます。

git://github.com/yoshiki/markdown2impress.git

をクローンします。それだけ。

$git clone git://github.com/yoshiki/markdown2impress.github

実行して環境が整ってないことで怒られる。

$ ./bin/markdown2impress.pl README.md                                  12-09-08
Can't locate Data/Section/Simple.pm in @INC (@INC contains: /opt/local/lib/perl5/site_perl/5.12.4/darwin-thread-multi-2level /opt/local/lib/perl5/site_perl/5.12.4 /opt/local/lib/perl5/vendor_perl/5.12.4/darwin-thread-multi-2level /opt/local/lib/perl5/vendor_perl/5.12.4 /opt/local/lib/perl5/5.12.4/darwin-thread-multi-2level /opt/local/lib/perl5/5.12.4 /opt/local/lib/perl5/site_perl /opt/local/lib/perl5/vendor_perl/5.12.3 /opt/local/lib/perl5/vendor_perl .) at ./bin/markdown2impress.pl line 6.
BEGIN failed--compilation aborted at ./bin/markdown2impress.pl line 6.

いやーんな感じです。

が、 ホント助かります。CPANって言うアーカイブからインストールするんですね。。 超助かりますし、しらんこと多すぎだし。そもそもPerl環境すらわからんし。。 お世話になりました。こちら

実行しつつ、怒られたものをいれてきます。

$ sudo cpan Data/Section/Simple.pm 
$ sudo cpan Text/Markdown.pm
$ sudo cpan Text/Xslate.pm
$ sudo cpan Path/Class.pm

実行した結果。

$ ./bin/markdown2impress.pl README.md                                  12-09-08
froggugugugu@sasakiMBP:~/markdown2impress
$ ls                                                                   12-09-08
README.md   bin     css     index.html  js

見てみた。

sugeeee.

これはこれからも使わせて頂きます。 たのしいわ。

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`

ひとまず完了

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

node + ejs + Socket.io のサンプル

まずい。。 一年間、一回も更新してない!!! つまり、、 趣味の時間がほぼ皆無ということの現れです。 業務と子育てに追われた一年でしたね。。。。。 危なく年をこすところだった。あぶない。なぶあい。 話を変えて。 今月初旬に 社内でちょっとしたプレゼンをする事に...