digital matter

‘symfony’ タグのついている投稿

DoctrineMongoDBBundleでAbstractDoctrineExtensionのエラー

PHP Fatal error:  Class ‘Symfony\\Bridge\\Doctrine\\DependencyInjection\\AbstractDoctrineExtension’ not found in /home/path/to/Symfony/vendor/bundles/Symfony/Bundle/DoctrineMongoDBBundle/DependencyInjection/DoctrineMongoDBExtension.php on line 31

php – DoctrineMongoDBBundle getting a fatal error in Symfony2 – Stack Overflow

masterリポジトリが書き換えられていて、最新バージョンでは動かないのでdepsファイルにDoctrineMongoDBBundleのレポジトリを指定するときに、バージョン番号を指定する必要があるということでした。
DoctrineMongoDBBundleセクションにversion=v2.0.0を追加すればOKです。

[doctrine-mongodb]
    git=http://github.com/doctrine/mongodb.git

[doctrine-mongodb-odm]
    git=http://github.com/doctrine/mongodb-odm.git

[DoctrineMongoDBBundle]
    git=http://github.com/symfony/DoctrineMongoDBBundle.git
    target=/bundles/Symfony/Bundle/DoctrineMongoDBBundle
    version=v2.0.0

関連する投稿

Windows版のPHP5.3がc0000005で強制終了する

Symfony 1.4.16をWindowsのPHP 5.3.9で実行したときに、特定の条件で強制終了する現象に悩まされていました。
主にSymfonyタスクでdoctrine:data-dumpなどを実行するときに落ちていました。

色々調べた結果、Symfonyに問題があるわけではなく、/lib/vendor/symfony/lib/config/sfDefineEnvironmentConfigHandler.class.phpがきっちり4096バイトだったということが原因でした。

つまり、PHP5.3.9では、4096バイトのファイルをrequireもしくはincludeするとc0000005(Access Violation)で強制終了します。CLIに限らず、Apache経由でも同じです。

試しに4096バイトの一切コードが書かれていないテキストファイルを作ってrequireしてみると、再現しました。

強制終了時のウィンドウと、内容は以下のとおり。

image

ちなみにモジュール名はnts(Non Thread Safe)版ならphp5.dll、ts(Thread Safe)版ならphp5ts.dllです。
どちらも確認しましたが、どちらも同じ現象です。モジュールは入っていても入っていなくても同じです。

問題の署名:
  問題イベント名:    APPCRASH
  アプリケーション名:    php.exe
  アプリケーションのバージョン:    5.3.9.0
  アプリケーションのタイムスタンプ:    4f0c5cd0
  障害モジュールの名前:    php5.dll
  障害モジュールのバージョン:    5.3.9.0
  障害モジュールのタイムスタンプ:    4f0c5dfb
  例外コード:    c0000005
  例外オフセット:    00090f5b
  OS バージョン:    6.1.7601.2.1.0.256.48
  ロケール ID:    1041
  追加情報 1:    0a9e
  追加情報 2:    0a9e372d3b4ad19135b953a78882e789
  追加情報 3:    0a9e
  追加情報 4:    0a9e372d3b4ad19135b953a78882e789

多分現象はこのバグ報告と同じだと思います。
PHP :: Bug #60758 :: require() crashes Apache

5.4.0RC5でも解決してないみたいですね。報告とほぼ同じですが、これで再現できます。

<?php
file_put_contents('test.txt', str_pad('', 4096));
include('./test.txt');

とりあえず対処法としては改行1文字でも増やしておけばいいってことですか…

関連する投稿

Symfonyの機能テストでclickを使わずにmultipartのファイルPOSTをする方法

Symfony 1.4で実装したAPIのFunctionalテストをする際に、ファイルをPOSTする方法に迷ったので調べました。

ファイルのアップロードテストは、通常のWebサイトであれば、

$browser->
    get('/upload')->
    click('Upload', array('form' => array(
        'file1' => sfConfig::get('sf_test_dir').'/datas/test01.jpg',
    )))
