У меня особая проблема с Phinx (версия 0.10.8): мне нужно перенести одну таблицу как таковую, чтобы эта строка была вставлена в одну таблицу, а затем AUTO_INCREMENTed last-insert-ID был вставлен в другую таблицу.
Поскольку я нахожусь в цикле for, я просто хотел бы использовать один и тот же построитель запросов на вставку для вставки в первой таблице; вместо перестройки всего компоновщика вставок. Но я не знаю, как сбросить данные 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) дают мне тот же результат, что у меня есть 3 строки после 2 вставок:
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
, похоже, есть общедоступный метод сброса. Но я не уверен, как добраться до этого ValueBinder.
Эта тема предлагает использовать замыкания, что на самом деле является хорошей идеей, если подумать. Они были упомянуты вскользь в этот документ. Но как это работает? Я тупой.
// 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();
🤔 А знаете ли вы, что...
CakePHP включает в себя встроенную систему кеширования для оптимизации производительности.
Повторное использование запросов может быть сложным, и я не уверен, что это действительно рекомендуется. В любом случае, по умолчанию значения добавлен, а не заменены, это позволяет легко динамически создавать многострочные запросы на вставку, так что это ваша проблема.
Если вы хотите заменить значения, вам необходимо получить доступ к базовому объекту \Cake\Database\Expression\ValuesExpression
, который можно получить через метод построителя запросов clause()
. И хотя вы могли бы использовать 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();