Make pretty urls in rails. Change /users/ to a database field - ruby-on-rails

Right now, my urls are being rendered as
/users/2/products
I know i can use friendly_id to have it render as
/users/username/products
But I dont want that. In my database User, there is a field role, which has entries like - worker, janitor and so on. So, I want to have
/users/2/products
as
/worker/username/products or /janitor/username/products and so on..
How do i do it ? Can i use friendly_id to do it ?

I don't know friendly_id. I can't even be sure of what you are asking, but something tells me you want to define a custom route.
Adding a custom route
in your route file, you can add
match "/worker/:username/products" => "users#show"
and in the action, check for :username parameter variable.
if params[:username]
#user = User.find_by_name(params[:username])
else
#user = User.find(params[:id])
end
Routes for your specific case
In your case, it seems that you want this in your route.rb:
match "/:role/:username/products" => "users#show"
and this in your action to use the params:
User.where("role = '?' and username = '?', pararms[:role], params[:username])
The route will make accessing a URL like /worker/fotanus/products/ end up in the action User#show with params[:role] = 'worker' and params[:username] = 'fotanus'.

Related

Getting param from friendly url like http://localhost:3000/user/adem-balka with Rails 5

So I want to display posts for an user in his/her profile page as at top user details, below all the posts.
I know I can get param from a url like http://localhost:3000/posts?category=article
with
if params[:category]
#category_id = Category.find_by(title: params[:category]).id
#posts = Post.where(category_id: #category_id)
end
but param doesn't work when I have an url like http://localhost:3000/user/adem-balka
So, how can I get user name to find its id and pull posts with that user id?
Thank you all.
The name of a parameter in a url is set in your routes file.
If you look at your routes in config/routes.rb, you should be able to find the line(s) that corresponds to the user model. It should look something like this:
get '/users/:name', to: 'users#show'
This means that if you go to /users/adem-balka, params[:name] will be set to 'adem-balka'. You can then access the parameter in the corresponding controller function.
What you are looking for is a path parameter, where adem-balka is say params[:username].
Assuming you have no forward slashes or dots in your parameter, this is as simple as adding /:username as part of your route, e.g.
get '/users/:username', to: 'users#show'
# in the controller
#user = User.find_by(username: params[:username])
This is all covered in the Rails Routing from the Outside In guide.
Note that the routes generated resources already contain the :id path parameter for you (for show, edit, etc.). But even if you change the controller, the generated helpers (e.g. users_path(#user)) will use the id.
To make it work with resources using say username instead of id however (e.g. users_path(#user) giving /users/ben instead of /users/5), you need to also override the to_param method, e.g.
class User < ApplicationRecord
def to_param
username #rather than id
end
end
# routes.rb
get '/users/:username' => 'users#posts'
# users_controller.rb
def posts
username = params[:username]
# etc..
end
This is described in the Rails Docs as Routing Parameters.
thank you for the answers. I learned from them 🙏
and this solved my problem
#user_id = User.friendly.find(params[:id])
#posts = Post.where(user_id: #user_id)

Add new route example.com/username while preserving existing routes

Is there a way to have example.com/username while preserving example.com/users/1 route?
To put simply, I just need to link to users's profile via username.
You can add an arbitrary route like this using:
get "/:username" => "users#show", as: :username
This will pass the username as a parameter to the show action.
Then in your views:
<%= link_to user.name, username_path(username: user.username) %>
You'll also need a controller action that knows how to handle the username parameter. If you want to use your existing users controller and preserve your /users/:id URLs too, you could do something like:
def show
#user = if params[:username].present?
User.find_by_username(params[:username])
else
User.find(params[:id])
end
end

Rails 3 routes double check on match

currently, I have this on my routes.rb
match '/:username' => 'profiles#show', :as => :public_profile
now I want to use this format, and add another parameter to make another match for another route.
I don't know how to explain this but let me give you an example.
Suppose with that match above:
http://example.com/foo_username
this connects to the profile of foo_username.
Now I want to have a url like this:
http://example.com/foo_username/5-my-story
This link should go to the stories#show action with 5-my-story as its id (slug), and this story is owned by the username provided in the path. How do I configure that in routes?
I tried this one:
match '/:username/:id' => 'stories#show', :as => public_story
It works, but when I change the parameter of the username, it still goes to the story. It should return a route not found because the username does not own the story.
How do I best implement this?
In your controller, you'll have something like this:
#story = Story.find(params[:id])
You need to restrict this to the user's stories maybe like this
#user = User.find_by_username( params[:username] )
#story = #user.stories.find(params[:id])
You then have to check if #story is nil, and return a 404 if not.
Adapt this to suit your needs:
# stories_controller.rb
def show
#story = Story.find(params[:id])
raise ActiveRecord::RecordNotFound unless #story.user.name == params[:username]
# ...
end

Rails: Route to custom controller action

I have a really hard time understanding routes and I hope someone can help me.
Here's my custom controller
class SettingsController < ApplicationController
before_filter :authenticate_user!
def edit
#user = current_user
end
def update
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to root_path
else
render "edit"
end
end
end
and I have the file settings/edit.html.erb and my link
<li><%= link_to('Settings', edit_settings_path) %></li>
The route
get "settings/edit"
doesn't work for this, because then I get
undefined local variable or method `edit_settings_path' for #<#<Class:0x00000001814ad8>:0x00000002b40a80>
What route do I have to give this? I can't figure it out. If I put "/settings/edit" instead of a path it messes up as soon as I'm on a other resource page because the resource name is put BEFORE settings/edit
Thx
Following should do:
get 'settings/edit' => 'settings#edit', :as => :edit_settings
# you can change put to post as you see fit
put 'settings/edit' => 'settings#update'
If you use /settings/edit directly in link, you shouldn't have problem with other resource name being prepended in path. However, without the leading slash, i.e. settings/edit it might have that issue.
Reason why edit_settings_path is not working might be because you didn't declare a named route. You have to use :as option to define by which method you will be generating this path/url.
If you want to explicitly define the route, you would use something like
get 'settings/edit' => 'settings#edit', :as => edit_settings
This statement defines that when a GET request is received for setting/edit, call the SettingsController#edit method, and that views can reference this link using 'edit_settings_path'.
Take some time to read the Rails guide on routing. It explains routing better than any other reference out there.
Also keep in mind the rake routes task, that lists the details of all the routes defined in your application.

Rails route dependent on current user

I'd like to create a rails route for editing a user's profile.
Instead of having to use /users/:id/edit, I'd like to have a url like /edit_profile
Is it possible to create a dynamic route that turns /edit_profile into /users/{user's id}/edit, or should I do thing in a controller or?
You might want to create a separate controller for this task but you could also continue using users_controller and just check whether there is a params[:id] set:
def edit
if params[:id]
#user = User.find(params[:id])
else
#user = current_user
end
end
But you should note that /users normally routes to the index action and not show if you still have the map.resources :users route. But you could set up a differently called singular route for that:
map.resources :users
map.resource :profile, :controller => "users"
This way /users would list all the users, /users/:id would show any user and /profile would show the show the currently logged in users page. To edit you own profile you would call '/profile/edit'.
Since a route and controller serve two different purposes, you will need both.
For the controller, assuming you're storing the user id in a session, you could just have your edit method do something like:
def edit
#user = User.find(session[:user_id])
end
Then have a route that looks something like:
map.edit_profile "edit_profile", :controller => "users", :action => "edit"
This route would give you a named route called edit_profile_path
Tomas Markauskas's answer could work, but here's the answer to your question from the Rails Guide:
get 'edit_profile', to: 'users#edit'
So, when someone goes to www.yoursite.com/edit_profile, it will route to www.yoursite.com/users/edit.
Then, in your controller you can access the user with
#user = User.find(session[:current_user_id])
Assuming you set that session variable when someone logs in. Also, don't forget to check if they're logged in. This will work if your using Resourceful Routing (the Rails default) or not.
Source: http://guides.rubyonrails.org/routing.html
make the route as
get '/users/:id/edit', to: 'users#edit', as: 'edit_profile'
As explained in this link section 'The hard way' :
http://augustl.com/blog/2009/styling_rails_urls/
The url will be
/users/edit_profile
Because the ID is no longer in the URL, we have to change the code a bit.
class User < ActiveRecord::Base
before_create :create_slug
def to_param
slug
end
def create_slug
self.slug = self.title.parameterize
end
end
When a user is created, the URL friendly version of the title is stored in the database, in the slug column.
For better understanding read the link below
http://blog.teamtreehouse.com/creating-vanity-urls-in-rails
write it in any home controler.
def set_roots
if current_user
redirect_to dashboard_home_index_path
else
redirect_to home_index_path
end
end
in routes.rb file
root :to => 'home#set_roots'
match "/find_roots" => "home#set_roots"

Resources