;

みたいに書くのですが、テストしたいアプリケーションはAPIなので、GETするページはありません。

sfBrowserBase::clickを参考にして、こんな感じで実装しました。

まずはテスト本体。test/functional/api/hogeActionsTest.php

<?php

include(dirname(__FILE__).'/../../bootstrap/functional.php');

$browser = new MyTestFunctional(new myBrowser());
$browser->loadData();

$browser->
    info('JPEGファイルをアップロードできる')->
    setUploadFile('file1', sfConfig::get('sf_test_dir').'/datas/test01.jpg')->
    post('/upload', array('title' => 'hoge'))->

    with('response')->begin()->
        isStatusCode(200)->
    end()->
;

次にmyBrowserを定義します。lib/test/myBrowser.class.php

<?php

class myBrowser extends sfBrowser
{

    public function setUploadFile($key, $filename)
    {
        if (is_readable($filename))
        {
          $fileError = UPLOAD_ERR_OK;
          $fileSize = filesize($filename);
        }
        else
        {
          $fileError = UPLOAD_ERR_NO_FILE;
          $fileSize = 0;
        }

        $this->parseArgumentAsArray($key, array(
            'name' => basename($filename),
            'type' => '',
            'tmp_name' => $filename,
            'error' => $fileError,
            'size' => $fileSize,
        ),
        $this->files);
    }

}

sfBrowserBaseクラスのfilesに対して配列でファイルの情報を書き込むと、POST直前に$_FILESに書きこんでくれます。

で、あとはMyでmyBrowser::setUploadFileを呼び出すだけです。lib/test/MyTestFunctional.class.php

<?php

class MyTestFunctional extends sfTestFunctional
{
    public function loadData()
    {
        $doctrine = Doctrine_Manager::getInstance()->getCurrentConnection()->getDbh();
        $doctrine->query('SET FOREIGN_KEY_CHECKS = 0');
        $doctrine->query('TRUNCATE TABLE uploads');
        $doctrine->query('SET FOREIGN_KEY_CHECKS = 1');
        unset($doctrine);

        Doctrine::loadData(sfConfig::get('sf_test_dir').'/fixtures/');
        return $this;
    }

    public function setUploadFile($key, $filename)
    {
        $this->browser->setUploadFile($key, $filename);

        return $this;
    }
}

こうやってsetUploadFile(…)->post(…)と呼び出せばOKです。

sfBrowserBase::callされるたびにfilesの中身はクリアされます。

関連する投稿

sfFlickrPluginのRequest failed.に対処する

symfony 1.4にて、Flickrから画像を取得して使いたかったので、sfFlickrPluginを使ってみました。

sfFlickrPluginのインストール方法はこちらのサイトを参考にチェックアウトできます。

symfonyでFlickrを使用するためのPluginまとめ – Layer8 Reference

基本的には手動によるセットアップです。
plugins/に展開します。
自分はエクスポートしました。

$ cd plugins/
$ svn export http://svn.symfony-project.com/plugins/sfFlickrPlugin

キーに関してはさきほどのサイトを参考に。

あとは

config/ProjectConfiguration.class.php

に、enablePluginsを追加すればOKです。

  public function setup()
  {
    $this->enablePlugins('sfDoctrinePlugin');
    $this->enablePlugins('sfFlickrPlugin');
  }

で、セットアップ完了。

ここから表題の話。

グループから写真を取得したい場合はgetGroupPhotoListを使います。

が、getGroupPhotoListを実行すると、

Request failed. The requested URL returned error: 413

というエラーで停止。

plugins/sfFlickrPlugin/lib/vendor/Phlickrを最新に差し替えることで解決します。

Phlickrはこちらにあります。

Phlickr | Download Phlickr software for free at SourceForge.net

Phlickrの最新版は0.2.8だったので、丸ごと上書きします。

ファイル一式とFramework、TextUiディレクトリも上書き

