From 86703bb545c14f95fce3bda7af41858630a793da Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:33:14 +0100 Subject: [PATCH] Migrate first master variant image to product image (cherry picked from commit 21cba0aa13b0539110e1ae9988e15ea6b01a69b5) --- ...3181837_migrate_master_image_to_product.rb | 46 +++++++++++++++++++ db/schema.rb | 2 +- spec/factories/asset_factory.rb | 9 ++++ spec/migrations/migrate_master_images_spec.rb | 39 ++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20230603181837_migrate_master_image_to_product.rb create mode 100644 spec/factories/asset_factory.rb create mode 100644 spec/migrations/migrate_master_images_spec.rb diff --git a/db/migrate/20230603181837_migrate_master_image_to_product.rb b/db/migrate/20230603181837_migrate_master_image_to_product.rb new file mode 100644 index 0000000000..3a2e2615ec --- /dev/null +++ b/db/migrate/20230603181837_migrate_master_image_to_product.rb @@ -0,0 +1,46 @@ +class MigrateMasterImageToProduct < ActiveRecord::Migration[7.0] + def up + # Multiple images can be present per variant, ordered by the `position` column. + # In some cases if the image was deleted and another was added, the numbering can be off, + # so the positions of the images might be 3,4,5 or 2,3 and the "first" image would be position 2 + # or position 3 in those cases (instead of 1). + # + # This finds the image for each variant with the lowest `position` out of the current images and + # sets it's `position` to 1 (if it's not already 1) to make it easier to operate on the "first" image. + renumber_first_image + + # Switches the association of the first image for each master variant to it's product + migrate_master_images + end + + def renumber_first_image + ActiveRecord::Base.connection.execute(<<-SQL + UPDATE spree_assets + SET position = '1' + FROM ( + SELECT DISTINCT ON (viewable_id) id, viewable_id, position + FROM spree_assets + WHERE spree_assets.viewable_type = 'Spree::Variant' + ORDER BY viewable_id, position ASC + ) variant_first_image + WHERE spree_assets.id = variant_first_image.id + AND spree_assets.position != '1' + SQL + ) + end + + def migrate_master_images + ActiveRecord::Base.connection.execute(<<-SQL + UPDATE spree_assets + SET viewable_type = 'Spree::Product', + viewable_id = spree_variants.product_id + FROM spree_variants + WHERE spree_variants.id = spree_assets.viewable_id + AND spree_variants.is_master = true + AND spree_variants.deleted_at IS NULL + AND spree_assets.viewable_type = 'Spree::Variant' + AND spree_assets.position = '1' + SQL + ) + end +end diff --git a/db/schema.rb b/db/schema.rb index ee71765608..6d00733f5b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_05_22_120633) do +ActiveRecord::Schema[7.0].define(version: 2023_06_03_181837) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" enable_extension "plpgsql" diff --git a/spec/factories/asset_factory.rb b/spec/factories/asset_factory.rb new file mode 100644 index 0000000000..5d6648432f --- /dev/null +++ b/spec/factories/asset_factory.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :asset, class: Spree::Asset do + viewable { nil } + position { 1 } + type { "Spree::Image" } + end +end diff --git a/spec/migrations/migrate_master_images_spec.rb b/spec/migrations/migrate_master_images_spec.rb new file mode 100644 index 0000000000..800338e972 --- /dev/null +++ b/spec/migrations/migrate_master_images_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_relative '../../db/migrate/20230603181837_migrate_master_image_to_product' + +describe MigrateMasterImageToProduct do + subject { MigrateMasterImageToProduct.new } + + let!(:product1) { create(:product) } + let!(:master1) { create(:variant, product: product1, is_master: true) } + let!(:image1) { create(:asset, viewable: master1, position: 1) } + let!(:image2) { create(:asset, viewable: master1, position: 2) } + + let!(:product2) { create(:product) } + let!(:master2) { create(:variant, product: product2, is_master: true) } + let!(:image3) { create(:asset, viewable: master2, position: 2) } + let!(:image4) { create(:asset, viewable: master2, position: 3) } + let!(:image5) { create(:asset, viewable: master2, position: 4) } + + describe "#renumber_first_image" do + it "updates the first image to position 1 if it's not already 1" do + subject.renumber_first_image + + expect(image1.reload.position).to eq 1 + expect(image3.reload.position).to eq 1 + end + end + + describe "#migrate_master_images" do + before { image3.update_columns(position: 1) } + + it "migrates the master variant image to the product" do + subject.migrate_master_images + + expect(product1.reload.image.id).to eq image1.id + expect(product2.reload.image.id).to eq image3.id + end + end +end