Find resource by slug in nested resource in rails - ruby-on-rails

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

Related

Rails singular resource, where does the ID come from?

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

Static pages to ruby on rails [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I'm new to Ruby on rails, and in a month i will start a course on Ruby-on-Rails, but i would like to get some code going before i start the course since i want to learn as much as i can.
I made a project with:
rails new portfolio
Then i did:
rails generate controller portfolio index
To get the front page going.
Rails.application.routes.draw do
get 'portfolio/index'
resources :company
root 'portfolio#index'
end
then:
rails generate controller company
controller:
class CompanyController < ApplicationController
def new
end
end
Then i made a file under views/portfolio/ called index.html.erb where my front page will be.
Under views/company/ i will have a file called company.
When i now go to the url: localhost:3000/company/company i get the error:
The action 'show' could not be found for CompanyController
My CompanyController is this:
class CompanyController < ApplicationController
def new
end
end
Anyone that knows alot about ruby that can just give me a little pointer in the right direction?
Thanks.
Start with a single model and controller for companies. Create a index method inside the app/controllers/companies_controller. Then create the content inside file app/views/companies/index.html.erb to check that everything works, for example:
<h1> Hi! This is root page and index method in CompaniesController! </h1>
In config/routes.rb, you must specify a plural name for companies if you plan to create and process more than one, and leave it as it is, if the entity is the only one company for this project. Set plural name for this resource for this moment to create standard routes for CRUD:
resources :companies
root to: "companies#index"
More about routes you can find in rails guide.
You can try using the built-in scaffold generator in order to quickly generate the application skeleton:
rails generate scaffold companies
The command above will generate controller, model, views and routes with CRUD methods in controller and views for the controller methods. Each view in app/views/"resource_name_plural" adjusted with method in controller in config/routes.rb file. This is how the MVC pattern works.
If you want to create static pages, maybe you should look at the high_voltage gem.
In Rails you need to pay careful attention to pluralization. When declaring routes for a resource it should always be the plural form unless its the rare case where the resource really is singular (there can be only one).
Rails.application.routes.draw do
resources :companies
end
This will route to all companies at /companies and a single company at /companies/:id. If you thus try to get /companies/company it will be routed to the #show action since /company will be interpreted as the id.
Controllers should also be named in plural:
# app/controllers/companies_controller.rb
class CompaniesController < ApplicationController
before_action :set_company, only: [:show, :edit, :update, :destroy]
# GET /companies
def index
#companies = Company.all
end
# GET /companies/:id
def show
end
# ...
private
def set_company
#company = Company.find(params[:id])
end
end
You can use the scaffold command to get a full example of a standard rails CRUD controller:
rails g scaffold companies
If you have the create method, or new, you have to include the show method and index method.
in your routes
resources :companies
resources :portfolios
try in your controller:
class CompanyController < ApplicationController
def new
end
def new
end
def index
#companies = Company.all
end
def show
end
end
and if you have the controller you can create the views and point without problem
localhost:3000/company/company
This, I think it should be something more like this.
localhost:3000/companies/
always plural.
Your controller will look for the view defined in the method. look how it is by default, that of a project of mine. in your place of groups will be companies.
look at the name of the views, are the same names of the methods of your controller. right?

Restrict devise users to their own view/association

I am using devise for authentication and have an association between users (has_many :products) and products model (belongs_to :user).
My routes file is
resources :users do
resources :products
end
Now what happens is, user with id 3 at /users/3/products can also see whats at /users/4/products. I want to restrict that. I dont want /users/3/products to able to see whats at /users/4/products and so on (not specific to these two users but for all). How do I do it ? Should I have a Users Controller? I dont have it right now. If i have the controller, how do I do it? I was thinking maybe redirect it?
thanks
You could add a before_filter in your products controller:
class ProductsController < ApplicationController
before_filter :user_is_current_user
...
private
def user_is_current_user
unless current_user.id == params[:user_id]
flash[:notice] = "You may only view your own products."
redirect_to root_path
end
end
end
Also, in the products controller you could retrieve only products belonging to the current_user:
def index
#products = current_user.products # will fetch all products with a user_id matching the current user's
end
If you used the above you wouldn't really need a user's ID in the URL, you could use a path like /users/products or just /products.

Rails 3.1 - How do I organize multiple index actions for the same model?

I have a User model that has_many projects. The Project model belongs_to the User model. I am currently using the projects_controller.rb index action to display all of the projects that have been created across all users (Project.all).
On a separate page, I would also like a way to display all of the projects that belong to a specific user (i.e. go to page and be able to see all of the projects that belong to a given user).
I am having difficulty figuring out which controller/action/view to use and how to set up the routes because I am already used the index action for the projects_controller for the purpose of displaying all of the projects. Does anybody have any suggestions?
You could do /users/{:id}/projects, which would map to the users controller projects action. The route would have to be custom member action
resources :users do
member do
get 'projects'
end
end
rather than having different pages of listing. use same index page based on different criterias i.e. filters.
URL
match ":filter/projects", :to => "projects#index"
inside controller something like
case params[:filter]
when "all"
#projects = Project.all
when "user"
#projects = current_user.projects
when "batch"
# ..
else
# ..
end
How about you display the projects that belong to a particular user on the User#show page?
Something like this perhaps:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
# rest of class omitted...
Then you can access the projects that belong to the user in the view for the show page by calling #user.projects.
You should nest projects under users to get working code/paths like: /users/1/projects without any additional coding, so you need to change your resource lines in routes.rb to:
resources :users, :shallow => true do
resources :projects
end
and then under Projects#show action instead of Project.find(params[:id]) you need to get Project.find(params[:user_id])
That's seems to be correct

Rails: how can I nest only associated resources?

To set up nested resources in Rails, I have seen example routes given like this:
map.resources :players
map.resources :teams, :has_many => :players
By doing this, you can visit teams/1/players and see a list. But it lists all players, not just those that belong to team 1.
How can I list only the resources that are associated with the parent resource?
You need to load the team first. A common practice is to do this in a before filter.
class PlayersController < ActionController::Base
before_filter :get_team
def get_team
#team = Team.find(params[:team_id])
end
def index
#players = #team.players # add pagination, etc., if necessary
end
def show
#player = #team.players.find(params[:id])
end
end
Note that the code above insists that you specify a team. If you want the same controller to work for both, you need to change it slightly (i.e. check for params[:team_id]).
You can use the excellent inherited_resources gem to DRY this up if you controller logic is straightforward.
The problem has little to do with map.resources and routing in general.
Note, players are not fetched magically by the framework: there's some action in some controller processing teams/1/players request and your code there fetches list of players to show. Examining that action (or posting here) should help.

Resources