• Миграция медиабиблиотеки Wordpress в S3

    В жизни каждого разработчика есть такие задачи, которыми невозможно гордиться, а упоминать их немного стыдно — потому что всё, абсолютно всё, связанное с такими задачами плохо. Поговорим, например, о Wordpress: он хранит данные в сериализованных массивах в базе данных, не умеет в миграции данных, нарушает все мыслимые и немыслимые, гласные и негласные правила написания хорошего кода… Но зато используется в продакшене, приносит бизнесу деньги, а значит — должен поддерживаться, развиваться… а, ну еще вам может понадобиться перенести всю медиа-библиотеку на S3 (на самом деле, это может быть любой CDN).

    Примерный план действий такой:

    1. Установить плагин для работы с S3
    2. Найти все изображения
    3. Залить их на S3
    4. Изменить пути до изображений в постах
    5. По возможности сохранить рассудок

    Чтобы было веселее — все эти действия должны произойти в автоматическом режиме, без нажимания кнопочек в админке, изменения конфигов, т.к. установка предполагается в AWS Elastic Beanstalk. И лучше не спрашивайте меня почему.

    Решение

    Для начала, необходимо установить в Wordpress штатным образом плагин «tantanwordpresss3», чтобы не изобретать в очередной раз велосипед. С файлами плагина всё просто — они станут частью приложения, а вот конфигурацию придется немного подправить вручную.

    $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}')
      ");
    }

    Самое главное — не забудьте скопировать сами файлы, благо это самая простая часть задачи.