Сбросить INSERT queryBuilder для повторного использования в Phinx

У меня особая проблема с 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 включает в себя встроенную систему кеширования для оптимизации производительности.


803
1

Ответ:

Решено

Повторное использование запросов может быть сложным, и я не уверен, что это действительно рекомендуется. В любом случае, по умолчанию значения добавлен, а не заменены, это позволяет легко динамически создавать многострочные запросы на вставку, так что это ваша проблема.

Если вы хотите заменить значения, вам необходимо получить доступ к базовому объекту \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();