I have a Rails 6 app and as resource hotel.
My user model has a belongs_to hotel (hotel_id) column.
No I want to create a singular resource where a user can access its hotel directly as mentioned here:
https://guides.rubyonrails.org/routing.html#singular-resources
So I did
resource :hotel which maps to hotels#show
The idea is that I don't need to have a link hotel/ID and instead just have /hotel
But now, how does my hotels controller know which hotel to load? I assumed that it would take it from the user table but instead I get
undefined method `name' for nil:NilClass
which means that there is no #hotel in my view.
What am I doing wrong here?
You need nested routes... so in config/routes.rb you need:
resources :users do
resource :hotel
end
Then examine the route set (e.g. from the rails console do rails routes) and you'll see a set of routes that include something like this:
user_hotel GET /users/:id/hotel hotels/show
do you see the :id parameter buried in the url?
Then in the hotels controller show method, the user's id is found in the params hash. So you can find the hotel like this:
class HotelsController < ApplicationController
def show
user = User.find(user_params(:id))
#hotel = user.hotel
end
private
def user_params
params.require(:user).permit(:id)
end
end
Related
I have following routes
resources :shops do
resources :categories
end
And when I visit this url:
http://localhost:3000/shops/rabin-shop/categories
I first want to find the shop by using slug 'rabin-shop', then I can filter the categories of products that belong to that shop. In my controller I have tried to implement
def find_shop
#shop = Shop.find(params[:slug])
end
But this is not working. I know this is not how to find in nested resource. I am using friendly_id gem . I cannot do something like current_user.shop because I want the page to be accessed even when the user is not logged in.
If you are using the freindly_id gem, this is how you find a record by it's slug :
def find_shop
# params[:shop_id] if nested
#shop = Shop.friendly.find(params[:id])
end
I have a Users table which also has a manager's id to implement a self-join. when I login as a a manager and click on "My subordinates", I should see my subordinates. The subordinates are also from the User table.
So my question is
What should I say here <%= link_to "My Subordinates", ????_path %>(I mean like user_path.).
How should the model and controller logic be?
I would do something like #ryanfelton said, but instead of overwriting the index method, i would create a new one specifically for the subordinates.
class Manager::UsersController < ApplicationController
before_action :ensure_manager! #this one check the manager_id or any other condition to be manager
def sobordinates
#subordinates = #user.subordinates
end
end
#routes.rb
namespace :manager do
resources :users do
collection do
get :subordinates
end
end
end
This way you can maintain the index of users and you have a method only for the subordinates.
Be aware that you need to create a subordinates.html.erb inside the users folder >
app/views/manager/users/subordinates.html.erb
EDIT:
You where asking for the model and the link also so, here it goes:
The link: after editing the routes.rb, go to the console and use rake routes
and search for the subordinates link. Add the _path or _url depending on the use you are whiling for that path.
The model, I strongly recommend you to read the official documentation about relations: http://guides.rubyonrails.org/association_basics.html. That would help you more than having the answer for copying and pasting :)
I would recommend namspacing a users_controller.rb.
So it would be in the folder app/controllers/manager/users_controller.rb
class UsersController < ApplicationController
before_action :ensure_manager!
def index
#manager.users
end
end
In the routes.rb you would have this route:
namespace :manager do
resources :users
end
So ultimately your path would be manager_users_path
I am having some difficulties passing in parameters to my controller. I created an Single table inheritance model in my model file.
class Account < ActiveRecord::Base
belongs_to :user
end
class AdvertiserAccount < Account
end
class PublisherAccount < Account
end
I setted up my routes table with nested resources
resources :advertiser_accounts do
resources :campaigns
end
I want to be able to pass the current account_id (an account_id from one of my two subclasses of account) to my campaign controller file.
A URL that I would use is http://127.0.0.1:3000/advertiser_accounts/1/campaigns
Since my resource for the url is advertiser_accounts and not accounts, I am not able to get the parameter :account_id.
class CampaignsController < ApplicationController
def index
#account = current_user.accounts.find_by_id(params[:account_id])
end
end
is there a shortcut to get the current resource or the id? Am I passing in parameters correctly? It seems confusing to call many find_by_id in the controller. Any help is appreciated.
Edit Possible solution:
One of the solutions that I was thinking was setting a type in my routes and then in my controller I would use case statement then get params[:advertiser_account_id] but that seems very tedious and messy. Especially if I will need to copy and paste a list of case statements in each action.
routes.rb
resources :advertiser_accounts, :type => "AdvertiserAccounts" do
resources :campaigns
end
campaigns_controller.rb
def index
case params[:type]
when "AdvertiserAccounts"
#account = current_user.accounts.find_by_id(params[:advertiser_account_id])
when "PublisherAccounts"
#account = current_user.accounts.find_by_id(params[:publisher_account_id])
end
end
Try this out:
resources :advertiser_accounts, :as => "account" do
resources :campaigns
end
that should give you
/advertiser_accounts/:account_id/campaigns/:id(.:format)
You can try with "becomes" method in your controller.
In your private method where you're looking for the account_id you would have:
#account = Account.find(params[:account_id]).becomes Account
So currently I have something like /users/1/ when I want to view a user profile. How can I go through routes.rb to change that to /user/chiggins/ where chiggins is a unique username?
You need is to override to_param method in User model:
class User
def to_param
username
end
end
Then rails will use it automagically for routing. See http://api.rubyonrails.org/classes/ActiveRecord/Base.html#method-i-to_param
Another possibility to consider would be the friendly_id gem - https://github.com/norman/friendly_id
Nowadays there is a :param argument on the resource declaration.
http://guides.rubyonrails.org/routing.html#overriding-named-route-parameters
You can get per-resource identifier customization by redefining the member_scope and nested_scope methods on the Resource instance.
resources :users do
#scope[:scope_level_resource].tap do |u|
def u.member_scope
"#{path}/:username"
end
def u.nested_scope
"#{path}/:#{singular}_username"
# member_scope also usable here, assuming username will be nowhere in nested routes.
end
end
end
Regarding the question about #nested_scope below: It gets used when you do something like this in routing:
resources :members do
resources :playlists, only: :index
end
Then, the param would be :member_username instead of just :username. This is useful in the playlists controller when assembling the collection so you can infer the scope of the request.
The best way is to define a route with a custom param:
match "/users/:username" => "users#show"
In your controller, the plain old params[:id] will be params[:username], and you can get the user from de DB using:
User.find_by_username(params[:username])
In my "routes.rb" file I have the following line:
resource :users
which gives me a bunch of named routes for accessing my User model in a RESTful manner.
Now, I've made some additions to the User model including creating a special class of user. These are still stored in the User model but there is a "special" flag in the database that identifies them as special.
So, is it possible to create special_users resource? For example, I'd like to have a "special_users_path" as a named route to "/special_users" which will return an index of only the special users when you perform a GET on the URL.
Is there a way to do this?
In Rails routing, a 'resource' refers to the standard 7 routes that are created for RESTful resources: index, show, new, create, edit, update and destroy. Normally that is enough, but sometimes you might want to create another action.
In the model, you want to create a scope that only returns special users:
class User < ActiveRecord::Base
scope :special, where(:special => true)
end
On the controller side, there are two ways to go about this. What you are suggesting is the creation of an additional action:
match "/users/special" => "users#special"
resource :users
In the controller, your special action would return the scope you just created:
class UsersController < ApplicationController
def special
#users = User.special
end
end
That will do what you ask, but I would suggest NOT doing it this way. What if you add other flags later that you want to search by? What if you want to search by multiple flags? This solution isn't flexible enough for that. Instead, keep the routes the way they are:
resource :users
and just add an additional line to your controller:
class UsersController < ApplicationController
def index
#users = User.all
#users = #users.special if params[:special]
end
end
and now, when you want to display special users, simply direct the user to /users?special=true
This approach is much more future-proof, IMO.
(This answer is assuming Rails-3. If you're still using 2.3 let me know)
You could set the special_users as a resource:
resource :special_users
If you need to point it to a special controller, you could specify it with:
resource :special_users, :controller => :users
But I would really suggest you to not creating another controller for retrieving a kind of user, but using a param to get them:
class UsersController < ApplicationController
def index
users = case params[:type].to_s
when "special"
User.special_users # Using named scopes
else
User.all
end
end
end
When you use the users_path to call the special users:
users_path(:type => :special)