Динамические маршруты с Rails 3

У меня есть задача разработать приложение rails, следуя модели маршрутизации.

Мне нужна модель PageController и Page. URL-адреса страницы должны быть как /contacts, /shipping, /some_page.

Также мне нужна модель CatalogController и Category. Категории URL-адреса должны быть похожими на /laptops, /smartphones/android.

И это будет модель ProductsController и Product, для URL-адресов продуктов должна быть строка /laptops/toshiba_sattelite_l605, /smartphones/android/htc_magic

Я понимаю, что эту проблему можно решить, используя URL-адреса типа

  • /page/shipping
  • /catalog/smartphones/android

Но клиент не хочет видеть в URL URL-адреса "/page" или "/catalog".

Скажите, пожалуйста, направление для решения этой проблемы. Извините за мой плохой английский.

Ответ 1

Вам нужно написать правило "catch-all":

На маршрутах .rb:

get '*my_precioussss' => 'sauron#one_action_to_rule_them_all'

Ваш драгоценный контроллер:

class SauronController < ApplicationController
  def one_action_to_rule_them_all
    @element = find_by_slug(params[:my_precioussss])
    render @element.kind # product, category, etc
  end
end

Затем вы пишете одно представление для каждого "вида" элемента: product.html.erb, category.html.erb и т.д.

Убедитесь, что вы пишете реализацию find_by_slug.

Вы можете изменить one_action_to_rule_them_all на pikachu_i_choose_you и SauronController на PokemonController, также будет работать.

Ответ 2

Вам нужно написать catch all

match '*page' => 'pages#show'

тогда обработайте путь страницы в вашем контроллере страницы

def show
  if params[:page] 
    @page = Page.find_by_page(params[:page])
  else
    @page = Page.find(params[:id])
  end
...

если вы используете Omniauth или другой rails lib, который использует свой собственный catch, вам может понадобиться исключить из него некоторые URL-адреса, используйте lambda в своем ограничении

match '*page' => 'pages#show', :constraints => lambda{|req|  !req.env["REQUEST_URI"].include? "auth"}

Если вы хотите иметь вложенные пути (например, "smartphones/android" ), вам нужно разделить параметр: page param и обработать сегменты пути в вашем контроллере.

path = :page.split("/")  // you now have an array of path segments you can use in your controller

Ответ 4

Я сделал это в сочетании двух подходов

  • использовать friendly_id gem для работы с пулями в поисках запросов вместо числовых идентификаторов

  • определить маршруты с помощью :constraints=>ConditionClass, которые должны возвращать true или false (так что маршрут будет определен или нет)

маленький пример:

#routes.rb
    class CategoryBrandGoodConstraint
      def self.matches?(request)
        path_parts = request.path.split('/')
        Category.find(path_parts[1]).present?&&Brand.find(path_parts[2]).present?&&Good.find(path_parts[3]).present? rescue false
      end
    end

    get ':category_friendly_id/:brand_friendly_id/:good_friendly_id' => 'goods#index', :constraints => CategoryBrandGoodConstraint
    #another conditional routes with another :constraints=>ConditionClass2 definition
    #...
    #until you define all necessary conditional application routes

#goods_controller.rb

    def index

      #operate with params[:category_friendly_id], params[:brand_friendly_id], params[:good_friendly_id] to collect all you need for view rendering

    end

конечно, вы можете перенаправить на другой контроллер, чтобы отобразить другое представление get '/:whatever' => 'another_controller#another_action', :constraints=>ConditionClassN

надеюсь, что это поможет!

веселит

Ответ 5

Маршруты Rails основаны исключительно на URL. Если у вас нет /pages или /categories или что-то еще до имени, то как Rails собирается определить, является ли URL-адрес, такой как http://example.com/smartphones URL-адрес страницы или URL-адрес категории? Если у вас нет предопределенного списка имен страниц в условиях маршрутов, он не может.

Единственный способ сделать это в этом примере - это фактически искать страницу smartphones, и если ни один не найден, предположим, что это имя категории и поиск категории. Это невозможно сделать в маршрутах рельсов. Для этого вам понадобится один контроллер, который будет обрабатывать все эти URL-адреса и выполнять этот поиск, а затем отображать правильный шаблон (вы не можете перенаправить, так как это изменит URL-адрес). И это уродливый способ решить эту проблему.

Если бы я был вами, я бы попытался убедить клиента согласиться на какой-то префикс (например, /p_pagename) или предпочтительно разные URL-адреса. Это может быть что-то вроде /p/shipping для страниц, а все остальные URL-адреса могут быть перенаправлены на CategoriesController или ProductsController.