3
4

More than 5 years have passed since last update.

FuelPHP:PDO+PostgreSQLで、insert時にidがとれないエラーを回避する

Last updated at Posted at 2015-03-26

問題

FuelPHPのModel_Crudを継承したモデルでcreateを行うと、生成後のidがとれず下記のようなエラーとなる

Property "id" not found for Model_Monkey. in /vagrant_data/html/fuelphp/fuel/core/classes/model/crud.php on line 441

これはPDOのlastInsertId()を使って生成後のidをとってきているのが原因。PDOのlastInsertId()はPostgreSQLでは最後に追加したidを返さない。

対策

PostgreSQLでserial型(mysqlでいうところのauto_increment相当)を使う場合にはinsert文の最後に returning 節をつけることでidを返すことができる

create table foo (id serial, name text);
insert into foo (name) values ('bar') returning id;
#=> 
id
---
1

ということでfuelPHPに手を入れる
まずはinsert文を作っているところ

fuel/core/classes/database/query/builder/insert.php
public function compile($db = null)
{
  // .. snip ..

  if(in_array("id", $this->_columns)){
     $query .= " returning id";
  }

  return $query;
}

PDOを実行しているところ

fuel/core/classes/database/pdo/connection.php
// .. snip
elseif ($type === \DB::INSERT)
{

  $row = $result->fetch(\PDO::FETCH_ASSOC);
  $ret_id = array_key_exists('id',$row) ? $row['id'] : 0;
  return array($ret_id,
               $result->rowCount());
  );
}

とりあえず期待の動作はした。こんなんでいいんだろか

変更

idがないテーブルでやってしまうとうまくいかない。つまりmigrationでコケることが判明した

diff

diffはっておく

pg.patch
diff --git fuel/core/classes/database/pdo/connection.php fuel/core/classes/database/pdo/connection.php
index 4c1cd3d..ef352b3 100644
--- fuel/core/classes/database/pdo/connection.php
+++ fuel/core/classes/database/pdo/connection.php
@@ -324,10 +324,14 @@ class Database_PDO_Connection extends \Database_Connection
                elseif ($type === \DB::INSERT)
                {
                        // Return a list of insert id and rows created
-                       return array(
-                               $this->_connection->lastInsertId(),
-                               $result->rowCount(),
-                       );
+                       $row = $result->fetch(\PDO::FETCH_ASSOC);
+      if($row){
+                               $ret_id = $row && array_key_exists('id',$row) ? $row['id'] : 0;
+                       } else {
+                               $ret_id = 0;
+                       }
+                       return array($ret_id,
+                                                                       $result->rowCount());
                }
                else
                {
diff --git fuel/core/classes/database/query/builder/insert.php fuel/core/classes/database/query/builder/insert.php
index 68bee47..214b38a 100644
--- fuel/core/classes/database/query/builder/insert.php
+++ fuel/core/classes/database/query/builder/insert.php
@@ -196,6 +196,10 @@ class Database_Query_Builder_Insert extends \Database_Query_Builder
                        $query .= (string) $this->_values;
                }

+               if(in_array("id", $this->_columns)){
+            $query .= " returning id";
+         }
+
                return $query;
        }

diff --git fuel/core/classes/dbutil.php fuel/core/classes/dbutil.php
index 0951ae4..7d7947a 100644
--- fuel/core/classes/dbutil.php
+++ fuel/core/classes/dbutil.php
@@ -123,7 +123,7 @@ class DBUtil
                {
                        $key_name = \DB::quote_identifier(implode('_', $primary_keys), $db ? $db : static::$connection);
                        $primary_keys = \DB::quote_identifier($primary_keys, $db ? $db : static::$connection);
-                       $sql .= ",\n\tPRIMARY KEY ".$key_name." (" . implode(', ', $primary_keys) . ")";
+                       $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")";
                }

                empty($foreign_keys) or $sql .= static::process_foreign_keys($foreign_keys);
@@ -709,4 +709,3 @@ class DBUtil
        }

 }
-

下記のように実行するとうまくいくのでは

$ patch -p0 -u  --ignore-whitespace --verbose < pg.patch
3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4