それ以外のディレクトリは調べてないのでわからないですが、まぁ、置いておけば良いのでは。

Testsは、いらない雰囲気。

これで正常に動くようになりました。

あと、桁あふれの問題があるので、

PhlickrのbuildImgUrlでPhoto IDが桁あふれ : blog.loadlimit – digital matter -

を参考にして修正してください。

関連する投稿

Symfony 1.4からZendFrameworkでPicasaWebAlbumsにアクセスする

symfony-projectのページがGoogle検索に引っかかりにくい(引っかかってもバージョン違ったりする)ので色々キーワード入れてメモ。

ZendFrameworkをSymfonyに導入する方法は以下のJobeetの17日目に書いてありました。

Practical symfony | 17日目: 検索 | symfony | Web PHP Framework

cd lib/vendor/
wget http://framework.zend.com/releases/ZendFramework-1.11.7/ZendFramework-1.11.7.zip
unzip ZendFramework-1.11.7.zip
mv ZendFramework-1.11.7/library/Zend/ .

とりあえずlib/vendorに入れておきました。

あとはconfig/ProjectConfiguration.class.phpに上記ページのコードを貼りつけて、使う直前にProjectConfiguration::registerZend();してやればOK。

Picasaの方は

Zend Framework: Documentation: Picasa Web Albums の使用法 – Zend Framework Manual

このページを参考にしてできます。

丸投げだけど日本語だしわかりやすいので特に解説も不要かな、と…

関連する投稿

Symfonyでembed18nを使う

Symfonyで多言語対応する予定があるけど、とりあえず日本語だけ使いたかったときのメモです。embedI18nを自在に使う参考に。例によってSymfony 1.4+Doctrine。

// HogeForm.class.php
public HogeForm extends BaseHogeForm
{

    public function configure()
    {
        parent::configure();

        $this->embedI18n(array('ja'));

        // 二次元配列でembedしたフォームにアクセスできる
        $this->validatorSchema['ja']['name']->setOption('required', true);

        $this->useFields(array('ja', 'foo', 'bar'));
    }

}

このままテンプレートで$form->render()とかしてしまうと、入れ子表示になってしまうのでrenderRowでそれぞれ表示するようにしてやれば、useFieldsでembedされたフォームの順番がいじれないという問題も解決。

// hogeCreateSuccess.php
$form['ja']['name']->renderRow();
$form['foo']->renderRow();
$form['bar']->renderRow();

ちなみにsfFormDoctrine::getI18nFormClass()とかあるのですが、クラス名が取れるだけです。

追記

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ’19-ja’ for key ‘PRIMARY’

フォームをsaveしたら上記のエラーが出ました。

updateObject後にobject->toArray()をすると見覚えのない、「ja_JP」というキーが。

Array
(
    [id] =>
    [foo] => hoge
    [bar] => moge
    [Translation] => Array
        (
            [ja] => Array
                (
                    [id] =>
                    [name] => someone
                    [lang] => ja
                )
            [ja_JP] => Array
                (
                    [id] =>
                    [name] =>
                    [lang] => ja_JP
                )
        )
)

色々調べた結果、原因は

  • langがchar(2)なので、jaとja_JPの区別がつかないこと
  • sfDoctrineRecord::getDefaultCulture()がja_JPなこと

恐らく、

http://www.symfony-project.org/jobeet/1_4/Doctrine/ja/19

このページを参考にしているとハマるのではないかと…

ちなみにlangがchar(2)なのは、Doctrine_I18nの中で定義されています。

対策として考えられるのは以下のパターン。

  • sfDoctrineRecord::setDefaultCulture(‘ja’)をコールする
  • langをchar(5)にしてja_JPの形式で扱う
  • settings.ymlのdefault_cultureをjaにする
  • embedI18nにja_JPを渡す

sfDoctrineRecordのカルチャをセットするのは、sfUserのカルチャをセットすることでイベントが駆動して同時にセットできます。なので、$this->getUser()->setCulture(‘ja’)とかしておけばOK。

