SQL Миграция через Phinx. Ошибка при миграции, ругается на id записи
Задача: В таблице есть поле содержащие тело статьи(текст). В этом тексте есть ссылки старого формата, нужно пройтись регуляркой и заменить формат. К сожалению на проекте версия MySQL 5.7, а функции для регулярной замены (REGEXP_REPLACE) была добавлена в 8.0. Ещё нужно учитывать что на проекте все работы с БД происходят через миграции Phinx(из Cake).
Было принято решение сделать выборку по всем статьям(их +- 6к) и перебрать тексты статьи и заменить ссылки силами php, в цикле.
Вроде бы всё должно работать но почему то БД ругается на id. Пометка: 27 id - это id первой записи в таблице.
public function up()
{
$rows = $this->fetchAll("SELECT id, body from blogposts");
foreach ($rows as $post )
{
$post['body'] = preg_replace('/sustainability/(\d+/\d+/\d+)/(.+)', '/sustainability/$2', $post['body']);
$postId = $post['id'];
$postBody = $post['body'];
$this->query( "UPDATE blogposts SET body = $postBody WHERE id = $postId ")->save();
}
}
Прошу подсказать и разобраться в чем может быть проблема?
Ответы (2 шт):
обычно такое делается в виде
$bpt = TableRegistry::getTableLocator()->get('BlogPosts');
$items = $bpt->find('all')->select(['id', 'body'])->toArray();
foreach($items as $p){
$p->body = preg_replace("/..../", "...", $p->body);
}
$pbt->saveAll($items);
при этом учесть, что регулярное выражение должно иметь вид /expr/flags, то есть имеет ограничители в начале и конце. То есть не /sustainability/(\d+/\d+/\d+)/(.+) а ~/sustainability/(\d+/\d+/\d+)/(.+)~. (Обычно в качестве ограничителей выступают /, но поскольку эти символы у вас используются, то лучше заменить на ~ или #, в основном используют эти 3 варианта)
для таких целей имхо лучше не миграции использовать а Command сгенерить.
Документация к phinx, велит делать примерно следующим образом
$rows = $this->fetchAll("SELECT id, body from blogposts WHERE body like '%/sustainability/%'");
$builder = $this->getQueryBuilder();
foreach($rows as $row){
$body = ...;
if(!$changed) continue;
$builder->update('blogposts')
->set(['body', $body])
->where(['id' => $row->id)
->execute();
}
Однако, да, getQueryBuilder в конечном итоге сводится к PdoAdapter::getDecoaratedConnection, возвращающий экземпляр \Cake\DatabaseConnection.
Интересно, что в этой ситуации методы execute() и query() имеют только один параметр - текст запроса и не позволяют передавать параметры.
Возможно, альтернативой будет получить нижележащий экземпляр PDO и выполнить запросы через привычные подготовленные выражения.
Получить вроде можно через $this->getAdapter()->getConnection().
А дальше обычные $connection->prepare() и execute() с передачей параметров.
Удалось решить.
$rows = $this->fetchAll("SELECT id, body from blogposts");
foreach ($rows as $post )
{
$post['body'] = preg_replace('~/sustainability/(\d+/\d+/\d+)/(.+)~', '/sustainability/$2', $post['body']);
$post['body'] = addslashes($post['body']);
$postId = $post['id'];
$postBody = $post['body'];
$this->execute( "UPDATE blogposts SET body = '$postBody' WHERE id = $postId ");
Добавил обработку экранирования, т.к в статьях содержаться апострофы свойственные для английского языка.
$post['body'] = addslashes($post['body']);
Так же в запросе на UPDATE переменную содержащую текст необходимо обромить в ковычки.
$this->execute("UPDATE blogposts SET body = '$postBody' WHERE id = $postId")
