スポンサーサイト

0

    一定期間更新がないため広告を表示しています


    • 2014.12.23 Tuesday
    • -
    • -
    • -
    • -
    • -
    • by スポンサードリンク

    icu4.4以上が用意できないサーバーでSymfony2.3以上を使う方法

    0
      Symfony2.3 standard editionから、新しいcomponentとしてSymfony¥Component¥Icuが必須になりました。ICUはInternational Components for Unicodeの略で、主に国際化関係のライブラリのようです。
      https://github.com/symfony/Icu

      ところが、このIcuコンポーネント、2013年夏現在で一つ問題があります。
      Symfony¥Component¥IcuはicuというC言語のパッケージ
      http://site.icu-project.org/home
      に依存しており、しかもバージョン4.4以上を要求しています。
      しかし、CentOS6.4の標準的なyumレポジトリでインストールできるicuはバージョンが4.2です。php開発者にとって最新版に素早く対応してくれることで有名なremiレポジトリにもありません。(※2013年夏現在)
      さらに、php-intlはicuに依存しており、yumで入れられるphp-intlは必然的に低いバージョンのicuにリンクされてしまいます。
      これでは、専用サーバー・VPSで、しかもphp-intlだけでもyumを使わずにphpizeしてビルドしないとせっかくのLTS版Symfonyが使えないことになってしまいます。こうなると共有サーバー(もともと、さくらやxrea,coreserverなどのsshがある程度自由に使えるサーバーじゃないと難しいですが)での利用は絶望的に思われます。

      先日、私がSymfony2.3で開発済みのとあるシステムを、用意されたサーバー(icuなどの環境が変更不可な環境)にデプロイしようとした際に、このicuのバージョン問題にひっかかって文字通り立ち往生でした。再枠の場合、Symfonyで作ったシステムをまるっと捨てて、別の依存ライブラリの少ないフレームワークで再構築まで半ば覚悟しました。
      が、その時、@hidenorigotoさんからありがたいアドバイスが!



      もう再構築のために他のフレームワークを選定する直前まで行っていたのですが、急遽composer.jsonでsymfony/symfonyの前にsymfony/icuをv1.0.0にバージョン指定して記述したうえで、再度composer.phar installを試みることにしました。
      (略)
      "symfony/icu" : "1.0.*",
      "symfony/symfony": "2.3.*",
      (略)
      
      結果、無事にicu4.2の環境でSymfony2.3のシステムを動かすことに成功しました!


      使うライブラリのcomposer.jsonはよく読みましょう、という教訓でしたorz


      WindowsのPCで開発するphperがxhprofを使う方法

      0

        xhprofはfacebookが開発した超・強力なプロファイリングツールです。コールグラフというどこがボトルネックになっているかを綺麗に画像にして見せてくれる機能が有名です。
        しかし、残念なことに、phpのライブラリではなくphpの拡張なので、Windowsでは使うことができません(自分でビルドすればいいんですが、それが難しい…)
        「そろそろmacにしたら…?」というお馴染みの声が聞こえてくる中、家庭の事情でWindowsのPCしか使えない私が色々試行錯誤して、xhprof使える方法を見付けたのでご紹介します。

        結論:Vagrant使え!

        え?ここで終わっちゃ、だめ?w

        Vagrantって最近よく聞くけど何?という方も多いでしょう。
        Vagrantは仮想OSを開発者にとって嬉しい状態にセットしてくれるありがたいソフトです。ぜんぶ無料で使えます(お金払うと快適な速度のほうが使えるらしいですが)。ま、私も2か月前に初めて知ったんですけどね!

        さっそくやり方を書きます。

        1. VirtualBoxをインストールします。(無料)
        2. Vagrantをインストールします。
        3. コマンドプロンプトを開き、適当なフォルダでvagrant initを実行します。
        4. フォルダを見るとVagrantfileというファイルができています。お好きなエディタで開いて下さい。編集する箇所は4か所です。
          config.vm.box = "base"
          を
          config.vm.box = "precise64"
          に変更
          
          # config.vm.box_url = "http://domain.com/path/to/above.box"
          を
          config.vm.box_url = "http://files.vagrantup.com/precise64.box"
          に変更
          
          # config.vm.network :private_network, ip: "192.168.33.10"
          を
          config.vm.network :private_network, ip: "192.168.33.10"
          に変更
          
          # config.vm.synced_folder "../data", "/vagrant_data"
          を
          config.vm.synced_folder "./data", "/var/www/vagrant_data"
          に変更
          
        5. ここでVagrantfileがあるフォルダ内にdataというフォルダを作っておきます。
        6. コマンドプロンプトでvagrant upを実行して、仮想マシンを作らせます。だーっと実行結果が流れるので待ちましょう。特に最初は時間がかかります。
        7. 実行が終わったらコマンドプロンプトでvagrant sshを実行しましょう。お馴染みの(?)SSHで仮想マシンに接続できます。precise64にしたのでOSはUbuntuの64bitです。 sudoが効くので、いつもサーバー構築する時のように、phpやapacheをインストールできます。
          特にバージョン等にこだわりが無ければ
          sudo apt-get update
          sudo apt-get install php5
          
          でOKです。今日(2013/07/25)の時点では、PHP5.3.10が入りました。
        8. そしていよいよxhprofをインストールします。
          sudo apt-get install php-pear make
          sudo pecl install xhprof-beta
          
          extension=xhprof.soをphp.iniに追記したらインストール完了です!Vagrant万歳!

        後は、Vagrantfileのあるフォルダの下に作ったdataフォルダの中にxhprofを試したいPHPアプリ(wordpressとかOpenPNEとか自作の何かとか)のソースを入れて、Ubuntu側に何かhttpサーバーを入れて、/var/www/vagrant_data(アプリによっては/var/www/vagrant_data/webとかになりますが)をドキュメントルートに設定するだけで、Windowsでも好きなようにxhprofでのプロファイリング結果を出して見ることができます。
        xhprofの設定方法やプロファイル結果の見方については検索するとたくさん記事が出て来るのでググってみてください。

        これだけなら単にVirtualBoxかVmwarePlayerを使えば従来でもできたことですが、Vagrantのすごいところは、dataフォルダに入れたPHPのソースをプロファイル結果に従ってその場で、しかもWindowsのPC側の使い慣れたエディタやIDEで編集できるところ(synced_folderのすごいところ、というべきか)。

        色々と開発中の不便に悩むたびに「YOU macにしちゃいなYO!」と言われることが多いWindowsですが、Vagrantという強力な武器をゲットした今では、もうmacの誘惑も怖くないですね!



        #phpmatsuri 2013でOpenPNE3のcookbookを書きました

        0

          7/14〜7/15札幌で開催されたphpmatsuriに行ってきました!

          わざわざ札幌まで行った理由としては、今すごいコミュニティ欲が高いシーズンが来ていて、更に、札幌は私の生まれ故郷で実家があるので子供達を連れて行っても預け先がある(子供を自宅に置いて行かなくて済むのでDAuth=ダンナauthが取りやすい)というだけです。ついでに、春先に一度倒れた祖父の見舞い(超元気でした)も行きたかったし、単に暑すぎる名古屋から涼しい札幌に少しでも涼みに行きたかったというのもありました。不純な動機ばかりですがw ともかく子供二人と自分、三人分の飛行機代をかけて札幌まで行きました。

          作ったもの:OpenPNE3のcookbookと、そのcookbookを使ったVagrant環境

          OpenPNE3のcookbookと、そのcookbookを使ったVagrant環境を作りました。

          なぜ作ろうと思ったかと言うと、素人さんは素人さんでもmac使いの人はいざとなれば自力でビルドしてもらえばいいのでまぁいいとして、windows使いの人にOpenPNE3が動く環境を作ってもらうのが難しいと前から考えていて、唯一おすすめできると思っていたのがxamppぐらい。でも、xamppの最新版はPHP5.4で、OpenPNE3が今のところPHP5.4で動かない(PNEが悪いわけではなくsymfony1.4とdoctrine1.2の問題で)という状態になっていました。
          私自身、開発用xamppを5.4にしてからPNE3の案件が戻って来たときに非常に困っていたという事情もあります。私もPHPカンファレンス関西で安藤さんが紹介してくださったvagrantを使って開発環境を用意して回避したので、同様に一般向けに作ったらいいんじゃないかと思ったわけです。

          開発は苦労しました。
          まず、私のruby力不足。随分前にこのブログで「rubyを始めてみた」的なエントリーを書いた記憶があるのですが、必要に迫られなかったこともあり、実際はhello world以上のことをやったことがなかったのです。Vagrantfileはまだいいとしても、cookbookのほうを書くためには、制御構造(if,each…)の書き方やら連想配列の書き方からグーグル先生に訊きながらの作業になりました。しかも一日目夜のcandycaneのワークショップで「if文のブロックで囲むとインデントが深くなるのでnot_ifやonly_ifを使え」とか言われて慌てて直したり…。
          さらに、会場の回線環境があまり良くなくてほぼスマフォのテザリングで頑張ったのですが、大量の依存パッケージ(apache,mysql,php…)をインストールさせることになるので、タイムアウトエラーが頻発してしまい、実際のPNEインストールに辿り着かせるまでが大変という状態でした。

          最終的には、OSにprecise64を使い、かつsynced_folderなしであればインストール完了して画面が表示できるところまではなんとか作ることができました。
          phpmatsuriは終わりましたが、phpmatsuriだけのために作ったcookbookやvagrant環境ではないので、今後も開発を続けたいと考えています。
          具体的には、
          * Vagrantを使う場合にsynced_folder内にソースを入れられるようにする
          * 設定項目でもっと柔軟性を持たせたい(インストールするプラグインとか)
          * precise64以外のOSにも対応したい(特にRedhat,CentOSはマストかと)
          * ruby力をつける&モジュール分割するなどしてもう少しcookbook自体のコードを綺麗にしたい
          という野望を持っています。目標は大きく!

          LTについて

          初日に講演されたどなたか(失念…ごめんなさい><)を見習ってスライドは英語、喋りは日本語にしました。一人同時通訳にしたら絶対に時間が足りないと思ったので。結局、日本語だけ喋っても時間は足りませんでした…。

          少しだけウケを狙った「〜〜済のものがこちらになります」が滑って悲しかったです。タイトルのひねりが足りなかったのではないかと反省。せっかくLT時間が3分間だったり、ネタがchefだったりしたので、『OpenPNE3 3分間クッキング』とかつけるべきでしたね。←反省するところ間違ってる?

          結果20位台(魂抜けてて正確な順位は聞いてなかった)という評価をいただきました。次はもっと上を狙えるよう頑張りたいです!

          二日間通しての感想

          OpenPNE関係で普段からお世話になっている手嶋屋の手嶋さん(鳩サブレありがとうございましたm(_ _)m)と「リアルでは初めまして」。川原さん・NEKOGETさん・cakephperさんとも以下同文。PHPメンターズの道場生仲間の佐藤さんとも初めまして。
          更に、LTで「名古屋から来た」と言ったお陰で、岐阜から来た外人さんお二人からも声を掛けていただきました。名古屋在住と岐阜在住が札幌のphpmatsuriまで来て、子供の話とか保育園の話で盛り上がるという謎の展開^^;お二人が日本語で喋ってくれてるのに私が英語交じりで返すというアホっぷりを晒してしまい、申し訳なかったです。
          偶然、福岡のfusicから来た前回・前々回の優勝者の方とずっと同じテーブルでハックするという幸運に恵まれました。が、お隣の同僚さんも含めて、すごすぎて何も盗んで来れなかった…orz

          KEEP

          • 来年も参加する!
          • ネタだけじゃなく実用的なものを作る

          PROBLEM

          • 準備はしっかりと(実はCentOSのOSイメージ持っていくのを忘れてたのです)

          TRY

          • もっと色々な人と話す(今回ほとんど同じテーブル+前からネット上で知っている人としか話してないので)

          来年もぜったい行くぞー!というか名古屋でやろうよ!←



          Symfony2のControllerで@Templateを使うメリット

          0
            仕事が忙しくて予定してた実験ができなかったので小ネタ。

            symfony1.xに馴れた身でSymfony2を触ると、色々面倒なことってありますよね。
            例えば、sfDoctrineRoute。例えば、テンプレート名とアクション名が同じならわざわざ指定しなくていい。

            明示的で後から参加する人にもわかりやすくなったのは良いんですが、裏を返せばエキスパートにとってはわかりにくくなったと思うんです。(言いすぎ)

            symfony1.xの便利機能が恋しい〜。
            ところで、それ、Sensio¥Bundle¥FramewordExtraBundleにほとんどあります。

            Sensio¥Bundle¥FrameworkExtraBundle¥Configuration¥ParamConverter

            sfDoctrineRouteみたいなもんです。
            コントローラのアクションのメソッドにアノテーションにして使います。
            使い方はこの辺り
            一々$this->getDoctrine()->getManager()とかめんどくさくてやってられないでしょ。

            Sensio¥Bundle¥FrameworkExtraBundle¥Configuration¥Template

            通常$this->render()なんかで指定するテンプレート名が指定不要になって、更に、アクションのメソッドが配列を返すだけで良くなる設定。
            これをつけておくだけでAcmeDemoBundle:Foo:barコントローラのテンプレートはAcme/DemoBundle/Resources/views/Foo/bar.html.twigになります。
            @Template("AcmeDemoBundle:Foo:baz.html.twig")と書けばAcme/DemoBundle/Resources/views/Foo/baz.html.twigにも変更可。

            で。ここから、特に@Templateを使うと何が良いのかという話。

            @Templateをつけない場合、コントローラのメソッドの返り値はSymfony¥Component¥HttpFoundation¥Responseになります。
            普通のコントローラ作ってるだけなら別にいいのですが、汎用的なコントローラを作って、それを拡張した新しいコントローラを作ろうとした場合に拡張元が普通のアクションだとメソッドの返り値がResponseになってしまい、非常にカスタマイズしにくいです。

            @Templateなしの場合
            // src/Acme/DemoBundle/Controller/BaseController.php
                public function barAction(Request $request)
                {
                    //何かの処理
            
                    return $this->render('AcmeDemoBundle:Foo:bar.html.twig', 
                        array('name' => $name)
                    );
                }
            // src/Acme/DemoBundle/Controller/FooController.php
                public function barAction(Request $request)
                {
                    //parent::barAction($request)だとResponseが返って来ちゃうから…
                    //何かの処理(BaseController::barAction()と同じ内容コピペ)
                    //追加の処理(カスタマイズ)
            
                    return $this->render('AcmeDemoBundle:Foo:bar.html.twig', 
                        array('name' => $name, 'tel' => $tel)
                    );
                }
            

            @Templateありの場合
            // src/Acme/DemoBundle/Controller/BaseController.php
                /**
                 * @Template()
                 */
                public function barAction(Request $request)
                {
                    //何かの処理
            
                    return array('name' => $name);
                }
            // src/Acme/DemoBundle/Controller/FooController.php
                /**
                 * @Template()
                 */
                public function barAction(Request $request)
                {
                    //返ってくるのは配列だからparent::barAction()使ってOK
                    $vars = parent::barAction($request);
                    //追加の処理(カスタマイズ)
                    $vars['tel'] = $tel;
            
                    return $vars;
                }
            

            テンプレート指定の面倒がなくなるだけじゃないよ!という話でした。


            SonataAdminBundleでフォームや一覧表示を実用的にカスタマイズする方法(初歩編)

            0
              SonataAdminBundleとは、"The missing Symfony2 Admin Generator"というキャッチフレーズの通り、symfony1時代のAdmin Generatorのような機能が使えるバンドルです。
              つまり、いくつか決められた設定をするだけで、管理画面が自動で作られる使えるようになるバンドルというわけです。

              インストール方法・基本的な使い方などはバンドルのドキュメント(本家の英語版@最新,@okaponさんによる日本語翻訳@少し古い?)を見てください。

              さて、このSonataAdminBundle、便利な汎用バンドルにはありがちなことですが、少し変わったことをしようと思うと、工夫が必要になります。
              例えば、顧客の性別データを1=男性,2=女性,3=その他で管理しているとします。
              //src/Acme/DemoBundle/Entity/Customer.php
              class Customer
              {
                  /**
                   * @var integer
                   *
                   * @ORM¥Column(name="id", type="integer")
                   * @ORM¥Id
                   * @ORM¥GeneratedValue(strategy="AUTO")
                   */
                  private $id;
              
                  /**
                   * @var string
                   *
                   * @ORM¥Column(name="name", type="string", length=255)
                   */
                  private $name;
              
                  /**
                   * @var integer
                   *
                   * @ORM¥Column(name="seibetsu", type="integer")
                   */
                  private $seibetsu;
              }
              

              (この実装が良いかどうかは別として)これを前提として、SonataAdminBundleで通常通りに管理画面設定をすると、下記のようになります。
              // src/Acme/DemoBundle/Admin/CustomerAdmin.php
              
              use Sonata¥AdminBundle¥Admin¥Admin;
              use Sonata¥AdminBundle¥Datagrid¥DatagridMapper;
              use Sonata¥AdminBundle¥Datagrid¥ListMapper;
              use Sonata¥AdminBundle¥Form¥FormMapper;
              
              class CustomerAdmin extends Admin
              {
                  protected $baseRouteName = 'admin_customer';
              
                  /**
                   * @param FormMapper $formMapper
                   */
                  protected function configureFormFields(FormMapper $formMapper)
                  {
                      $formMapper
                          ->add('name')
                          ->add('seibetsu')
                      ;
                  }
              
                  /**
                   * @param DatagridMapper $datagridMapper
                   */
                  protected function configureDatagridFilters(DatagridMapper $datagridMapper)
                  {
                      $datagridMapper
                          ->add('name')
                          ->add('seibetsu')
                      ;
                  }
              
                  /**
                   * @param ListMapper $listMapper
                   */
                  protected function configureListFields(ListMapper $listMapper)
                  {
                      $listMapper
                          ->addIdentifier('name')
                          ->add('seibetsu')
                      ;
                  }
              }
              

              フォーム表示
              一覧表示

              フォーム(登録・編集、絞込み)でselect,radioを使えるようにする

              まず、フォームで性別コード(1,2,3)を設定するのをやめたいと思います。
              いくら管理画面とはいえ不親切すぎますし、間違いやすいです。
              selectかradioで、男・女・その他のキャプションを見ながら選べるほうがいいですよね。

              最初に、性別専用のフィールドタイプを作ります。サービスとして登録するところまでやっておきましょう。
              作り方はこちら→カスタムフォームフィールドタイプの作成方法(日本語版)
              ※昨日翻訳を更新しておいたので最新版になってます!

              上記ドキュメントではm=Male,f=Femaleになっていますが、1=男,2=女,3=その他で作ります^^;
              フィールドタイプを作り終わったら、CustomerAdmin::configureFormFields()とCustomerAdmin::configureDatagridFilters()でseibetsuをaddするときに、第二引数として専用フィールドタイプのサービス名、つまりgenderを指定します。
                  /**
                   * @param FormMapper $formMapper
                   */
                  protected function configureFormFields(FormMapper $formMapper)
                  {
                      $formMapper
                          ->add('name')
                          ->add('seibetsu', 'gender')
                      ;
                  }
              
                  /**
                   * @param DatagridMapper $datagridMapper
                   */
                  protected function configureDatagridFilters(DatagridMapper $datagridMapper)
                  {
                      $datagridMapper
                          ->add('name')
                          ->add('seibetsu', 'gender')
                      ;
                  }
              

              これで、フォーム上の性別のウィジェットが性別選択専用のカスタムフィールドに変わりました。簡単でしたね:)
              変更後のフォーム表示

              一覧上で生の値でなくキャプションを表示させる

              フォームが直ったのでもう充分な気もしますが、一覧で性別コード(1,2,3)が表示されるのも直したいと思います。
              いくら管理画面とはいえ不親切(以下同文

              今度は、カスタムTwigExtensionを用意します。
              作り方はこちら→カスタムTwig拡張の書き方(日本語版)
              ※昨日翻訳したてのほやほやです。

              作るTwig拡張の内容は、【性別コード(1,2,3)を性別のキャプション(男,女,その他)に変換する】という機能をもつ関数にします。
              // src/Acme/DemoBundle/Twig/Extension/GenderNameExtension.php
              class GenderNameExtension extends ¥Twig_Extension
              {
                  public function getName()
                  {
                      return 'acme_demo_gender_name';
                  }
              
                  public function getFunctions()
                  {
                      return array(
                          'gender_name' => new ¥Twig_Function_Method($this, 'getGenderName'),
                      );
                  }
                  
                  /**
                   * @param string $value
                   * @return string
                   */
                  public function getGenderName($value)
                  {
                      $list = array(
                          1 => '男',
                          2 => '女',
                          3 => 'その他',
                      );
                      
                      return isset($list[$value]) ? $list[$value] : '-';
                  }
              }
              
              専用TwigExtensionはサービスとして登録しておきます。
              これで、twigテンプレート側で{{ gender_name(1) }}のような形で呼び出すことで、性別コード→性別キャプションの変換ができるようになりました。

              専用のTwig関数ができたので、ここで、一覧表示の項目用のテンプレートを作ります。
              {# src/Acme/DemoBundle/Resources/views/sonata_customer_list_gender.html.twig #}
              {% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
              {% block field %}
                  {% spaceless %}
                      {% if value is empty %}
                           
                      {% else %}
                          {{ gender_name(value) }}
                      {% endif %}
                  {% endspaceless %}
              {% endblock %}
              

              このテンプレートをCustomerAdmin::configureListFields()でseibetsuのテンプレートとして指定したら、完了です。
                  /**
                   * @param ListMapper $listMapper
                   */
                  protected function configureListFields(ListMapper $listMapper)
                  {
                      $listMapper
                          ->addIdentifier('name')
                          ->add('seibetsu', 'string', array(
                                  'template' => 'AcmeDemoBundle::sonata_customer_list_gender.html.twig',
                              ))
                      ;
                  }
              

              性別コードでなく性別キャプションのほうが表示されるようになりました。
              変更後の一覧表示

              実は私もまだ触り始めたばかりですが、SonataAdminBundleはちょっとした設定だけで色々カスタマイズができることがわかりました。現時点では、設定項目を網羅したリファレンス的なドキュメントが不足しているために、カスタマイズができるところがわかりにくいのが欠点かなと思います。
              今後に期待したいところです!


              Symfony2.3でsessionのflashデータの操作方法が変わった件

              0
                先週後半から、最初のLTS版Symfony2.3を早速使っているわけですが、2.2以前に比べて結構たくさん仕様変更があり、慣れたつもりの私でも「あれ?」という事態が発生しています。
                日本語ドキュメントが全部追いつくまでかなり時間がかかると思われる(スミマセンスミマセン)ので、私がハマった部分だけ個別に書いておくことにしました。

                早速ですが、セッションのflashメッセージの件。
                セッションのflashメッセージは、一回限り(セットしたリクエストの次のリクエストまでの間だけ)保存されるセッションデータです。
                主な用途は、postされたデータをサーバー側で処理した際の完了メッセージ等(たぶん)

                (追記:ソースコードを追った結果この変更は2.3でなく2.1からの仕様変更で、2.2までは後方互換で使えていただけということがわかりました。)

                2.2までの書き方

                コントローラでflashメッセージをセットする:
                $this->get('session')->setFlash('notice', '更新しました。');

                twigテンプレートでflashメッセージが存在するかどうか確かめる:
                {% if app.session.hasFlash('data') %}
                    {# dataというflashがある場合の処理 #}
                {% else %}
                     {# dataというflashがない場合の処理 #}
                {% endif %}

                twigテンプレートでflashメッセージを表示:
                {# hogeというflashを表示 #}
                {{ app.session.flash('hoge') }}

                2.3からの書き方

                コントローラでflashメッセージをセットする:
                $this->get('session')->getFlashBag()->set('notice', '更新しました。');

                twigテンプレートでflashメッセージが存在するかどうか確かめる:
                {% if app.session.flashBag.has('data') %}
                     {# dataというflashがある場合の処理 #}
                {% else %}
                     {# dataというflashがない場合の処理 #}
                {% endif %}

                twigテンプレートでflashメッセージを表示:
                {# hogeというflashを表示 #}
                {% for flashMessage in app.session.flashBag.get('hoge') %}
                     {{ flashMessage }}
                {% endfor %}
                $this->get('session')->getFlashBag()->set('notice', '更新しました。');のように文字列を一つだけセットしてあっても、app.session.flashBag.get('notice')の中身は必ず配列になります。
                一度$this->get('session')->getFlashBag()->set('notice', '更新しました。');としてから$this->get('session')->getFlashBag()->add('notice', 'もう一つ更新しました。');とすると、2つのメッセージを保存できます。
                ただし、$this->get('session')->getFlashBag()->set('notice', '更新しました。');としてから$this->get('session')->getFlashBag()->set('notice', '再度更新しました。');とすると、後でセットしたメッセージのみが保存されます。(addだと追加されるがsetだと上書き)

                #わかりづらくなって、正直言って改悪じゃないの?という気がしないでもないのですが(-_-;)


                PHPカンファレンス関西2013に参加してLTしてきました #phpkansai

                0

                  昨日、大阪で開催されたPHPカンファレンス関西2013に参加してきました。

                  基調講演

                  まず、久保さんの基調講演。

                  Pear::Net_UserAgent_Mobileの作者って久保さんだったのですね。多大にお世話になっておきながら知りませんでしたm(_ _)m

                  もつれた意図(糸とかけてる?とか不謹慎なこと考えた^p^)をほぐす、それぞれの意図を表すのに適した方法で。そして、これからのプログラムはもつれをなくす方向に進んでいる。示唆に富んだ内容で、出て来たキーワードだけでも一晩ではとても消化しきれてないので、じっくり考えて、これからのコードに生かしたいと思います。

                  セッション

                  セッション中はずっと4階後方のユーザー会ブースにいて、4階の発表を聞いていました。

                  楽天のエンジニア、Graham Weldonさんによるdebugging=自転車 profiling=バイクの例えはなるほどと思いましたし、スライドに時々登場する日本語がかわいくて和み、xhprofのコールグラフは「是非PNE3やSymfonyのプロジェクトで見てみたい!」と思ってしまいました。最適化のタイミングについても、かなり納得。

                  多言語パネルディスカッション。メモに一行「ステートレス」と^^;

                  後藤さんの発表、関心を分離する。私にとっては普段から触れている内容なのですが、HTMLとCSSという例えは、初めての人にも非常にわかりやすくてよかったと思います!分離のデメリットは道具(IDE)でカバーというのも素敵。

                  安藤さんのChefとVagrantは実は今、(主に仕事の都合上で)一番興味があるところでした。というのも、よりによって年度末にかけて連続してハードウェア障害に見舞われて、サーバー構築やり直しという非常事態を短期間に何度も経験するはめになったばかりで。まずは「挫折しねえ」とオススメされていた『入門chef solo』をkindleでポチってみることにします。

                  LT

                  「まだ技術ブログを始めていないPHPerのあなたへ。」という題でLTをしました。最初は間に合ったらSymfony Componentネタをやりたいと考えていたのですが、朝からのセッションの流れを見ると、特定ライブラリのネタに走らなくて逆に正解だったかもしれません。

                  スライドです。

                  運がいいのか悪いのか抽選の結果一番手になってしまい、緊張MAXのまま発表したので、声が震えたり早口になったりと、お聞き苦しい所があったと思います。聴いてくださった皆さまありがとうございました。手加減してくださったドラ娘のお二人もありがとうございました…。

                  他の方のLT中は一人で脳内反省会をしていたので、あまり真面目に聞いてなかったのですが(スミマセン)、サーバーサイドにSymfony2を使ってるという話がさらりと出てきた時と、Yamlのお話が本題に入ったと思しきところで時間切れになった時が印象に残っています。YamlのLTされた方のスライドが公開されたらじっくり読みに行かないと。

                  裏カンファレンス(?)

                  4階がセッション会場から懇親会会場にお色直しする間、6階の会場でディスカッションをするというので移動しました。通称裏カンファレンス?

                  最初は挙手アンケートだったものの、途中から生々しいお金の話が飛び交うようになって、さすが大阪ww だが、それがいいw

                  突発開催&ust中継がなかったからこそ聞けた話も多く、現地に行った醍醐味を実感することができたイベントだったと思います。

                  日本Symfonyユーザー会ブース

                  ちょっと時系列から外れて開場〜撤収までほぼ一日中居座っていたユーザー会ブースの話。

                  「Symfonyって何ですか?」という方から、「symfony1.4でfacebook SDKの認証のやり方がわからない!」と質問しに来て下さった方まで、色々な人に来ていただいて楽しかったです!
                  個人的には、「symfony1系は改修案件で何度か触ったことが」という声を結構聞いた気がするので、symfony1系のドキュメントや小規模な勉強会的なものは需要あるかも…などと。

                  思いがけないご縁もあり、楽しくお喋りして時間があっという間でした。(馴れ馴れしくてすみません。。)

                  しんふぉにゃんステッカーは是非作りましょう。あと、次はステッカー整理用にクリップと輪ゴムを用意して行きたいと思います^^;

                  懇親会

                  入り口近くのphpmentorsのテーブル(?)でまったりと語ってました。
                  他のテーブルに出張はしなかったのですがお話したいと思った方とは大体お話しできて満足。

                  日帰りなので、残念ながら懇親会LT1本目が終わって間もなく離脱しました。

                   

                  名古屋からみると、大阪は東京より断然近いので、また機会があったらぜひ遠征したいと思いました。実行委員の皆様、お疲れ様でした&ありがとうございました!



                  phper.jpでSilexを使うための設定

                  0
                    silex.pharでなくcomposerで管理するようになった版のSilexで動くwebアプリを国産PaaSのphper.jpで動かすことに成功したので、設定などをメモを兼ねて書いておきたいと思います。

                    phper.jpは面倒なサーバー構築不要でphp実行環境を使うことができる素敵なサービスですv

                    アプリサーバーの設定

                    ドキュメントルートはプロジェクトルート=ドキュメントルートの時は空文字列のままでOKのようです。

                    アプリサーバー設定直後は503になるようです。もしアプリサーバーを設定してしばらく経っても503のままの時は、何も変更せず再度設定の保存をすると表示できるようになるというレポがありました。
                    アプリサーバーを設定しても、設定保存し直してもずーっと503の時、エラーログにNo route to hostというエラーが出ていたら、何をやっても無駄なのでプロジェクトを作成し直したほうが良いです…。←私はこれで丸一日はまりました

                    vendorライブラリのインストール(composer.phar install)をアプリサーバー側で実行させる

                    通常、composerを使ったSilexのプロジェクトをどこかのサーバーにリリースする場合、サーバー側でcomposer.phar installを実行してvendorライブラリを入れますよね。しかし、phper.jpのアプリサーバーには、VPSや専用サーバーと違い自力でSSH接続をすることはできません。
                    かといって、vendorライブラリを全部gitの管理下に置いてpush→デプロイはちょっと面倒です。(まぁ、ほかに手段が無ければやっても良いのですが)

                    そこで、phper.jpのデプロイ時スクリプトを利用します。
                    http://phper.jp/manual#deploy

                    .phper/deployはデプロイごと(=pushごと)なのでcomposer.phar installの実行タイミングとしては多すぎる気もします。(「別に多すぎる気がしない」という方はそのまま.phper/deployに書いてよいと思います)
                    私はrequireを足したとき、主要なライブラリがアップデートされたときに手動で実行したいと思ったので、少々用途が違う気もしつつ.phper/initdbに書きました。(後述するようにrsync_exclude.txtがうまく効かなかったので、結局deployに書き直したのですが)

                    mod_rewriteを効かせる

                    UrlGeneratorServiceProviderを使って生成される内部URLは基本的にmod_rewriteが前提ですね。(index.php付きでアクセスした場合は別ですが)
                    つまり、UrlGeneratorServiceProviderを使う場合はmod_rewriteが必要になります。

                    apacheの設定は.phper/httpd.confに書けば効きますと書いてあるのですが
                    http://phper.jp/manual#deploy
                    .phper/httpd.confにmod_rewrite設定を書いてpush→デプロイしてもmod_rewriteは有効になりませんでした。(先にアプリサーバー追加した状態で後からhttpd.confを足したからかも?)
                    ダメもとでドキュメントルートに.htaccessを置いたところ正常に動作。
                    RewriteEngine On
                    RewriteBase /
                    RewriteCond %{REQUEST_FILENAME} !-f
                    RewriteRule ^ index.php [QSA,L]

                    composerでインストールしたvendorライブラリがデプロイ時に消えないようにする

                    push時のデプロイはrsyncが--deleteオプション付きで実行されるようなので、.phper/rsync_exclude.txtを置いてvendorライブラリ群を除外します。
                    http://phper.jp/manual#deploy
                    ※私の書き方が悪いのか公式のドキュメントが古いのか不明ですが、.phper/rsync_exclude.txtで除外したつもりのvendorライブラリがデプロイごとに削除されてしまうため、composer.phar installをデータベース初期化時スクリプトからデプロイ時スクリプトに書き直しました。

                    proxy設定する

                    proxyが動作するようなのでSilexの公式ドキュメントにある通り、proxyをtrustするようにSilex側の設定をします。
                    http://silex.sensiolabs.org/doc/usage.html#bootstrap
                    "If your application is hosted behind a reverse proxy and you want Silex to trust the X-Forwarded-For* headers, you will need to run your application like this:"
                    Request::trustProxyData(); $app->run();


                    OpenPNE3カスタマイズテクニック:no_image画像のカスタマイズ

                    0
                      コミュニティのno_image画像はユーザーのno_image画像とは違うのを表示したい、という場合のカスタマイズ方法について。

                      公式SNSの下記トピックでは、javascriptを使っててっとり早く変更する方法を紹介しちゃいましたが、この記事では真面目に確実に(javascriptが使えない環境も考慮。たとえばガラケーとか)変更する方法を考えてみたいと思います。
                      ■no_imageの画像と通知センターの画像変更について(閲覧には公式SNSのアカウントが必要)
                      http://sns.openpne.jp/communityTopic/8895

                      前提:op_image_tag_sf_imageヘルパーのno_imageオプション

                      op_image_tag_sf_imageヘルパーとは、PNE内の各所で、スキンプラグインやコアに含まれないアップロードされた画像のimgタグを出力するために使われているヘルパーです。
                      ソース(lib/helper/opUtilHelper.php)を見るとわかる通り、image_tag_sf_imageヘルパーのラッパーで、no_imageというオプションが無い場合にデフォルト値をセットしているだけです。
                      つまり、このno_imageオプションを個別に指定して呼び出してやることで、no_image画像を指定できることがわかります。
                      (本当にno_imageオプションでno_image画像が指定できるのかどうか?や、大元のimage_tag_sf_imageヘルパーのその他の各オプションについては、lib/plugins/sfImageHandlerPlugin/lib/helper/sfImageHelper.phpを見てください。)

                      コミュニティTOPのno_image画像を任意の画像に変える

                      コミュニティTOPのテンプレートはapps/pc_frontend/modules/community/homeSuccess.php
                      しかし、そこからop_include_parts()ヘルパでmemberImageBoxというパーツの呼び出しが行われているので、実際はapps/pc_frontend/templates/_partsMemberImageBox.phpになります。
                      <?php $imgParam = array('size' => '180x180', 'alt' => $options->object->getName()) ?>
                      <?php $nameMethod = $options->name_method ?>
                      <?php if ($options->object): ?>
                      <?php echo op_image_tag_sf_image($options->object->getImageFileName(), $imgParam) ?>
                      <?php else: ?>
                      <?php echo op_image_tag('no_image.gif', $imgParam) ?>
                      <?php endif; ?>
                      op_image_tag_sf_imageに渡されているオプションは$imgParamという配列をテンプレート内で独自に定義していることがわかります。
                      $imgParam初期化後、op_image_tag_sf_imageに渡すまでの間のどこかで
                      <?php $imgParam['no_image'] = op_image_path('コミュ専用のNOIMAGE画像'); ?>
                      のような行を追加すれば変更できますが、_partsMemberImageBox.phpはコミュTOPのみならずメンバープロフィール等でも使用されるパーツテンプレートなので、ここで変更してしまうとコミュだけでなくメンバーのほうも変更されてしまい、不都合です。
                      そこで、パーツ呼び出し時にno_imageの指定がある場合のみ、$imgParam['no_image']をセットすることを考えます。つまり、こんな感じです。
                      <?php $options->setDefault('no_image', false); ?>
                      <?php if ($options->no_image): ?>
                        <?php $imgParam['no_image'] = $options->no_image; ?>
                      <?php endif; ?>
                      あとは、community/templates/homeSuccess.phpに戻ってno_imageオプションを渡すだけです。
                      <?php
                      $options = array(
                        'object' => $community,
                        'no_image' => op_image_path('コミュ専用のNOIMAGE画像'),
                      );
                      op_include_parts('memberImageBox', 'communityImageBox', $options);
                      ?>

                      コミュニティ検索結果のno_image画像を任意の画像に変える

                      コミュニティ検索結果のテンプレートはapps/pc_frontend/modules/community/searchSuccess.php
                      しかし、コミュTOP同様にop_include_parts()ヘルパーでsearchResultListが呼び出されているので、実際に見るべきテンプレートはapps/pc_frontend/templates/_partsSearchResultList.phpです。
                      op_image_tag_sf_imageヘルパーの呼び出しは
                      <?php echo link_to(op_image_tag_sf_image($result->getImageFilename(), array('size' => '76x76')), sprintf($options['link_to_detail'], $result->getId())); ?>
                      のように行われています。ヘルパーコールが重複して見づらいですが、op_image_tag_sf_image()に渡されているオプションはarray('size' => '76x76')のみだとわかります。
                      コミュTOPの時と同様に、このarray()に'no_image' => op_image_path('コミュ専用のNOIMAGE画像')を足したいのですが、画像の表示オプションに各データ行ごとに違いはないですから、わざわざ個別のコミュデータごとにarray('size' => '76x76', 'no_image' => op_image_path('コミュ専用のNOIMAGE画像'))を作る意味はなく、全部のデータに共通のオプションを作ってしまって良いでしょう。
                      具体的には$options['pager']->getResults()のforeachループが始まるより前に、
                      <?php $options->setDefault('no_image', false); ?>
                      <?php $imgParam = array('size' => '76x76'); ?>
                      <?php if ($options->no_image): ?>
                        <?php $imgParam['no_image'] = $options->no_image; ?>
                      <?php endif; ?>

                      を追記して(コミュTOPのmemberImageBox同様、searchResultListも別の画面でも使うパーツのため、直接の書き換えは避けます)、foreachループ内のop_image_tag_sf_image()ヘルパー呼び出し部分は下記のように書き換えます。
                      <?php echo link_to(op_image_tag_sf_image($result->getImageFilename(), $imgParam), sprintf($options['link_to_detail'], $result->getId())); ?>
                      後は、community/templates/searchSuccess.phpに戻ってsearchResultListを呼び出す際のオプションにno_imageを追加するだけです。
                      $options = array(
                        'title'          => __('Search Results'),
                        'pager'          => $pager,
                        'link_to_page'   => '@community_search?page=%d',
                        'link_to_detail' => '@community_home?id=%d',
                        'list'           => $list,
                        'no_image' => op_image_path('コミュ専用のNOIMAGE画像'),
                      );

                      #このように、PNE3は拡張性が高くできていると思うのですが、あまり方法が知られていないのが残念ですね^^;


                      [バッドノウハウ]Symfony2で別テーブルの集計項目を一覧に含めたいとき

                      0
                        タイトルが日本語でおk状態ですが…。
                        [支店]にoneToManyで結びつく[売上]があるシステムで「支店一覧画面に、各支店の今月の売り上げ表示してよ」という類の要望が来たときとイメージしてくださいwww

                        symfony1+doctrine1なら簡単です。[支店]モデルに下記のようなメソッドを足して、
                        public function getMonthlyAmount($year, $month)
                        {
                          return Doctrine::getTable('売上')->getMonthlyAmount($this->getId(), $year, $month);
                        }
                        支店一覧テンプレートの各行で下記のように呼び出せばいい。
                        <?php foreach ($pager->getResults() as $branch): ?>
                            <tr>
                              <td><?php echo $branch->getName(); ?></td>
                              //略…ここに支店の所在地とか電話番号とか
                              <td><?php echo $branch->getMonthlyAmount(); ?></td>
                              //略…ここに編集ボタンとか詳細ボタンとか削除ボタン辺り。
                            </tr>
                        <?php endforeach; ?>


                        しかし、この手段はSymfony2+Doctrine2では取れません。
                        [支店]エンティティのメソッドからはエンティティマネージャや他のエンティティのレポジトリを呼び出すことができず、売上テーブルへの集計SQLを発行できないからです。
                        ※mappingの書き方次第では全部の売り上げのArrayCollectionなら持ってたりしますけど、一々全ての[売上]をforeachでループ廻して集計するのは現実的じゃないので却下しますw

                        さて、どうするか。
                        doctrineのイベント使ったら、[支店]エンティティからメソッド呼び出したタイミングで[売上]に対する集計ができたりするのかな?
                        コントローラ側でSQL発行するときにDBテーブルにない項目含めてもエンティティが取りだせたっけ?
                        ブログチュートリアルやったレベルのビギナーはここで完全に途方に暮れます(’・ω・`)
                        いっそsymfony1にフレームワークを変更しようか…まで考えます。

                        ところがどっこい、Symfony2をとことん勉強しないと不可能に思えた、各行ごとに別テーブル(同じテーブルに対してでもいいですが)からのデータを取り出す裏ワザ、TwigExtensionを使うと実現できちゃうんです。
                        カスタムTwig拡張の書き方(まだユーザー会翻訳がなくて英語版のみ)
                        http://symfony.com/doc/master/cookbook/templating/twig_extension.html

                        以下、簡単な書き方サンプル(タイプヒントとかちゃんと書いてない悪い例ですよ〜)

                        namespace My¥CoolBundle¥Twig¥Extension;

                        class BadKnowHowExtension extends ¥Twig_Extension
                        {
                            private $em;

                            public function __construct($doctrine)
                            {
                                $this->em = $doctrine->getEntityManager();
                            }

                            public function getName()
                            {
                                //他と被らなければ何を入れてもOK
                                return 'bad_knowhow';
                            }

                            public function getFunctions()
                            {
                                return array(
                                    'monthly_uriage' => new ¥Twig_Function_Method($this, 'calculateMonthlyAmount'),
                                );
                            }

                            public function calculateMonthlyAmount($branch, $year, $month)
                           {
                                return $em->getRepository('MyCoolBundle:売上')->calculateMonthlyAmountForBranch($branch->getId(), $year, $month);
                            }
                        }
                        #My/CoolBundle/Resources/config/services.yml
                        services:
                            my.cool_bundle.twig.bad_knowhow_extension:
                                class: My¥CoolBundle¥Twig¥Extension¥BadKnowHowExtension
                                arguments:
                                    - @doctrine
                                tags:
                                    - { name: twig.extension }

                        ポイントはservices.ymlで自作のTwigExtensionを登録するときにargumentsとして@doctrineを入れておくこと。
                        これで自作TwigExtensionのコンストラクタでentityManagerを取得することができ、好き勝手にSQLを発行させることができます。Ψ(`∀´)Ψ

                        あとはtwig側から呼び出すだけ!
                        {% for branch in list %}
                          <tr>
                            <td>{{ branch.name }}</td>
                            //略
                            <td>{{ monthly_uriage(branch, year, month) }}</td>
                            //略
                          </tr>
                        {% endfor %}


                        #ホントのホントにバッドノウハウもいいところですが、これを読んだら「そんな酷い書き方しちゃいかん」とユーザー会のすごい人たちが出てきて正しい方法を教えてくれるかもしれない、という期待を込めて書いてみた(キリッ



                        PR

                        calendar

                        S M T W T F S
                         123456
                        78910111213
                        14151617181920
                        21222324252627
                        28293031   
                        << May 2017 >>

                        twitter

                        selected entries

                        categories

                        archives

                        recent comment

                        • 結局CodeIgniter用汎用Modelクラス&汎用CRUDスクリプトを書きました
                          プログラマー
                        • icu4.4以上が用意できないサーバーでSymfony2.3以上を使う方法
                          よし
                        • icu4.4以上が用意できないサーバーでSymfony2.3以上を使う方法
                          ななうぇぶ
                        • icu4.4以上が用意できないサーバーでSymfony2.3以上を使う方法
                          よし
                        • icu4.4以上が用意できないサーバーでSymfony2.3以上を使う方法
                          よし
                        • WindowsのPCで開発するphperがxhprofを使う方法
                          ななうぇぶ
                        • WindowsのPCで開発するphperがxhprofを使う方法
                          川本
                        • [バッドノウハウ]Symfony2で別テーブルの集計項目を一覧に含めたいとき
                          よし
                        • Symfony Advent Calendar JP 2012 day 14 - vendorをcomposerで管理しているプロジェクトにcomposerを使わずにバンドルを追加したときのautoloadの書き方
                          77web
                        • Symfony Advent Calendar JP 2012 day 14 - vendorをcomposerで管理しているプロジェクトにcomposerを使わずにバンドルを追加したときのautoloadの書き方
                          ktz

                        recent trackback

                        • HTMLの表(TABLE)のセル(TD)に斜線を引くjavascriptライブラリ slash.js 作っちゃいました
                          常山日記
                        • django対symfony 日本語メール送信(その1 symfony編)
                          CPA-LABテクニカル
                        • CodeIgniterでユーザー認証
                          されどLAMPな日々
                        • 久々にdjangoを最新版にしたらHTMLがエスケープされちゃった!!(解決済み)
                          常山日記
                        • FastCGIを諦めてmod_pythonを使う。Apacheのアップグレード
                          サーバー用語集
                        • さくらインターネット、sqlite3でdjango@CGI版を使う際の設定メモ
                          常山日記
                        • さくらインターネット スタンダードプランでdjango使ってる方、DBは?
                          mitszoの日記
                        • python多次元リストをsort(並べ替え)する方法?
                          mitszoの日記
                        • フォームから送信した値とrequest.POSTの挙動($_POST@PHPとの比較)
                          Humming Via Kitchen
                        • 日本語テキストをtruncate@django(Python全般にも??)
                          常山日記

                        recommend

                        links

                        profile

                        search this site.

                        others

                        mobile

                        qrcode

                        powered

                        無料ブログ作成サービス JUGEM