langをchar(5)にするのは、I18nビヘイビアのオプションにlength:5を指定すればできます。この場合、カルチャをすべて5文字で扱うようにしないとlangがマッチしなくなります。length:2の場合は暗黙的に先頭2文字だけにマッチさせることで5文字のカルチャを許容していたので。

# schema.yml
Sample:
  actAs:
    Timestampable: ~
    I18n:
      fields: [name]
      length: 5

settings.ymlのdefault_cultureをjaにするのが一番簡単です。en_USとen_GBはどうするんだという話ですけど…

formクラスに$this->embedI18n(array(‘ja_JP’))とすることでも対応できます。

気になる方はsfDoctrineRecordI18nFilterクラスのfilterSet関数を見てください。

カタログファイルとかにも影響しそうなのでsettings.ymlに2文字カルチャをセットするのがベターかなぁ。

関連する投稿

symfonyで行の並び順を指定できるビヘイビアを使ってみた

今回はこちらを参考に、csDoctrineActAsSortablePluginを使ってみました。

$ ./symfony plugin:install csDoctrineActAsSortablePlugin
>> plugin    installing plugin "csDoctrineActAsSortablePlugin"
>> sfPearFrontendPlugin downloading csDoctrineActAsSortablePlugin-1.5.1.tgz ...
>> sfPearFrontendPlugin Starting to download csDoctrineActAsSortablePlugin-1.5.1.tgz
>> sfPearFrontendPlugin (6,795 bytes)
>> sfPearFrontendPlugin .
>> sfPearFrontendPlugin .
>> sfPearFrontendPlugin ...done: 6,795 bytes
>> sfSymfonyPluginManager Installation successful for plugin "csDoctrineActAsSortablePlugin"
>> sfSymfonyPluginManager Installing web data for plugin

使い方はschema.ymlのモデルのActAsにSortableを追加してやればいいだけです。

ModelName:
  actAs:
    Timestampable: ~
    Sortable: ~

これで、自動的にpositionというbigintのカラムが生成されて、ソート順が数字で指定できるようになります。

併せて順序を入れ替えるための便利なメソッドが使えるようになります。

http://www.symfony-project.org/plugins/csDoctrineActAsSortablePlugin/1_5_1?tab=plugin_readme

今回、I18nの下にSortableを置いたら、インデックス名がやたら長くなってしまい、CREATE文でエラーが出るようになってしまいました。

SQLSTATE[42000]: Syntax error or access violation: 1059 Identifier name '******_*********_***_*******_******_*****_translation_position_sortable_idx' is too long.

plugins/csDoctrineActAsSortablePlugin/lib/template/Sortable.php

  protected function getSortableIndexName()
  {
    return sprintf('%s_%s_%s', $this->getTable()->getTableName(), $this->_options['name'], $this->_options['indexName']);
  }

ということなので、nameとindexNameを指定して短くできそうです。

と言ってもposition_sortable分しか短くできないのですけど。

最終的には、schema.ymlの方でtableNameをdatabase_name.table_nameで指定していたのですが、これをtableName: table_nameにするだけで対応できました。

関連する投稿

MySQL Workbenchからschema.ymlを生成する

MySQL Workbenchからsymfony+Doctrine用のschema.ymlを書き出せないかなぁとつぶやいたら@hidenorigotoさんに教えていただいたので試してみました。
http://twitter.com/#!/hidenorigoto/status/38153994970988544

MySQL Workbench schema exporter

MySQL Workbench schema exporterは、MySQL WorkbenchプラグインのMySQL Workbench Doctrine Pluginにインスパイアを受けて開発されたようです。
が、こちらのプラグインはすでにメンテされていないようです。

開発終了した理由がぞろぞろ書いてありますが、要約すると「LUAが…」ってことみたいです。

