SONICMOOV Googleページ

【symfony】Cannot insert a value for auto-increment primary keyの解消法

【symfony】Cannot insert a value for auto-increment primary keyの解消法

  • このエントリーをはてなブックマークに追加

こんにちはこんばんはシステムエンジニアやってますcubeです。

弊社のアプリケーションは大体symfony1.4のpropelで作られていまして、実稼働しているアプリケーションでの管理画面もsymfony1.4のpropelで作りました。
symfonyではadmin-generateと呼ばれる管理画面が以下のタスク実行で簡単に作られるのでとてもお手軽ですね。

 
php symfony generate:app backend php symfony propel:generate-admin backend テーブル名 --module=作成されるモジュール名 

ymfonyの公式ページなんかが詳しく解説されていますね
第14章 – Admin ジェネレーター

そして今回は

 Cannot insert a value for auto-increment primary key (◯◯) 

の解消法。

これはsymfonyのアプリケーションからauto-increment指定されているprimary keyを指定して登録しようとした時に出力されるPropelExceptionでのエラー。
symfonyというよりもpropelのエラーですね。 このエラーをgoogle検索した時に出てくる記事は、弊社システムエンジニアkazさんの以下の記事
symfonyではまったDBのID変更~備忘録~

ほうほう、こんな感じで解消できるのねーと理解はしたものの、数十テーブル一つ一つに書いて行くのはちょっとキツいかもかも.. オーバーライドするのも面倒なので、勝手にbuildしないようにする方法は無いのかな? という面倒臭い精神でbuildするタスクを読んで見る事に。 まずbuildしてるのどこなのーってことでgrep。

 $ grep -r 'Cannot insert a value for auto-increment primary key' lib/vendor lib/vendor/symfony/lib/plugins/sfPropelPlugin/lib/vendor/propel-generator/classes/propel/engine/builder/om/php5/PHP5PeerBuilder.php:1126: throw new PropelException('Cannot insert a value for auto-increment primary key ('.".$this->getColumnConstant($col).".')'); lib/vendor/symfony/lib/plugins/sfPropelPlugin/test/functional/fixtures/lib/model/om/BaseArticlePeer.php:1192: throw new PropelException('Cannot insert a value for auto-increment primary key ('.ArticlePeer::ID.')'); lib/vendor/symfony/lib/plugins/sfPropelPlugin/test/functional/fixtures/lib/model/om/BaseAttachmentPeer.php:749: throw new PropelException('Cannot insert a value for auto-increment primary key ('.AttachmentPeer::ID.')'); lib/vendor/symfony/lib/plugins/sfPropelPlugin/test/functional/fixtures/lib/model/om/BaseAuthorPeer.php:483: throw new PropelException('Cannot insert a value for auto-increment primary key ('.AuthorPeer::ID.')'); lib/vendor/symfony/lib/plugins/sfPropelPlugin/test/functional/fixtures/lib/model/om/BaseBookPeer.php:483: throw new PropelException('Cannot insert a value for auto-increment primary key ('.BookPeer::ID.')'); lib/vendor/symfony/lib/plugins/sfPropelPlugin/test/functional/fixtures/lib/model/om/BaseCategoryPeer.php:483: throw new PropelException('Cannot insert a value for auto-increment primary key ('.CategoryPeer::ID.')'); lib/vendor/symfony/lib/plugins/sfPropelPlugin/test/functional/fixtures/lib/model/om/BaseMoviePeer.php:486: throw new PropelException('Cannot insert a value for auto-increment primary key ('.MoviePeer::ID.')'); lib/vendor/symfony/lib/plugins/sfPropelPlugin/test/functional/fixtures/lib/model/om/BaseProductPeer.php:487: throw new PropelException('Cannot insert a value for auto-increment primary key ('.ProductPeer::ID.')'); 

