Rails переадресация старых url

от автора

Здесь опишу как я боролся с переездом страниц на новые 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 для управления и отслеживания всех переадресаций.

ссылка на оригинал статьи http://habrahabr.ru/post/203520/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *