可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a task to develop a rails application following the model for routing.
I need to have PageController
and Page
model. Page urls must be like /contacts, /shipping, /some_page
.
Also i need have CatalogController
and Category
model. Categories urls must be like /laptops, /smartphones/android
.
And it will be ProductsController
and Product
model, urls of products must be line /laptops/toshiba_sattelite_l605
, /smartphones/android/htc_magic
I understand that this problem can be solved by using URLs like
/page/shipping
/catalog/smartphones/android
But the customer does not want to see the insertion of "/page
" or "/catalog
" in the URL.
Please tell me the direction for solving this problem.
Sorry for my bad English.
回答1:
You'll have to write a "catch-all" rule:
On routes.rb:
get '*my_precioussss' => 'sauron#one_action_to_rule_them_all'
Your precious controller:
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
Then you write one view for each "kind" of element: product.html.erb, category.html.erb, etc.
Make sure you write your find_by_slug
implementation.
You can change one_action_to_rule_them_all
to pikachu_i_choose_you
and SauronController
to PokemonController
, will work too.
回答2:
You need to write a catch all
match '*page' => 'pages#show'
then handle the page path in your page controller
def show
if params[:page]
@page = Page.find_by_page(params[:page])
else
@page = Page.find(params[:id])
end
...
if you use Omniauth or another rails lib that uses it's own catch all you may need to exclude some urls from this, use a lambda in your constraint
match '*page' => 'pages#show', :constraints => lambda{|req| !req.env["REQUEST_URI"].include? "auth"}
If you want to have nested paths (e.g. "smartphones/android") you'll need to split the :page param apart and handle the path segments in your controller
path = :page.split("/") // you now have an array of path segments you can use in your controller
回答3:
I found this guide to rails 3 routing incredibly helpful:
http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/
回答4:
i have done it in combination of two approaches
use friendly_id gem to operate with slugs in find requests instead of numeric ids
define routes with :constraints=>ConditionClass
which has to return true
or false
(so route will be determined or not)
small example:
#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
of course, you can route to another controller to render another view get '/:whatever' => 'another_controller#another_action', :constraints=>ConditionClassN
hope it helps!
cheers
回答5:
Rails routes are based solely on URL. If you don't have /pages
or /categories
or whatever before the name, then how is Rails going to tell if a URL like http://example.com/smartphones
is a page URL or category URL? Unless you have some predefined list of pages names in the routes conditions, it cannot.
The only way to do that in this example would be to actually search for a page by smartphones
and if none is found assume that this is a category name and search for a category. This can't be done in rails routes. To do that, you'd need a single controller that would handle all those URLs and do that search and then render a proper template (you can't redirect, because that would change the url). And that is an ugly way to solve this problem.
If I were you I'd try to convince the customer to agree on some kind of prefix (like /p_pagename
) or preferably different URLs. It could be something like /p/shipping
for pages and all other URLs could be routed to CategoriesController
or ProductsController
.