I think I'm running across a conflict due to names:
Two models: store coupon
Url needed that will display coupons: http://localhost/coupons/:store_name ('coupons' is written in the url, not replaced with anything)
Controller name: coupons_controller
Here is what I have in my routes right now:
match '/coupons/:store_name' => 'coupons#index', :as => :stores
When I try to do redirect stores_path(store) in another controller, I get this error:
No route matches {:controller=>"coupons"}
Any clues? I'm new to rails so I bet it's a silly mistake.
UPDATE
Is there a central place to tell the dynamic _path() functions to use a specific url structure? i.e. Instead of having to do the following everywhere:
redirect_to stores_path(:store_name => store.store_name)
Instead using just:
redirect_to stores_path(store)
yes you can, redefine to_param in your model:
class Store < ...
def to_param
store_name
end
end
redirect_to stores_path(:store_name => store)
should work if it doesn't (cannot confirm right now), you should be able to do the (little hacky)
redirect_to stores_path+"?store_name=yourstorename"
Doing it the restful way, you should probably have something like this (in your routes):
resources :stores do
resources :coupons # this will give you e.g. /stores/:store_id/coupons for the coupons#index action
end
If you want to use the store name instead of the ID, just search SO for using "slug" or have a look here: getting a 'name' based URL in RESTful routes instead of an id based url or ID + Slug name in URL in Rails (like in StackOverflow)
Related
I am now working on website SEO optimization and what I am required to do is proper routing for links to be very seo friendly. I have read lots of information about routing, but it messed up in my head and I stuck.
So I have Store model which belongs to StoreType model, to City model and District model + District belongs_to :city.
I need to have routes like this:
/stores/store_type_name/ - store_type 'show' action(list of stores by type)
/stores/city_name/store_type_name/ - store_type 'show' action(list of stores by city&type)
/stores/city_name/district_name/store_type_name/ - store_type 'show' action(list of stores by city&district&type)
/stores/city_name/store_type_name/store_name - store 'show' action
The only solution I came up with for now is:
Routes.rb
namespace :stores do
get ':transliterated', to: 'store_types#show'
get ':transliterated/:name_en', to: 'store_types#city'
get ':transliterated/:name_en/:id', to: 'store_types#district'
end
With controller like this:
def district
#store_type = StorerType.find_by_transliterated(params[:transliterated])
#city = City.find_by_name_en(params[:name_en])
#district = District.find_by_id(params[:id])
if #store_type && #city && #district
stores = #store_type.stores.where(city_id:#city.id)
#stores = stores.where(district_id:#district.id)
else
redirect_to root_path
end
end
That works well but 1) I can not now add route for last example(store show page) as route is looking for :transliterated params in that namespace and redirects if record is not found. 2) I understand that this solution is bad and can be done much better, I just do not know how. Give me an advice please.
PS. Actually there is routing implemented on the site already so I am looking for the solution for those 4 urls listed above only, without touching anything else there.
Resourceful
Firstly, let me define the basis of all your routing for you...
Rails' routing structure is known as being resourceful - meaning based around resources / objects. As with Ruby being an object-orientated language, Rails is an object-orientated framework; the routes are no exception to this:
This means anything you do with your routes has to be resource-based, as follows:
#config/routes.rb
namespace :stores do
resources :store_types, only: [:show], path: "" do #-> domain.com/stores/:id -> store_types#show
get :name_en, action: :city #-> domain.com/stores/:store_type_id/:name_en
get :name_en/:id, action: :district #-> domain.com/stores/:store_type_id/:name_en/:id
end
end
This will give you the ability to send the traffic directly to your store_types controller without having all sorts of crazy routes all over the place
--
friendly_id
Something else to consider is a gem called friendly_id
friendly_id basically allows you to define / call routes with slugs, rather than ids. The difference is that the routes remain the same - it's the data, and the handling of that data, which changes
Typically in Rails, you'll create routes like this: domain.com/controller/:id
When you send people to links, they'll hit domain.com/controller/1 for example. Friendly_ID basically facilities the ability to send people to domain.com/controller/your_name, handling it in exactly the same way as you would with an ID:
#app/models/your_model.rb
Class YourModel < ActiveRecord::Base
friendly_id :name, use: [:slugged, :finders]
end
This will allow you to call:
#app/controllers/your_controller.rb
Class YourController < ApplicationController
def show
#model = Model.find params[:id]
end
end
You can use some static strings in the urls to help to identify the actions, for examples:
namespace :stores do
get 'type/:transliterated', to: 'store_types#show'
get 'type/:transliterated/city/:name_en', to: 'store_types#city'
get ':transliterated/:name_en/:id', to: 'store_types#district'
end
i have routes like this :
get "/:article_id" => "categories#show", as: :articles_category
get '/:account_id' => "accounts#show", as: :show_account
but why when i access show_account_url, i always entry to articles_category_url ??
why?
how to make my routes have twice "/:id" in url with different action?
But why when i access show_account_url, i always entry to
articles_category_url ??
The problem you have is you're trying to access the same URL -- domain.com/______. Because Rails cannot process the difference, it uses the first route - your category_url.
There are two ways to deal with this:
Have a "Routing" controller / use slugs
Split your routes up conventionally
Everyone wants app-wide slugs, but you can't do it unless you have a mechanism to calculate which URL is correct. The methods you have to achieve this are either to create a routing controller, or use slugs.
Using a routing controller is actually quite simple:
#config/routes.rb
get "/:id" => "router#direct", as: :slug
#app/controllers/routers_controller.rb
def direct
#routing code (lots of ifs etc)
end
A better way is to use a slug system, which allows you to route to your slugs directly. We use this with http://firststopcosmeticshop.co.uk & the slugalicious gem:
#Slugs
begin
Slug.all.each do |s|
begin
get "#{s.slug}" => "#{s.sluggable_type.downcase.pluralize}#show", :id => s.slug
rescue
end
end
rescue
end
This allows you to send specific slugs to specific controllers / actions. The reason? It creates /[slug] routes, which you can access across the site
Further to this, you could look at the friendly_id gem -- which helps you create resourceful routes using slugs. Highly recommended
I have a community_users model that I route in the following way:
resources :communities do
resources :users
end
This creates the route /communities/:id/users/.
I'd like to configure this route so that only the name of the community with the corresponding :id is shown.
In other words, if a community has an id of '1' and the name 'rails-lovers' - the route would read:
/rails-lovers
and not:
/communities/1/users/
You might want to check out the gem friendly_id
That will give you the clean URLs you are looking for.
I'm not quite sure if this is what you're looking for, but:
One option would be to create the route
match ':community_name' => 'users#show_users_for_community'
and then in the UsersController have
def show_users_for_community
#community = Community.find_by_name(params[:community_name])
<do what you need to do here>
end
I'm not sure if that route will match too many URLs or not -- it's a very general route. So if you do this, maybe put it low down in your routes file.
I understand how to create a vanity URL in Rails in order to translate
http://mysite.com/forum/1 into http://mysite.com/some-forum-name
But I'd like to take it a step further and get the following working (if it is possible at all):
Instead of:
http://mysite.com/forum/1/board/99/thread/321
I'd like in the first step to get to something like this: http://mysite.com/1/99/321
and ultimately have it like http://mysite.com/some-forum-name/some-board-name/this-is-the-thread-subject.
Is this possible?
To have this work "nicely" with the Rails URL helpers you have to override to_param in your model:
def to_param
permalink
end
Where permalink is generated by perhaps a before_save
before_save :set_permalink
def set_permalink
self.permalink = title.parameterize
end
The reason you create a permalink is because, eventually, maybe, potentially, you'll have a title that is not URL friendly. That is where parameterize comes in.
Now, as for finding those posts based on what permalink is you can either go the easy route or the hard route.
Easy route
Define to_param slightly differently:
def to_param
id.to_s + permalink
end
Continue using Forum.find(params[:id]) where params[:id] would be something such as 1-my-awesome-forum. Why does this still work? Well, Rails will call to_i on the argument passed to find, and calling to_i on that string will return simply 1.
Hard route
Leave to_param the same. Resort to using find_by_permalink in your controllers, using params[:id] which is passed in form the routes:
Model.find_by_permalink(params[:id])
Now for the fun part
Now you want to take the resource out of the URL. Well, it's a Sisyphean approach. Sure you could stop using the routing helpers Ruby on Rails provides such as map.resources and define them using map.connect but is it really worth that much gain? What "special super powers" does it grant you? None, I'm afraid.
But still if you wanted to do that, here's a great place to start from:
get ':forum_id/:board_id/:topic_id', :to => "topics#show", :as => "forum_board_topic"
Take a look at the Rails Routing from the Outside In guide.
maybe try something like
map.my_thread ':forum_id/:board_od/:thread_id.:format', :controller => 'threads', :action => 'show'
And then in your controller have
#forum = Forum.find(params[:forum_id])
#board = #forum.find(params[:board_id])
#thread = #board.find(params[:thread_id])
Notice that you can have that model_id be anything (the name in this case)
In your view, you can use
<%= link_to my_thread_path(#forum, #board, #thread) %>
I hope this helps
I have the following in my routes.rb
map.resources :novels do |novel|
novel.resources :chapters
end
With the above defined route, I can access the chapters by using xxxxx.com/novels/:id/chapters/:id.
But this is not what I want, the Chapter model has another field called number (which corresponds to chapter number). I want to access each chapter through an URL which is something like
xxxx.com/novels/:novel_id/chapters/:chapter_number. How can I accomplish this without explicitly defining a named route?
Right now I'm doing this by using the following named route defined ABOVE map.resources :novels
map.chapter_no 'novels/:novel_id/chapters/:chapter_no', :controller => 'chapters', :action => 'show'
Thanks.
:id can be almost anything you want. So, leave the routing config untouched and change your action from
class ChaptersControllers
def show
#chapter = Chapter.find(params[:id])
end
end
to (assuming the field you want to search for is called :chapter_no)
class ChaptersControllers
def show
#chapter = Chapter.find_by_chapter_no!(params[:id])
end
end
Also note:
I'm using the bang! finder version (find_by_chapter_no! instead of find_by_chapter_no) to simulate the default find behavior
The field you are searching should have a database index for better performances