MySQL Workbench schema exporterはPHPで書かれています。MySQL Workbenchのmwbファイルを読み込んでパース、フォーマッタを指定してそれぞれの形式を書き出すようです。
ちなみにmwbファイルはZIP圧縮されたXMLです。あと、SQLite3のデータが入ってました。

注意点としてPHP 5.3以降でないと実行できません…
いつものdebian環境では5.2を使ってるので、Windows版の5.3をダウンロードしてインストールしました。

まずはexampleディレクトリに移動します。

C:\Users\test\Downloads\johmue-mysql-workbench-schema-exporter-7d08e29\example>php -v
PHP 5.3.5 (cli) (built: Jan  5 2011 20:36:18)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies

example/data/にtest.mwbがあり、サンプルではこのファイルを使います。

ちなみにサンプルのEER(Enhanced Entity-Relationship) diagramはこんな感じです。

test.mwb

C:\Users\test\Downloads\johmue-mysql-workbench-schema-exporter-7d08e29\example>php .\doctrine1.yaml.php
<textarea cols="100" rows="50">Bureau:
  tableName: mydb.bureaus
  columns:
    id:
      type: integer(4)
      primary: true
      notnull: true
      autoincrement: true
    room:
      type: string(45)
  indexes:
    testIndex:
      fields: [room]
      type: unique
  options:
    charset: utf8
    type: InnoDB

Email:
  actAs:
    timestampable:
  tableName: mydb.emails
  columns:
    id:
      type: integer(4)
      primary: true
      notnull: true
      autoincrement: true
    email:
      type: string(255)
    users_id:
      type: integer(4)
      primary: true
      notnull: true
  relations:
    User:
      class: User
      local: users_id
      foreign: id
      foreignAlias: Emails
      onDelete: no action
      onUpdate: no action
  indexes:
    fk_Emails_Users:
      fields: [users_id]
  options:
    charset: utf8
    type: InnoDB

User:
  actAs:
    timestampable:
  tableName: mydb.users
  columns:
    id:
      type: integer(4)
      primary: true
      notnull: true
      autoincrement: true
    name:
      type: string(255)
  options:
    charset: utf8
    type: InnoDB

UsersBureaus:
  tableName: mydb.users_bureaus
  columns:
    users_id:
      type: integer(4)
      primary: true
      notnull: true
    bureaus_id:
      type: integer(4)
      primary: true
      notnull: true
  relations:
    User:
      class: User
      local: users_id
      foreign: id
      foreignAlias: UsersBureauss
      onDelete: no action
      onUpdate: no action
    Bureau:
      class: Bureau
      local: bureaus_id
      foreign: id
      foreignAlias: UsersBureauss
      onDelete: no action
      onUpdate: no action
  indexes:
    fk_users_bureaus_bureaus1:
      fields: [bureaus_id]
  options:
    charset: utf8
    type: InnoDB

Testtable:
  tableName: mydb2.testtable
  columns:
    id:
      type: integer(4)
      primary: true
      notnull: true
  options:
    charset: utf8
    type: InnoDB
</textarea><br><br>1 MB used<br>0.055 sec needed

というわけで、リレーションやインデックスなども書きだされるようです。

HTMLのタグはdoctrine1.yaml.phpの中に書いてあるので、CLIから使うときは適当に外してください。

timestampableなどのビヘイビアに関しては、コメント欄に書くことで出力できるようです。

{d:actAs}
  actAs:
    timestampable:
{/d:actAs}

この仕様はちょっと微妙な気が…

created_atとupdated_atがあったらtimestampable付けるとかの方が楽だなぁ、と。

作っちゃった後の全テーブルにコメント指定して回るのはちょっと辛いので。

サーバにPHP 5.3が入っているなら、symfony taskでmwbからschema.ymlの生成と、ActAsの追加をやってしまうのがいいかな、と思いました。

うーん、個人的にはMySQL Workbench上で完結したいから、プラグインの方が便利な気はするんですけどねぇ。LUAとは言わなくてもPythonスクリプティング対応しているようなので、Pythonに書き直すとか…

