2014年11月3日月曜日

Looking for Lost on the Tokyo Metro

2015/11/14にこのアプリの公開を停止しました。

現在はこのアプリはご利用いただけません
Looking for Lost on the Tokyo Metroというアプリを開発しました。
この記事はLooking for Lost on the Tokyo Metroというアプリの告知兼サポートページになります。
またこのアプリは東京メトロアプリコンテストへ応募する予定です。

 
これは東京メトロAPIの列車ロケーション情報を使用した電車内に置き忘れた物を探すアプリです。
忘れ物をした時の情報を入力する事により忘れ物をした車両の位置をリアルタイムで探します。
駅係員の方や鉄道会社の遺失物担当者の方へ状況を伝える場合などにご利用ください。
 


使い方

使い方はとっとも簡単です。忘れ物に気付いた際に入力画面で乗降者した列車の沿線、駅名、進行方向、時間を入力 して探すボタンを押すだけです。サーバーへ列車を問い合わせし結果画面へ該当する列車があれば列車の情報が表示 されます。表示された内容をもとに駅係員様へお問い合わせください。

Movie


スクリーンショット




 

2014年10月24日金曜日

PhalconのDB\AdapterをDIへ登録する際の注意点

今まで勘違いしていたので覚えて置く為のメモ。

PhalconでDIへDb\Adapterを登録してtransactionを一元管理するのはできないのだと思っていたら 使い方が間違っていた。
DIへ登録する時にはshared(共有)として登録すれば呼び出し先で同じコネクションのインスタンス を使用してくれる。逆を言うとsharedしないと同じインスタンスを使用してくれないようだ。
具体的には
// 第三引数にtrueを指定
$di->set('db', function() use ($config) {
    return new \Phalcon\Db\Adapter\Pdo\Postgresql(
    $config->database->toArray());
}, true);
本当にトランザクションが制御できているか以下の方法で確認
DBにテーブルを用意する
CREATE TABLE name1 (
  id SERIAL PRIMARY KEY,
  name text
);
テスト登録用処理を実装したモデルクラスを用意する。
class Name1 extends \Phalcon\MVC\Model
{

    public $id;
    public $name;

    public function getSource()
    {
        return "name1";
    }

    /**
      * 引数のコネクションと関数内で生成したコネクション
      * でそれぞれ登録
      *
      */
    public static function insertTest($src_db){
        $name = new \App\Mvc\Model\Name1();
        $db = $name->getDI()->get("db");
        for($i=0;$i<5;$i++){
            $name = new \App\Mvc\Model\Name1();
            $name->name = 'FFFFF' . sprintf("%05d", $i);
            $name->create();
            $result = $db->query("INSERT INTO name1(name) VALUES(
                '" . "DDDDD" . sprintf("%05d", $i) . "')");
            $resulta = $src_db->query("INSERT INTO name1(name) VALUES(
                '" . "EEEEE" . sprintf("%05d", $i) . "')");

        }
    }
}
DIのsharedをtrueにしたものとしないものの両方で以下の処理を実行する。
具体的にはコントローラー、生成したモデルのテスト用関数で登録処理が行われ、例外を意図的に 発生させてrollbackを行う。
try{
   
    $this->db->begin();
    for($i=0;$i<5;$i++){
        $name = new \App\Mvc\Model\Name1();
        $name->name = 'AAAAAA' . sprintf("%05d", $i);
        $name->create();
                
        $result = $this->db->query("INSERT INTO name1(name) VALUES(
            '" . "BBBBB" . sprintf("%05d", $i) . "')");
    }
    \App\Mvc\Model\Name1::insertTest($this->db);
            
    throw new \Exception("trnsaction test");
    $this->db->commit();
}catch(Exception $e){
    if($this->db->isUnderTransaction()){
        $this->db->rollback();
    }
    throw $e;
}

sharedした場合の結果は
test=# select * from name1;
 id | name 
----+------
(0 行)

しなかった場合、インスタンスを別で取得して登録した分が残ってしまう。
test=# select * from name1;
 id |    name    
----+------------
 12 | DDDDD00000
 15 | DDDDD00001
 18 | DDDDD00002
 21 | DDDDD00003
 24 | DDDDD00004
(5 行)
一元管理したい場合はsharedをする事で可能になる。一番起きそうなのは一元管理するつもりだったのに sharedしないで登録してしまいtransactionがめちゃくちゃになってしまったという事故。

connectionのインスタンスはDB回りのクラスで管理されるものと思い込んでいたのでDIでやる発想が無かった。 こんな方法もあるんだな。