`sfPropelPlugin/test`下のファイル群はbuild後に作成されたものだろうと推測可能なので問題のbuildされている部分は

 lib/vendor/symfony/lib/plugins/sfPropelPlugin/lib/vendor/propel-generator/classes/propel/engine/builder/om/php5/PHP5PeerBuilder.php:1126: throw new PropelException('Cannot insert a value for auto-increment primary key ('.".$this->getColumnConstant($col).".')'); 

だと特定できます。`(‘.”.$this->getColumnConstant($col).”.’)`と動的に作成されているのでまず間違いないでしょう。

 vi lib/vendor/symfony/lib/plugins/sfPropelPlugin/lib/vendor/propel-generator/classes/propel/engine/builder/om/php5/php5peerbuilder.php 

php5peerbuilder.php

 foreach ($table->getColumns() as $col) { $cfc = $col->getPhpName(); if ($col->isPrimaryKey() && $col->isAutoIncrement() && $table->getIdMethod() != "none" && !$table->isAllowPkInsert()) { $script .= " if (\$criteria->containsKey(".$this->getColumnConstant($col).") && \$criteria->keyContainsValue(" . $this->getColumnConstant($col) . ") ) { throw new PropelException('Cannot insert a value for auto-increment primary key ('.".$this->getColumnConstant($col).".')'); } if (!$this->getPlatform()->supportsInsertNullPk()) { $script .= " // remove pkey col since this table uses auto-increment and passing a null value for it is not valid \$criteria->remove(".$this->getColumnConstant($col)."); } } elseif ($col->isPrimaryKey() && $col->isAutoIncrement() && $table->getIdMethod() != "none" && $table->isAllowPkInsert() && !$this->getPlatform()->supportsInsertNullPk()) { $script .= " // remove pkey col if it is null since this table does not accept that if (\$criteria->containsKey(".$this->getColumnConstant($col).") && !\$criteria->keyContainsValue(" . $this->getColumnConstant($col) . ") ) { \$criteria->remove(".$this->getColumnConstant($col)."); } } } 

っと、対象の生成部分のコードがこれですね。 `if ($col->isPrimaryKey() && $col->isAutoIncrement() && $table->getIdMethod() != “none” && !$table->isAllowPkInsert()) {` の部分がありますね。このif文に入らないように、`false`になるように値を設定してやれば良さそうです。 名前的に`$table->isAllowPkInsert()`ってのが怪しいですね。 なのでまたgrep

 $ grep -r isAllowPkInsert lib/vendor lib/vendor/symfony/lib/plugins/sfPropelPlugin/lib/vendor/propel-generator/classes/propel/engine/builder/om/php5/PHP5PeerBuilder.php:1123: if ($col->isPrimaryKey() && $col->isAutoIncrement() && $table->getIdMethod() != "none" && !$table->isAllowPkInsert()) { lib/vendor/symfony/lib/plugins/sfPropelPlugin/lib/vendor/propel-generator/classes/propel/engine/builder/om/php5/PHP5PeerBuilder.php:1136: } elseif ($col->isPrimaryKey() && $col->isAutoIncrement() && $table->getIdMethod() != "none" && $table->isAllowPkInsert() && !$this->getPlatform()->supportsInsertNullPk()) { lib/vendor/symfony/lib/plugins/sfPropelPlugin/lib/vendor/propel-generator/classes/propel/engine/database/model/Table.php:892: public function isAllowPkInsert() 

宣言している場所はここですね `lib/vendor/symfony/lib/plugins/sfPropelPlugin/lib/vendor/propel-generator/classes/propel/engine/database/model/Table.php:892: public function isAllowPkInsert()`

 vi lib/vendor/symfony/lib/plugins/sfPropelPlugin/lib/vendor/propel-generator/classes/propel/engine/database/model/Table.php 

Table.php

 /** * Whether we allow to insert primary keys on tables with * idMethod=native * * @return boolean */ public function isAllowPkInsert() { return $this->allowPkInsert; } 

のメソッドがそうですね。コメントの内容的にここに`true`を入れれば目的の「primary keyを指定して登録」が出来るようになるなー `$this->allowPkInsert`をセットしてるとこを探しましょう

Table.php

 /** * Sets up the Rule object based on the attributes that were passed to loadFromXML(). * @see parent::loadFromXML() */ public function setupObject() { $this->name = $this->getAttribute("name"); $this->phpName = $this->getAttribute("phpName"); $this->idMethod = $this->getAttribute("idMethod", $this->getDatabase()->getDefaultIdMethod()); $this->allowPkInsert = $this->booleanValue($this->getAttribute("allowPkInsert")); 

同じファイル内の`setupObject`で入れているようですね`$this->getAttribute(“allowPkInsert”);`を`true`に入れれば良い事が解ります。 このgetしているattributeはどこじゃーいとなると、またコメントを見てみると` * @see parent::loadFromXML()`の記述が。 XMLを読み込みだったり一緒に`name`だったり`phpName`だったり`idMethod`なりを取得してるーってことでとりあえず当たりが付いたので探索は終了。 XMLを使ったり`phpName`だったりの記述があるところは`config/schema.yml`の中って事が解りました。 試しに変更してみます

 $ vi config/schema.yml $ svn diff hugehu_ge_table: - _attributes: { phpName: HugehuGeTable } + _attributes: { allowPkInsert: true, phpName: HugehuGeTable } $ ./symfony propel:build-model $ svn diff - if ($criteria->containsKey(HugehuGeTablePeer::ID) && $criteria->keyContainsValue(HugehuGeTablePeer::ID) ) { - throw new PropelException('Cannot insert a value for auto-increment primary key ('.HugehuGeTablePeer::ID.')'); - } - 

対象の箇所が無事削除されました。 これでおーしまい! の人もいるとは思うのですが、いやいやまだまだ。 弊社の環境では`schema.yml`の直変更はしていなく、build用のデータベースを作成して`schema.yml`を自動生成しています。 以下のタスクですね。

 ./symfony propel:build-schema --env="buildonly" 

なのでー、書き換えちゃうと次回の`schema.yml`作成の時に戻ってしまうんですね。 どうしよー yml作成のタスクでそんなオプション無いか?と思い探してみた物の、無いようですね.. しょうがないので今までのタスクをコピーして`sncPropel:build-schema`を作成、タスクが動くようにclass名を変えたり読み込んでるbaseファイルのパス変えるぐらい。 そして、以下の箇所を

 // Fix database name if (file_exists($xmlSchemaPath)) { $schema = file_get_contents($xmlSchemaPath); $schema = preg_replace('/<database\s+name="[^"]+"/s', '<database name="'.$connection.'" package="lib.model"', $schema); file_put_contents($xmlSchemaPath, $schema); } 

こういう風に変更してしまえば

 // Fix database name if (file_exists($xmlSchemaPath)) { $schema = file_get_contents($xmlSchemaPath); $schema = preg_replace('/<database\s+name="[^"]+"/s', '<database name="'.$connection.'" package="lib.model"', $schema); // snc追加分、allowPkInsertでID指定の登録が可能に $schema = preg_replace('/\s+<table\s+(name="[^"]+")/s', '<table ${1} allowPkInsert="true" ', $schema); file_put_contents($xmlSchemaPath, $schema); } 

タスク実行でも`schema.yml`は書き変わったものになりますね!

  • このエントリーをはてなブックマークに追加

記事作成者の紹介

cube(システムエンジニア)

ソーシャルアプリ事業部でシステムエンジニアをやっています。cubeです。cubeというニックネームは主に会社で使っていて、個人でのネット上のハンドルネームはsoramugiで主に活動しています。

関連するSONICMOOVのサービス

システムエンジニア募集中!

×

SNSでも情報配信中!ぜひご登録ください。

×

SNSでも
情報配信中!
SONICMOOV Facebookページ SONICMOOV Twitter SONICMOOV Googleページ
システムエンジニア募集中!

新着の記事

mautic is open source marketing automation