Здесь опишу как я боролся с переездом страниц на новые url.
Эта заметка рассчитана на новичков в Ruby On Rails.
Изначально имею свой проект на Ruby on Rails, структура url в нем выглядит следующим образом: /locale/group/product
пример: /ru/bar-code-scanners/datalogic-magelan-1100i
group и product это permalink — строка по которой осуществляется поиск в DB, в место id.
Проблема в том, что пользователи которые добавляют контент на сайт иногда допускают ошибки в permalink.
Вот пример: /ru/bar-code-scanners/datalogic-magelan-1100i
Ошибка в том, что магелан пишется с двумя ll — magellan.
Но товар был добавлен относительно давно и страница уже проиндексирована поисковиками, по этому стоит задача исправить permalink и настроить пере адресацию на новый URL.
Конечно эту задачу можно решить на уровне nginx или apache. Но к web серверу обычно есть доступ только у администраторов и плюс править redirect еще и для нескольких locale довольно рутинная задача.
По этому я решил это дело автоматизировать, для этого создал простенькую модель Redirection с полиморфной связью, то есть она может принадлежать как Product так и Group.
Файл миграции 20131113223332_create_redirections.rb
class CreateRedirections < ActiveRecord::Migration
def change
create_table :redirections do |t|
t.references :redirectable, polymorphic: true
t.string :permalink
t.timestamps
end
end
end
А вот как выглядит сама модель Redirection:
class Redirection < ActiveRecord::Base
attr_accessible :permalink
belongs_to :redirectable, polymorphic: true
def self.product(permalink)
redirection = Redirection.where(permalink: permalink, redirectable_type: "Product").first
redirection.redirectable if not redirection.nil?
end
def self.group(permalink)
redirection = Redirection.where(permalink: permalink, redirectable_type: "Group").first
redirection.redirectable if not redirection.nil?
end
end
Теперь осталось подправить каждую модель, для которой нужно настроить пере адресацию Group и Product.
Добавляем связь с моделью Redirection.
has_many :redirections, as: :redirectable, :dependent=>:destroy
Теперь нужно реализовать отслеживать изменения атрибута permalink и при необходимости создавать запись Redirection.
К счастью все модели которые имею permalink наследованны от одного моего промежуточного класса AbstractContent. Так что достаточно добавить код отслеживания изменение permalink только в этом классе, не нарушая принципов DRY.
И вот за что я люблю Rails — само отслеживание реализовать оказалось элементарно. Rails расширяет нашу модель и ее атрибуты очень удобными методами _changed? и _was.
Все что нужно, это добавить callback after_save, который и будет отслеживать изменения и в случае необходимости создаст новую запись модели Redirection
class AbstractContent < ActiveRecord::Base
self.abstract_class = true
after_save :check_permalink_changes
def check_permalink_changes
if self.permalink_changed?
if self.permalink_was
self.redirections.create!(permalink: self.permalink_was)
end
end
end
end
Осталось только настроить саму переадресацию в контроллере
class GroupsController < ApplicationController
def show
@group = Group.find_by_permalink(params[:id])
if @group.nil?
@group = Redirection.group(params[:id]) || not_found
redirect_to group_path(@group), status: 301
end
end
end
И не забываем добавить status 301 — moved permanently. По умолчанию возвращается 302 — moved temporarily.
Думаю, что приводить код второго контроллера здесь нет смысла потому, что он аналогичен.
Так же замечу, что в реальном коде проекта поиск find_by_permalink осуществляется через кеш, find_in_cache отсюда убрал для упрощения примера.
Для удобного вызова ошибки 404 добавил в ApplicationController метод not_found вызывающий Exception.
def not_found
raise ActionController::RoutingError.new('Not Found')
end
К стати, если не охота рыться в production.log, а есть желание просматривать все ошибки по запросам URL именно нужной нам структуры /group/product. То здесь удобно добавить логирование всех вызовов not_found в отдельный файл.
P.S. Ну и можно еще дополнительно добавить RedirectionsController для управления и отслеживания всех переадресаций.
Автор: greenif