Skip to content

Add mobility gem and basic usage for products #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: fix/update_sanbox.sh
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Spree
module Api
module V2
module Platform
class TaxonsController < ResourceController
class TaxonsController < TranslatableResourceController
include ::Spree::Api::V2::Platform::NestedSetRepositionConcern

private
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Spree
module Api
module V2
module Platform
class TranslatableResourceController < ResourceController
def scope
super.joins(:translations)
end
end
end
end
end
end


2 changes: 1 addition & 1 deletion api/app/helpers/spree/api/v2/collection_options_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def collection_meta(collection)
# leaving this method in public scope so it's still possible to modify
# those params to support non-standard non-JSON API parameters
def collection_permitted_params
params.permit(:format, :page, :per_page, :sort, :include, fields: {}, filter: {})
params.permit(:format, :page, :per_page, :sort, :include, :locale, fields: {}, filter: {})
end

private
Expand Down
2 changes: 2 additions & 0 deletions core/app/models/concerns/spree/product_scopes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,8 @@ def self.for_user(user = nil)
def self.search_by_name(query)
if defined?(SpreeGlobalize)
joins(:translations).order(:name).where("LOWER(#{Product::Translation.table_name}.name) LIKE LOWER(:query)", query: "%#{query}%").distinct
elsif defined?(Mobility)
where("LOWER(#{Product::Translation.table_name}.name) LIKE LOWER(:query)", query: "%#{query}%")
else
where("LOWER(#{Product.table_name}.name) LIKE LOWER(:query)", query: "%#{query}%")
end
Expand Down
18 changes: 17 additions & 1 deletion core/app/models/spree/product.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
module Spree
class Product < Spree::Base
extend FriendlyId
extend Mobility
include ProductScopes
include MultiStoreResource
include MemoizedData
Expand All @@ -36,11 +37,14 @@ class Product < Spree::Base
default_variant_id tax_category default_variant
purchasable? in_stock? backorderable?)

friendly_id :slug_candidates, use: :history
TRANSLATABLE_FIELDS = %i[name description slug meta_description meta_keywords meta_title]

friendly_id :slug_candidates, use: [:history, :mobility]

acts_as_paranoid
auto_strip_attributes :name

translates *TRANSLATABLE_FIELDS, backend: :table
# we need to have this callback before any dependent: :destroy associations
# https://github.com/rails/rails/issues/3458
before_destroy :ensure_not_in_complete_orders
Expand Down Expand Up @@ -168,6 +172,18 @@ class Product < Spree::Base
after_transition to: :draft, do: :after_draft
end

def translatable_fields
#TODO I think better solution is to implement get_translatable_fields in mobility gem(?)
TRANSLATABLE_FIELDS
end

def get_field_with_locale(locale, field_name)
Mobility.with_locale(locale) do
#Remove fallback: false if you want to see fallbacks at the table
public_send(field_name, fallback: false)
end
end

# Can't use short form block syntax due to https://github.com/Netflix/fast_jsonapi/issues/259
def purchasable?
default_variant.purchasable? || variants.any?(&:purchasable?)
Expand Down
14 changes: 14 additions & 0 deletions core/app/models/spree/taxon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

module Spree
class Taxon < Spree::Base
extend Mobility
include Metadata
if defined?(Spree::Webhooks)
include Spree::Webhooks::HasWebhooks
Expand All @@ -14,6 +15,8 @@ class Taxon < Spree::Base

acts_as_nested_set dependent: :destroy

translates :name, :description, backend: :table

belongs_to :taxonomy, class_name: 'Spree::Taxonomy', inverse_of: :taxons
has_many :classifications, -> { order(:position) }, dependent: :delete_all, inverse_of: :taxon
has_many :products, through: :classifications
Expand Down Expand Up @@ -54,6 +57,17 @@ class Taxon < Spree::Base

scope :for_stores, ->(stores) { joins(:taxonomy).where(spree_taxonomies: { store_id: stores.ids }) }

def translatable_fields
%w[name description]
end

def get_field_with_locale(locale, field_name)
I18n.with_locale(locale) do
#Remove fallback: false if you want to see fallbacks at the table
public_send(field_name, fallback: false)
end
end

