Эта заметка рассчитана на новичков в 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 для управления и отслеживания всех переадресаций.
ссылка на оригинал статьи http://habrahabr.ru/post/203520/
Добавить комментарий