2014年10月14日火曜日

Google Play アプリ開発で参考にした本

Google Play版のPocket Trumpetterも無事リリースに至りました。

アプリ開発ではもはや名著「初めてのAndroid 第3版」を参考にしています。

初めてのAndroid 第3版

javaの基本的な知識は必要ですが、シンプルなアプリの開発ならこの本の例を作る過程で身につく
と思います(少なくとも私は身についたと思っています)。
強いて惜しいと思われるのは、内容はちょっと古いんじゃないかと思っています。
サンプルで使用されたライブラリや関数が非推奨になっていたり、Fragmentについては触れられなか
ったりと。まぁこんなものは大したマイナスではないと思いますが。

2014年8月31日日曜日

Phalconで強引なSubmodule

ここ最近、自称最速PHPフレームワークを謳っているphalconというのを
使っています。早い根拠はコアな部分をC言語で実装している為だそうです。
(SQLをログに出力すると意図しないクエリーをDBに流しているように見受けられ、遅くならないか? と心配になるが今回の趣旨とは異なるのでこれ以上触れません)

Laravelもチラ見したたけれども、最近のフレームワークはmoduleというくくりがちょっと 大きい単位になってしまっている気がしていまいち自分の意図と合わない。
個人的には関連するcontrollerやviewなどをある程度纏めた単位をsubmoduleとした構成にしたい。
かなり強引だけどnamespaceを使って実現してみる事に。
phalconのインストールからmodule構成まではphalconの公式ページを探せば見つかるので これまたここでは触れません。

先ずファイル構成はこんな感じ

├─app
│  ├─config
│  │      config.ini
│  │      loader.php
│  │      module.php
│  │      service.php                        # route処理を定義。
│  │                                         # ここでURLとmodule、name
│  │                                         # space、controller、acti
│  │                                         # onを関連付け
│  │
│  ├─frontend                               # frontend module
│  │  │  Module.php
│  │  │
│  │  ├─modules
│  │  │  ├─error                          # エラー処理用 submodule 
│  │  │  │  │                             # コントローラーのnamesp
│  │  │  │  │                             # aceは error
│  │  │  │  ├─controllers
│  │  │  │  │      IndexController.php
│  │  │  │  │      
│  │  │  │  ├─forms
│  │  │  │  └─views
│  │  │  │      └─index
│  │  │  │              app.volt
│  │  │  │              notfound.volt
│  │  │  │              system.volt
│  │  │  │              
│  │  │  └─index                          # index処理 submodule 
│  │  │      │                             # コントローラーのnam
│  │  │      │                             # espaceは index
│  │  │      ├─controllers
│  │  │      │      IndexController.php
│  │  │      │      
│  │  │      ├─forms
│  │  │      │      IndexForms.php
│  │  │      │      
│  │  │      ├─language
│  │  │      │  └─exprss
│  │  │      │          ja.php
│  │  │      │          
│  │  │      └─views                      # index module 専用のテ
│  │  │          │                         # ンプレートをここに作
│  │  │          │                         # 成する。
│  │  │          └─index
│  │  │                  index.volt
│  │  │                  
│  │  └─views
│  │      │  base.volt                      # 共通template
│  │      │  
│  │      └─layouts
│  │              main.volt                  # 共通layout
│  │              
│  ├─library
│  │  └─mvc
│  │          Module.php                     # module用親クラス。sab
│  │                                         # moduleのロード等
│  ├─logs
│  └─plugins
│          
├─cache
└─public
        .htaccess
        index.php
            

}}}
[config/service.php] phalconを実装する多くの場合でdependency injectionの各serviceをここで 定義していると思う。 ここでsubmodule構成の為にrouterサービスを定義。
/**
 * Specify routes for modules
 */
$di->set('router', function () {

    $router = new \Phalcon\Mvc\Router();

    // デフォルトモジュールを frontend に設定
    $router->setDefaultModule("frontend");

    /* frontend/modulesの直下にあるディレクトリ名をnamespaceとして定義
       ここで/foo/var/indexとアクセスがあった場合、以下が設定される
       module     = frontend
       namespace  = foo/Controllers
       controller = var
       action     = index
     */
    $modulesDir = APP_ROOT_PATH . DIRECTORY_SEPARATOR . 'frontend/modules';
    $dir = @opendir($modulesDir);
    if($dir){
        while($dir_name = readdir($dir)){
            // exclude [.],[..],[*.php]
            if($dir_name == '.' || $dir_name == '..'
               || strpos($dir_name, ".php") > -1){
                continue;
            }
            $router->add("/" . $dir_name . "/:controller/:action", array(
                    'module'     => 'frontend',
                    'namespace' => $dir_name . '\Controllers',
                    'controller' => 1,
                    'action' => 2,
            ));
        }
    }

    return $router;
});
もしModuleがfrontend以外にもあるのならそのディレクトリも同じように登録すれば 実現可能。