# indicate which filters should be used for a taxon
# this method should be customized to your own site
def applicable_filters
Expand Down
120 changes: 120 additions & 0 deletions core/config/initializers/mobility.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
Mobility.configure do
# PLUGINS
plugins do
# Ransack plugin
ransack
# Backend
#
# Sets the default backend to use in models. This can be overridden in models
# by passing +backend: ...+ to +translates+.
#
# To default to a different backend globally, replace +:key_value+ by another
# backend name.
#
backend :table
fallbacks
locale_accessors

# ActiveRecord
#
# Defines ActiveRecord as ORM, and enables ActiveRecord-specific plugins.
active_record

# Accessors
#
# Define reader and writer methods for translated attributes. Remove either
# to disable globally, or pass +reader: false+ or +writer: false+ to
# +translates+ in any translated model.
#
reader
writer

# Backend Reader
#
# Defines reader to access the backend for any attribute, of the form
# +<attribute>_backend+.
#
backend_reader
#
# Or pass an interpolation string to define a different pattern:
# backend_reader "%s_translations"

# Query
#
# Defines a scope on the model class which allows querying on
# translated attributes. The default scope is named +i18n+, pass a different
# name as default to change the global default, or to +translates+ in any
# model to change it for that model alone.
#
query

# Cache
#
# Comment out to disable caching reads and writes.
#
cache

# Dirty
#
# Uncomment this line to include and enable globally:
dirty
#
# Or uncomment this line to include but disable by default, and only enable
# per model by passing +dirty: true+ to +translates+.
# dirty false

# Fallbacks
#
# Uncomment line below to enable fallbacks, using +I18n.fallbacks+.
# fallbacks
#
# Or uncomment this line to enable fallbacks with a global default.
# fallbacks { :pt => :en }

# Presence
#
# Converts blank strings to nil on reads and writes. Comment out to
# disable.
#
presence

# Default
#
# Set a default translation per attributes. When enabled, passing +default:
# 'foo'+ sets a default translation string to show in case no translation is
# present. Can also be passed a proc.
#
# default 'foo'

# Fallthrough Accessors
#
# Uses method_missing to define locale-specific accessor methods like
# +title_en+, +title_en=+, +title_fr+, +title_fr=+ for each translated
# attribute. If you know what set of locales you want to support, it's
# generally better to use Locale Accessors (or both together) since
# +method_missing+ is very slow. (You can use both fallthrough and locale
# accessor plugins together without conflict.)
#
# fallthrough_accessors

# Locale Accessors
#
# Uses +def+ to define accessor methods for a set of locales. By default uses
# +I18n.available_locales+, but you can pass the set of locales with
# +translates+ and/or set a global default here.
#
# locale_accessors
#
# Or define specific defaults by uncommenting line below
# locale_accessors [:en, :ja]

# Attribute Methods
#
# Adds translated attributes to +attributes+ hash, and defines methods
# +translated_attributes+ and +untranslated_attributes+ which return hashes
# with translated and untranslated attributes, respectively. Be aware that
# this plugin can create conflicts with other gems.
#
# attribute_methods
end
end
1 change: 1 addition & 0 deletions core/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,7 @@ en:
notice_messages:
icon_removed: Image has been successfully removed
prices_saved: Prices successfully saved
translations_saved: Translations successfully saved
product_cloned: Product has been cloned
product_deleted: Product has been deleted
product_not_cloned: "Product could not be cloned. Reason: %{error}"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class CreateProductNameAndDescriptionTranslationsForMobilityTableBackend < ActiveRecord::Migration[7.0]
def change
create_table :spree_product_translations do |t|

# Translated attribute(s)
t.string :name
t.text :description

t.string :locale, null: false
t.references :spree_product, null: false, foreign_key: true, index: false

t.timestamps null: false
end

add_index :spree_product_translations, :locale, name: :index_spree_product_translations_on_locale
add_index :spree_product_translations, [:spree_product_id, :locale], name: :index_89f757462683439a75913375358673bb7f45ebe0, unique: true

end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class CreateSpreeProductMetaDescriptionAndMetaKeywordsAndMetaTitleTranslationsForMobilityTableBackend < ActiveRecord::Migration[7.0]
def change
add_column :spree_product_translations, :meta_description, :text
add_column :spree_product_translations, :meta_keywords, :string
add_column :spree_product_translations, :meta_title, :string
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class ChangeProductNameNullToTrue < ActiveRecord::Migration[7.0]
def change
change_column_null :spree_products, :name, true
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class CreateSpreeTaxonNameAndDescriptionTranslationsForMobilityTableBackend < ActiveRecord::Migration[7.0]
def change
create_table :spree_taxon_translations do |t|

