From 56205b7052e36ca686061163d9c191ecca45cefd Mon Sep 17 00:00:00 2001 From: miteruzo Date: Sun, 27 Sep 2026 17:13:02 +0900 Subject: [PATCH] #55 --- backend/app/controllers/tags_controller.rb | 134 ++++++++++++++------- backend/app/models/tag.rb | 30 ++--- backend/app/models/tag_name.rb | 7 ++ 3 files changed, 104 insertions(+), 67 deletions(-) diff --git a/backend/app/controllers/tags_controller.rb b/backend/app/controllers/tags_controller.rb index d8cbcd4..b1057c2 100644 --- a/backend/app/controllers/tags_controller.rb +++ b/backend/app/controllers/tags_controller.rb @@ -5,6 +5,8 @@ require 'set' class TagsController < ApplicationController def index + locale = resolve_locale! + post_id = params[:post] name = params[:name].presence @@ -36,9 +38,14 @@ class TagsController < ApplicationController tags = if post_id.present? - Tag.joins(:posts, :tag_name).where(posts: { id: post_id }) + Tag + .joins(:posts, :tag_names) + .where(tag_names: { language_code: locale.language_code, primary_flg: true }, + posts: { id: post_id }) else - Tag.joins(:tag_name) + Tag + .joins(:tag_names) + .where(tag_names: { language_code: locale.language_code, primary_flg: true }) end external_tags = @@ -167,9 +174,10 @@ class TagsController < ApplicationController tags_by_id = Tag - .joins(:tag_name) - .includes(:tag_name, :materials, tag_name: :wiki_page) - .where(id: tag_ids) + .joins(:tag_names) + .includes(:tag_names, :materials, tag_names: :wiki_page) + .where(tag_names: { language_code: locale.language.code, primary_flg: true }, + id: tag_ids) .index_by(&:id) external_tags_by_id = ExternalTag @@ -205,6 +213,8 @@ class TagsController < ApplicationController end def autocomplete + locale = resolve_locale! + q = params[:q].to_s.strip.sub(/\Anot:/i, '') prefix = "#{ ActiveRecord::Base.sanitize_sql_like(q) }%" @@ -213,28 +223,28 @@ class TagsController < ApplicationController alias_rows = TagName + .where(language_code: locale.language_code, primary_code: false) .where('name LIKE ?', prefix) - .where.not(canonical_id: nil) - .pluck(:canonical_id, :name) + .pluck(:tag_id, :name) matched_alias_by_tag_name_id = { } - canonical_ids = [] - - alias_rows.each do |canonical_id, alias_name| - canonical_ids << canonical_id - matched_alias_by_tag_name_id[canonical_id] ||= alias_name + tag_ids = [] + alias_rows.each do |tag_id, alias_name| + tag_ids << tag_id + matched_alias_by_tag_name_id[tag_id] ||= alias_name end base = Tag - .joins(:tag_name) - .includes(:tag_name, :materials, tag_name: :wiki_page) - .where(deprecated_at: nil) + .joins(:tag_names) + .includes(:tag_names, :materials, tag_names: :wiki_page) + .where(tag_names: { language_code: locale.language_code, primary_flg: true }, + deprecated_at: nil) base = base.where('tags.post_count > 0') if present_only canonical_hit = base.where('tag_names.name LIKE ?', prefix) - internal_tags = canonical_hit.or(base.where(tag_name_id: canonical_ids.uniq)) + internal_tags = canonical_hit.or(base.where(id: tag_ids.uniq)) internal_rows = internal_tags @@ -265,11 +275,14 @@ class TagsController < ApplicationController end def show + locale = resolve_locale! + tag = Tag - .joins(:tag_name) - .includes(:tag_name, :materials, tag_name: :wiki_page) - .find_by(id: params[:id]) + .joins(:tag_names) + .includes(:tag_names, :materials, tag_names: :wiki_page) + .find_by(id: params[:id], + tag_names: { language_code: locale.language_code, primary_flg: true }) return render json: TagRepr.base(tag) if tag external_tag = ExternalTag.find_by(id: params[:id]) @@ -282,11 +295,13 @@ class TagsController < ApplicationController name = params[:name].to_s.strip return render_bad_request('name は必須です.') if name.blank? + locale = resolve_locale! + tag = Tag - .joins(:tag_name) - .includes(:tag_name, :materials, tag_name: :wiki_page) - .find_by(tag_names: { name: }) + .joins(:tag_names) + .includes(:tag_names, :materials, tag_names: :wiki_page) + .find_by(tag_names: { name:, language_code: locale.language_code, primary_flg: true }) return render json: TagRepr.base(tag) if tag platform, external_name = name.split(':', 2) @@ -299,9 +314,13 @@ class TagsController < ApplicationController end def deerjikists - tag = Tag.joins(:tag_name) - .includes(:tag_name, tag_name: :wiki_page) - .find_by(id: params[:id]) + locale = resolve_locale! + + tag = + Tag.joins(:tag_names) + .includes(:tag_names, tag_names: :wiki_page) + .find_by(id: params[:id], + tag_names: { language_code: locale.language_code, primary_flg: true }) return head :not_found unless tag render json: { tag: TagRepr.base(tag), @@ -312,9 +331,13 @@ class TagsController < ApplicationController name = params[:name].to_s.strip return render_bad_request('name は必須です.') if name.blank? - tag = Tag.joins(:tag_name) - .includes(:tag_name, tag_name: :wiki_page) - .find_by(tag_names: { name: }) + locale = resolve_locale! + + tag = + Tag + .joins(:tag_names) + .includes(:tag_names, tag_names: :wiki_page) + .find_by(tag_names: { name:, language_code: locale.language_code, primary_flg: true }) return head :not_found unless tag render json: { tag: TagRepr.base(tag), @@ -325,14 +348,19 @@ class TagsController < ApplicationController return head :unauthorized unless current_user return head :forbidden unless current_user.gte_member? - tag = Tag.joins(:tag_name) - .includes(:tag_name, tag_name: :wiki_page) - .find_by(id: params[:id]) + tag = + Tag + .joins(:tag_names) + .includes(:tag_names, tag_names: :wiki_page) + .find_by(id: params[:id], + tag_names: { language_code: locale.language_code, primary_flg: true }) return head :not_found unless tag rows = normalise_deerjikist_rows(tag) return if performed? + locale = resolve_locale! + ApplicationRecord.transaction do tag.lock! @@ -346,7 +374,7 @@ class TagsController < ApplicationController row_indexes_by_key.key?([deerjikist.platform, deerjikist.code]) } - render_deerjikist_conflicts(requested_deerjikists, row_indexes_by_key, tag) + render_deerjikist_conflicts(locale, requested_deerjikists, row_indexes_by_key, tag) raise ActiveRecord::Rollback if performed? requested_keys_set = requested_keys.to_set @@ -367,7 +395,7 @@ class TagsController < ApplicationController render_deerjikist_form_record_invalid(deerjikist, row_index) unless deerjikist.save rescue ActiveRecord::RecordNotUnique conflicts = lock_deerjikists_for_tag_update(tag.id, [[platform, code]]) - render_deerjikist_conflicts(conflicts, row_indexes_by_key, tag) + render_deerjikist_conflicts(locale, conflicts, row_indexes_by_key, tag) end raise ActiveRecord::Rollback if performed? end @@ -381,11 +409,16 @@ class TagsController < ApplicationController def materials_by_name name = params[:name].to_s.strip return render_bad_request('name は必須です.') if name.blank? + + locale = resolve_locale! + material_filter = material_filter_param(default: 'any') - tag = Tag.joins(:tag_name) - .includes(:tag_name, :materials, tag_name: :wiki_page) - .find_by(tag_names: { name: }) + tag = + Tag + .joins(:tag_names) + .includes(:tag_names, :materials, tag_names: :wiki_page) + .find_by(tag_names: { name:, language_code: locale.language_code, primary_flg: true }) return head :not_found unless tag graph = build_with_depth_graph(material_filter) @@ -506,6 +539,8 @@ class TagsController < ApplicationController end def build_with_depth_graph material_filter + locale = resolve_locale! + children_by_parent_id = Hash.new { |h, k| h[k] = [] } parent_ids_by_child_id = Hash.new { |h, k| h[k] = [] } @@ -520,10 +555,13 @@ class TagsController < ApplicationController material_tag_ids = Material.unscoped.kept.where.not(tag_id: nil).distinct.pluck(:tag_id).to_set - tags_by_id = Tag.joins(:tag_name) - .where(id: tag_ids) - .pluck('tags.id', 'tag_names.name', 'tags.category', 'tags.deprecated_at') - .each_with_object({ }) do |(id, name, category, deprecated_at), h| + tags_by_id = + Tag + .joins(:tag_names) + .where(id: tag_ids, + tag_names: { language_code: locale.language_code, primary_flg: true }) + .pluck('tags.id', 'tag_names.name', 'tags.category', 'tags.deprecated_at') + .each_with_object({ }) do |(id, name, category, deprecated_at), h| h[id] = { name:, category:, deprecated: deprecated_at.present?, has_material: material_tag_ids.include?(id) } end @@ -857,15 +895,19 @@ class TagsController < ApplicationController .to_a end - def render_deerjikist_conflicts deerjikists, row_indexes_by_key, tag + def render_deerjikist_conflicts locale, deerjikists, row_indexes_by_key, tag conflicts = deerjikists.filter { |deerjikist| deerjikist.tag_id != tag.id } return if conflicts.empty? - tag_names_by_id = Tag - .joins(:tag_name) - .where(id: conflicts.map(&:tag_id).uniq) - .pluck('tags.id', 'tag_names.name') - .to_h + locale = resolve_locale! + + tag_names_by_id = + Tag + .joins(:tag_names) + .where(tag_names: { language_code: locale.language_code, primary_flg: true }, + id: conflicts.map(&:tag_id).uniq) + .pluck('tags.id', 'tag_names.name') + .to_h fields = { } conflicts.each do |deerjikist| diff --git a/backend/app/models/tag.rb b/backend/app/models/tag.rb index a8cdfd6..9add0ca 100644 --- a/backend/app/models/tag.rb +++ b/backend/app/models/tag.rb @@ -51,12 +51,9 @@ class Tag < ApplicationRecord has_many :tag_versions has_many :tag_names - delegate :wiki_page, to: :tag_name attribute :version_no, :integer, default: 1 - validates :tag_name, presence: true - enum :category, deerjikist: 'deerjikist', meme: 'meme', character: 'character', @@ -66,8 +63,6 @@ class Tag < ApplicationRecord validates :category, presence: true, inclusion: { in: Tag.categories.keys } - validate :tag_name_mustnt_start_with_nico - validate :tag_name_must_be_canonical validate :category_must_be_deerjikist_with_deerjikists CATEGORY_PREFIXES = { @@ -84,6 +79,7 @@ class Tag < ApplicationRecord def tag_name(language_code) = tag_names.find_by(language_code:, primary_flg: true) def name(language_code) = tag_name(language_code)&.name + def wiki_page = WikiPage.find_by(tag_name_id: id) def deprecated? = deprecated_at? @@ -234,7 +230,7 @@ class Tag < ApplicationRecord tag = tn.tag return tag if tag - tag = Tag.create!(tag_name: tn, category:) + tag = Tag.create!(tag_name_id: tn.id, category:) tn.update!(tag:) tag @@ -295,28 +291,20 @@ class Tag < ApplicationRecord target_tag.reload end - def snapshot_aliases = tag_name.aliases.order(:name).pluck(:name) + def snapshot_aliases(language_code) = tag_name(language_code).aliases.order(:name).pluck(:name) def snapshot_parent_tag_ids = parents.order(:id).pluck(:id) - def snapshot_linked_tag_names - linked_tags.joins(:tag_name).order('tag_names.name').pluck('tag_names.name') + def snapshot_linked_tag_names language_code + linked_tags + .joins(:tag_names) + .where(tag_names: { language_code:, primary_flg: true }) + .order('tag_names.name') + .pluck('tag_names.name') end private - def tag_name_mustnt_start_with_nico - if name.to_s.downcase.start_with?('nico:') - errors.add :name, 'タグの命名規則に反してゐます.' - end - end - - def tag_name_must_be_canonical - if tag_name&.canonical_id? - errors.add :tag_name, 'tag_names へは実体を示す必要があります.' - end - end - def category_must_be_deerjikist_with_deerjikists if !(deerjikist?) && deerjikists.exists? errors.add :category, 'ニジラーと紐づいてゐるタグはニジラー・カテゴリである必要があります.' diff --git a/backend/app/models/tag_name.rb b/backend/app/models/tag_name.rb index 046cc27..0dc2ebc 100644 --- a/backend/app/models/tag_name.rb +++ b/backend/app/models/tag_name.rb @@ -10,6 +10,7 @@ class TagName < ApplicationRecord validate :alias_name_must_not_have_prefix validate :alias_must_not_have_wiki_page validate :name_must_be_sanitised + validate :name_mustnt_start_with_nico def primary? = primary_flg @@ -58,4 +59,10 @@ class TagName < ApplicationRecord errors.add :name, '名前に使用できない文字が含まれてゐます.' end end + + def name_mustnt_start_with_nico + if name.to_s.downcase.start_with?('nico:') + errors.add :name, 'タグの命名規則に反してゐます.' + end + end end