この後、60テーブルほどのファイルを変換してみましたが、問題なく実行できました。

追記(2011/02/19)

注意点がいくつかあります。

というか、致命的かも。

1.NOT NULLのchar型にDEFAULT値”を入れるとNULLに変換される

これはDoctrineのbuild –sqlがおかしいかな…

column_name:

  type: char(3)

  notnull: true

  default: ”

column_name char(3) DEFAULT NULL NOT NULLとかいう矛盾を持ったクエリが生成されます。

ちなみにvarcharは大丈夫です。

default: ”の行は削除しましょう。

2.DECIMALやFLOATの最大桁数と少数点の値が逆になる

ひどい。

例えばMySQL WorkbenchでDECIMAL(15,4)と指定したカラムがDECIMAL(4,15)と書き出されます。

column_name:

  type: decimal(4,15)

  notnull: true

  default: ’0.0000′

もちろん、ちゃんとMySQL Workbenchからクエリのエクスポートをしたときは15,4になります。

置換で何とか…

うーん、やっぱりMySQL WorkbenchでDBをあらかじめ作成しておいてから→doctrine:build-schema→schema.yml修正が妥当かなぁ。

関連する投稿

A cache key must contain both a module and an action parameter

Symfony 1.4にて、Flash上のリンクからページにリンクをしたところ、500エラーが発生。

A cache key must contain both a module and an action parameter

同じURLをそのまま別のウィンドウで開くと問題なく開けるので、リクエストを見比べたところ、違いはrefererの有無だけ。

エラーはinclude_partialの先で起きていて、調べていくと、パラメータを渡さないpartialに、config/cache.ymlでcontextual: trueとしていたのが問題でした。

falseにして解決。

ちなみにこのException自体も、本来は404を出すはずのところらしいです。

あと、キャッシュ有効にしないとこのエラーは出ません。

関連する投稿

symfonyで携帯端末用のテンプレート切り替えを実装する

symfony 1.4で、PCサイトの携帯版を作ることになりました。

アクションは共通にして、テンプレートのファイル名を、携帯とPCで切り分けるようにしたいと思います。

例:
templates/indexSuccess.php (PC用)
templates/indexSuccess.mobile.php (携帯用)

で、これはフォーマットを指定することで実現できます。フォーマットに関してはJobeetを参照。

Practical symfony | 15日目: Web サービス | symfony | Web PHP Framework

まずは携帯キャリアのIPから高速にキャリア判定をしてくれるプラグインを準備しておきます。

sfMobileIPPluginをsymfony 1.4で使ってみた

次にフィルタを用意します。

app/frontend/lib/filter/myMobileLayoutFilter.class.php

<?php

class myMobileLayoutFilter extends sfFilter
{
    public function execute($filterChain)
    {
        if ($this->isFirstCall()) {

            if (sfMobileIP::carrier() != 'pc') {
                $request = $this->getContext()->getRequest();
                $request->setRequestFormat('mobile');
            }

        }

        $filterChain->execute();
    }
}

最後にfilters.ymlを編集すればOKです。

app/frontend/config/filters.yml

rendering: ~
security:  ~

myMobileLayoutFilter:
  class: myMobileLayoutFilter

cache:     ~
execution: ~

layoutのテンプレートも携帯版を用意しておきます。

例:

app/frontend/templates/layout.php (PC用)

app/frontend/templates/layout.mobile.php (携帯用)

注意点として、partialなども全部「.mobile.php」の形式のファイルがないと動作しません。どこかでファイルを作り忘れるとsfRenderExceptionが投げられるので注意してください。ちなみに、dev環境だとそのまま真っ白な画面になってしまって、原因追求がしにくいようです。

その場合は、ログファイルにてチェックしましょう。

log/frontend_dev.log

symfony [err] {sfRenderException} The template "indexSuccess.mobile.php" does not exist or is unreadable in "".

関連する投稿