# Translated attribute(s)
t.string :name
t.text :description

t.string :locale, null: false
t.references :spree_taxon, null: false, foreign_key: true, index: false

t.timestamps null: false
end

add_index :spree_taxon_translations, :locale, name: :index_spree_taxon_translations_on_locale
add_index :spree_taxon_translations, [:spree_taxon_id, :locale], name: :index_spree_taxon_translations_on_spree_taxon_id_and_locale, unique: true

end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class ChangeTaxonNameNullToTrue < ActiveRecord::Migration[7.0]
def change
change_column_null :spree_taxons, :name, true
end
end
11 changes: 11 additions & 0 deletions core/db/migrate/20220802070609_add_locale_to_friendly_id_slugs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class AddLocaleToFriendlyIdSlugs < ActiveRecord::Migration[7.0]
def change
add_column :friendly_id_slugs, :locale, :string, null: :false, after: :scope

remove_index :friendly_id_slugs, [:slug, :sluggable_type]
add_index :friendly_id_slugs, [:slug, :sluggable_type, :locale], length: { slug: 140, sluggable_type: 50, locale: 2 }
remove_index :friendly_id_slugs, [:slug, :sluggable_type, :scope]
add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope, :locale], length: { slug: 70, sluggable_type: 50, scope: 70, locale: 2 }, unique: true, name: :index_friendly_id_slugs_unique
add_index :friendly_id_slugs, :locale
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class CreateSpreeProductSlugTranslationsForMobilityTableBackend < ActiveRecord::Migration[7.0]
def change
add_column :spree_product_translations, :slug, :string
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
class TransferDataToTranslatableTables < ActiveRecord::Migration[7.0]
DEFAULT_LOCALE = 'en'
PRODUCTS_TABLE = 'spree_products'
PRODUCT_TRANSLATIONS_TABLE = 'spree_product_translations'
TAXONS_TABLE = 'spree_taxons'
TAXON_TRANSLATIONS_TABLE = 'spree_taxon_translations'

def up
# Products
ActiveRecord::Base.connection.execute("
INSERT INTO #{PRODUCT_TRANSLATIONS_TABLE} (name, description, locale, spree_product_id, created_at, updated_at, meta_description, meta_keywords, meta_title, slug)
SELECT name, description, '#{DEFAULT_LOCALE}' as locale, id, created_at, updated_at, meta_description, meta_keywords, meta_title, slug FROM #{PRODUCTS_TABLE}
")
ActiveRecord::Base.connection.execute("
UPDATE #{PRODUCTS_TABLE}
SET name=null, description=null, meta_description=null, meta_keywords=null, meta_title=null, slug=null;
")
#Taxons
ActiveRecord::Base.connection.execute("
INSERT INTO #{TAXON_TRANSLATIONS_TABLE} (name, description, locale, spree_taxon_id, created_at, updated_at)
SELECT name, description, '#{DEFAULT_LOCALE}' as locale, id, created_at, updated_at FROM #{TAXONS_TABLE}
")
ActiveRecord::Base.connection.execute("
UPDATE #{TAXONS_TABLE}
SET name=null, description=null
")
end

def down
ActiveRecord::Base.connection.execute("
UPDATE #{PRODUCTS_TABLE} as products
SET (name,
description,
meta_description,
meta_keywords,
meta_title,
slug) =
(t_products.name,
t_products.description,
t_products.meta_description,
t_products.meta_keywords,
t_products.meta_title,
t_products.slug)
FROM #{PRODUCT_TRANSLATIONS_TABLE} AS t_products
WHERE t_products.spree_product_id = products.id
")

ActiveRecord::Base.connection.execute("
TRUNCATE TABLE #{PRODUCT_TRANSLATIONS_TABLE}
")

ActiveRecord::Base.connection.execute("
UPDATE #{TAXONS_TABLE} as taxons
SET (name,
description) =
(t_taxons.name,
t_taxons.description)
FROM #{TAXON_TRANSLATIONS_TABLE} AS t_taxons
WHERE t_taxons.spree_taxon_id = taxons.id
")

ActiveRecord::Base.connection.execute("
TRUNCATE TABLE #{TAXON_TRANSLATIONS_TABLE}
")
end
end
Loading