[library/mvc/Module.php] ここでは特に2つの事を行う。

1つはmodules直下を読み込んでいってnamespaceとしてロードする
    /**
     * Register a specific autoloader for the module
     */
    public function registerAutoloaders()
    {

        $loader = new \Phalcon\Loader();

        $namespaces = array();

        $modulesDir = APP_ROOT_PATH . DIRECTORY_SEPARATOR . 
                        $this->module_name .  '/modules';
        $dir = @opendir($modulesDir);
        if($dir){
            while($dir_name = readdir($dir)){
                // exclude [.],[..],[*.php]
                if($dir_name == '.' || $dir_name == '..'
                  || strpos($dir_name, ".php") > -1){
                    continue;
                }
                $namespaces[$dir_name . '\Controllers'] = $modulesDir . 
                   DIRECTORY_SEPARATOR . $dir_name . DIRECTORY_SEPARATOR .
                   'controllers';
                $namespaces[$dir_name . '\Forms'] = $modulesDir . 
                   DIRECTORY_SEPARATOR . $dir_name . DIRECTORY_SEPARATOR .
                   'forms';
            }
        }
        
        $loader->registerNamespaces($namespaces);
        
        $loader->register();
    }
公式サイトにもModuleのautoloadersの実装方法があるが、少し変化させて今回の目的に対応できるようにした。
modulesの直下にあるnamespace用ディレクトリの直下にcontrollersとformsというディレクトリを用意して、 それぞれnamespaceとして定義してローダーに登録している。
modules/indexを例にすると以下のようになる
  └─index
      ├─controllers                # namespaceはindex\Controllers
      │      IndexController.php    
      │      
      ├─forms                      # namespaceはindex\Forms
      │      IndexForms.php
      │      
他にこのnamespaceの単位に組み込みたいものがあれば(modelを追加したい場合はあるかもしれない) ここで定義すれば追加可能。

2つ目はviewの定義。各ファイルはnamespaceを使う方法に依って相互に認識しあえるようにできたが、 viewがちょっと異なる。controllerなどのphpファイルはnamespaceが利用できたが、viewのテンプレートは PHPのクラスで無い為にnamespaceに頼る事が出来ない。
(他の多くのフレームワークでもそうだと思うがテンプレートは通常のファイルとして扱っている)

Moduleでserviceを登録できる方法が提供されているので、viewをここで定義する。
レイアウトとメインのテンプレートは共通のディレクトリを使い、各アクションに依存した画面テンプレートは modules以下を使うようにしたい。

    public function registerServices($di)
    {
        $module_name = $this->module_name;

        //Registering the view component
        $di->set('view', function() use ($di, $module_name){
            $config = $di->get("conf");
            $view = new \Phalcon\Mvc\View();
            $view->setViewsDir(APP_ROOT_PATH . DIRECTORY_SEPARATOR . 
               $module_name . $config->application->viewsDir);

            // レイアウトディレクトリとメインは共通のディレクトリを示
            // すように定義
            $view->setLayoutsDir('../../../views/layouts/');
            $view->setMainView('../../../views/base');
            
            $view->registerEngines(array(
                    ".volt" => 'volt'
            ));

            // change view directory by namespace
            $eventsManager = new \Phalcon\Events\Manager();
            $eventsManager->attach('view', function($event, $view)
               use($module_name) {
                $dispatcher = $view->getDI()->getShared("dispatcher");
                $nName = $dispatcher->getNamespaceName()
                if ('beforeRender' == $event->getType()) {
                    // レンダリング前にviewのディレクトリを
                    // modules/namespace/views になるように設定。
                    $view->setViewsDir(APP_ROOT_PATH . DIRECTORY_SEPARATOR . 
                      $module_name . DIRECTORY_SEPARATOR . 'modules' . 
                      DIRECTORY_SEPARATOR . substr($nName, 0, 
                      strpos($nName, '\\', 0)) . DIRECTORY_SEPARATOR . 
                      'views' . DIRECTORY_SEPARATOR
                    );
                }
            });
            $view->setEventsManager($eventsManager);
            
            return $view;
            
        });

        $di->set('volt', function($view, $di) use ($module_name) {
        
                    $volt = new \Phalcon\Mvc\View\Engine\Volt($view, $di);
                
                    $volt->setOptions(array(
                            "compiledPath" => APP_ROOT_PATH
                               . "/../cache/volt/" . $module_name
                               . DIRECTORY_SEPARATOR
                    ));
            return $volt;
        }, true);
            
    }



