在 Phinx 中重置 INSERT queryBuilder 以供重用

Reset INSERT queryBuilder for re-use, in Phinx

我在使用 Phinx(版本 0.10.8)时遇到了一个特殊的问题:我必须迁移一个 table 以便插入一个 table,然后 AUTO_INCREMENTed last-insert-ID 插入另一个table.

因为我在一个 for 循环中,所以我只想继续为第一个 table 中的插入循环使用相同的 insert-query-builder;而不是重建整个插入构建器。但是我不知道如何重置 VALUES 数据。

举例说明问题:

// insert-builder I hope to re-use.
$builder = $this->getQueryBuilder()->insert(array(
    'note',
))->into('test_table');

// cache this empty state for reset attempt #2.
$empty = $builder;

// insert one row of values.
$builder->values(array(
    'note' => "Please don't copy me. Please don't copy me. Please don't copy me ...",
))->execute();

// dump info.
var_dump($this->getAdapter()->getConnection()->lastInsertId());
$rows = $this->fetchAll("SELECT COUNT(*) FROM test_table");
var_dump($rows);

// reset attempts.    
//$builder->getValueBinder()->reset();              // (1)
$builder = $empty;                                  // (2)
//$builder->getQuery()->getValueBinder()->reset();  // (3)

// insert second row.
$builder->values(array(
    'note' => "Second insert.",
))->execute();

// dump info.
var_dump($this->getAdapter()->getConnection()->lastInsertId());
$rows = $this->fetchAll("SELECT COUNT(*) FROM test_table");
var_dump($rows);

数字 (3) 给了我一个例外,(1) 和 (2) 给了我同样的输出,即我在 2 次插入后有 3 行:

string(1) "1"
array(1) {
  [0]=>
  array(2) {
    ["COUNT(*)"]=>
    string(1) "1"
    [0]=>
    string(1) "1"
  }
}
string(1) "2"
array(1) {
  [0]=>
  array(2) {
    ["COUNT(*)"]=>
    string(1) "3"
    [0]=>
    string(1) "3"
  }
}

反正我是在摸黑钓鱼。我真的找不到任何好的文档。

/vendor/cakephp/database/ValueBinder.php 似乎有一个 public 重置方法。但我不确定如何访问该 ValueBinder。


This thread suggests to use closures, which is actually a good idea now that I think about it. They were mentioned in passing in this doc。但它是如何工作的?我傻了。

// not like this.
$values = array(
    'note' => "Please don't copy me. Please don't copy me. Please don't copy me ...",
);
$this->execute(function() use ($builder, $values) {
    return $builder->values($values)->execute();
});

// not like this.
$this->execute(function($builder) use ($values) {
    return $builder->values($values)->sql();
});

// not like this.
$builder->values(function($builder) use ($values) {
    return $builder->values($values);
})->execute();

重复使用 queris 可能很棘手,我不确定是否真的建议这样做。无论如何,默认情况下 values are being added,而不是 replaced,这允许轻松地动态构建多行插入查询,所以这就是你的问题。

如果要替换值,则需要访问基础 \Cake\Database\Expression\ValuesExpression object, which can be obtained via the query builder's clause() method。虽然您可以使用 ValuesExpression::setValues() 用新值覆盖现有值(这需要是嵌套数组,因为此方法接受多行数据),但这样做不会使查询对象回到脏状态,并且也不会清除值绑定器,因此最好的选择可能是使用 ValuesExpression::setValues() 重置数据,然后使用查询构建器 values() 方法设置新数据,例如:

$stmt = $builder
    ->values([
        'note' => 'first note'
    ])
    ->execute();
// ...
$stmt->closeCursor();

// reset the values clause
$builder
    ->clause('values')
    ->setValues([]);

// define new values, this will put the query in a dirty state and reset the value binder
$stmt = $builder
    ->values([
        'note' => 'second note'
    ])
    ->execute();
// ...
$stmt->closeCursor();