В жизни каждого разработчика есть такие задачи, которыми невозможно гордиться, а упоминать их немного стыдно — потому что всё, абсолютно всё, связанное с такими задачами плохо. Поговорим, например, о Wordpress: он хранит данные в сериализованных массивах в базе данных, не умеет в миграции данных, нарушает все мыслимые и немыслимые, гласные и негласные правила написания хорошего кода… Но зато используется в продакшене, приносит бизнесу деньги, а значит — должен поддерживаться, развиваться… а, ну еще вам может понадобиться перенести всю медиа-библиотеку на S3 (на самом деле, это может быть любой CDN).
Примерный план действий такой:
- Установить плагин для работы с S3
- Найти все изображения
- Залить их на S3
- Изменить пути до изображений в постах
- По возможности сохранить рассудок
Чтобы было веселее — все эти действия должны произойти в автоматическом режиме, без нажимания кнопочек в админке, изменения конфигов, т.к. установка предполагается в AWS Elastic Beanstalk. И лучше не спрашивайте меня почему.
Решение
Для начала, необходимо установить в Wordpress штатным образом плагин «tantan_wordpress_s3», чтобы не изобретать в очередной раз велосипед. С файлами плагина всё просто — они станут частью приложения, а вот конфигурацию придется немного подправить вручную.
$config = array(
"key" => AWS_KEY,
"secret" => AWS_SECRET,
"bucket" => AWS_BUCKET,
"wp-uploads" => 1,
"expires" => 315360000,
"permissions" => "public",
"cloudfront" => ""
);
$db->query("
INSERT INTO `wp_options` (`option_name`, `option_value`, `autoload`) VALUES ('tantan_wordpress_s3', :config, 'yes');
", array(
'config' => serialize($config)
));
Плагин также потребуется активировать:
$pluginsSerialized = $db->fetchOne("SELECT `option_value` FROM `wp_options` WHERE `option_name` = 'active_plugins'");
$plugins = unserialize($pluginsSerialized);
$plugins[] = "tantan-s3-cloudfront/wordpress-s3.php";
$db->query("UPDATE `wp_options` SET `option_value` = :option_value WHERE `option_name` = 'active_plugins';", array(
'option_value' => serialize($plugins)
));
На этом этапе все новые пополнения медиа-библиотеки попадут на S3. Осталось дело за миграцией старых данных. Для начала, выберем их все:
$stmt = $db->query("
SELECT * FROM wp_postmeta WHERE meta_key = '_wp_attachment_metadata'
");
Осталось изменить ссылки в старых данных с локальных путей на пути до файлов на CDN. В качестве библиотеки для доступа к локальным файлам использована knplabs/gaufrette.
foreach ($stmt->fetchAll() as $row) {
$data = unserialize($row['meta_value']);
$file = substr($data['file'], strpos($data['file'], '/wp-content'));
$data = array(
'bucket' => AWS_S3_BUCKET,
'key' => '/wp-content/uploads/' . $file
);
$serialized = serialize($data);
$stmt2 = $db->query("
INSERT INTO wp_postmeta (post_id, meta_key, meta_value)
VALUES (:post_id, :meta_key, :meta_value)
", array(
'meta_value' => $serialized,
'post_id' => $row['post_id'],
'meta_key' => 'amazonS3_info'
));
}
foreach ($localFilesystem->listKeys() as $key) {
$db->query("
UPDATE `wp_posts`
SET `post_content` = REPLACE(`post_content`, '/old_relative_url/wp-content/{$key}', 'http://AWS_BUCKET.s3.amazonaws.com/wp-content/{$key}')
");
}
Самое главное — не забудьте скопировать сами файлы, благо это самая простая часть задачи.