[frontend/modules/index/IndexController.php]
/index/index/indexでアクセスされた場合にrouterによって紐付けされるように実装する。 注意する点はnamespace。routerとModuleで定義した内容とマッチするように定義する
namespace index\Controllers;

class IndexController extends \Phalcon\Mvc\Controller
{

    public function indexAction()
    {
    }
}


どうしてこうなったのか?

色々考えた結果、phalconのフローの関係で今回挙げた処理は前述の箇所となっり、このスタイルに 落ち着いた。デフォルトのModuleの単位をこのレベルに引き落とす方法も見当したが、エラーハンド リングがどうしても共通で行う方法にしっくりくる物が無かった。
routerの定義、namespaceの登録とロードはdispatchがされる前で、viewサービスはdispatch後に実行 されるようなのであのような形になった。


もっと良い実装方法 or 実はphalconの機能で実現する方法が存在するかもしれない。
今のところはこんな方法だろうか。

2014年8月22日金曜日

PHP5.3系のEOL

PHP5.3系が2014/08/15にリリースされた5.3.29をもってEOL(End of Life)と宣言されました。
今後の5.3系でリリースは予定されていないそうです。
またすみやかな 5.4 o r5.5 への移行が推奨されています。

php.netを読み返してみると2009/06/30に5.3.0がリリースされてからおよそ5年が経過していました。 なんかしみじみするなぁ。

詳しくはphp.netのアナウンスにて。

2014年7月25日金曜日

IPhoneアプリ開発で参考にした本

Pocket Trumpetterに参考にした本。
そもそもPHPを中心としたWEBシステムの開発者なので IPhoneアプリの作成は門外漢。作ろうにも何の知識も経験 も無いので効率良く作る為に2冊の本を参考にした。

[iPhoneアプリをプロデュースiOS SDK 5]

iPhoneアプリをプロデュース iOS SDK5 (SCC Books)

アプリ開発の為の全体の流れをこの本で把握した。WEBシステムの開発であれば 多くのケースでMVCモデルを採用しているので実際に処理をするプログラムや、 画面表示用のプログラム等をどう用意すれば良いのかイメージが湧くのだけれども、 アプリ開発ではイメージがわかないので、イメージが湧くような入門書を読みたいと 思っていた。これは正にBING。
サンプルプログラムが書いてあるので、環境を整えてそのまま実装すれば大体動く物 ができあがる。
他言語の経験があればこれで大体流れがわかると思う。ただし、経験が無い場合は何を やっているかわからない内にアプリが出来上がってしまうと思う。


[Objective-Cの絵本 (絵本シリーズ) ]

Objective-Cの絵本

1冊目はアプリ開発の流れを知りたくて読んだが、Objective-Cについては全く知らないので 補助的に読んでおいた、基本はCなのでポインタなどの概念があったりする。PHPのポインタ とはだいぶ違うと思い読んでおいた。また、1冊目では理解できない(受け入れ難い)関数の 宣言方法など色々と「くせ」の違いもわかってモヤモヤが減ったので読んでよかったと思う

本当はもっと深い点に触れた本を読む必要があると思っているがPocket Trumpetterは そんなに複雑な事は行わないので、一先ず絵の多い薄めの入門書2冊に留めている。 また入門書とは言えど、ある程度の経験が無いと意味不明なままになるんじゃないかとはちょっと 思う。(これより簡単なものがあるかはわからない。 今後、本気でアプリ開発をする場合はポインターやメモリ管理について勉強して おくべきかと思う。

2014年7月13日日曜日

Pocket Trumpetter

I developed Pocket Trumpetter.
This Page is Announcement and suport for Pocket Trumpetter.

Pocket Trumpetterというアプリを開発しました。
この記事はPocket Trumpetterというアプリの告知兼サポートページになります。

 
 
This App is Trumpet Simulator. If you touch any embouchure button and three piston button, move trumpetter's finger and make sound. You are permitted playing trumpet at the limited place, but you can play this anytime and anywhere use earphone.
Let's Try!
これはトランペットシミュレーターアプリです。ボタンを押すとトランペッターの指が動いて押したボタンに対応した音がでます。トランペットは演奏する場所が限られますが、このアプリならばいつでもどこでも遊べます。
是非お試しください!
 


Screenshot




 

Movie