Reputation: 127
Im building an API in which i have a model called Studio. Studio has many movies and many series, and movies and series have many characters of their own. Also, a character can be in many movies and/or many series, and belongs to a studio. I got all the models relational tables logistics figured out by now, but what i'm trying to do right now, i can't think of a way.
So my routes are designed so i can do something like localhost:3000/api/v1/studios/1/characters -> this way i display all the studio's (studio_id: 1) characters (from all the movies and series that belong to that studio). That is fairly easy with just doing something like:
@characters = Character.where(studio_id:@studio.id)
In my CharactersController, where i have
def get_studio
@studio = Studio.find(params[:studio_id])
end
Now i am trying to design the logistics so i can do localhost:3000/api/v1/studios/1/seriees/1/characters
And only get the characters from that specific seriees. localhost:3000/api/v1/studios/1/movies/1/characters
should also be valid.
I tried
def get_movie
@movie = Movie.find(params[:movie_id])
end`
and
def get_serie
@serie = Seriee.find(params[:seriee_id])
end
in my CharactersController, with before_action :get_serie
and before_action :get_movie
at the very start of the code, but it gives me an error because i'm never getting both a movie_id and a seriee_id, it's either one or the other, so it can't search for both at the same time.
The POST from character i will only do from localhost:3000/studios/1/characters
so that's not a problem, i only want to be able to GET from localhost:3000/studios/1/movies/1/characters
and localhost:3000/studios/1/seriees/1/characters
I hope i didn't ramble to much and you are able to understand me. If you think that my way of solving this problem is just not right, please tell me as i am just learning and any advice would be very valuable.
Upvotes: 0
Views: 408
Reputation: 102240
I doubt you actually want to nest those resources more then one level deep:
Rule of thumb: resources should never be nested more than 1 level deep. A collection may need to be scoped by its parent, but a specific member can always be accessed directly by an id, and shouldn’t need scoping (unless the id is not unique, for some reason).
- Jamis Buck
Given the domain here it does not make sense that you would have to specify the studio to get the characters for a given movie. The goal of nesting in REST is to make the relationships between two entities explicit by making them part of the URL structure - not to be overly explicit.
Excessive nesting leads to some very overcomplicated code when it comes to stuff like url generation - I mean do you really want to write?
url_for [:ap1, :v1, studio, movie, :characters]
You can fix these issues by using the shallow: true
option:
resources :studios do
resources :movies, shallow: :true do
resources :characters
end
end
or by avoiding "russian doll" calls to resources
:
resources :studios do
resources :movies, only: [:new, :create]
end
resources :movies, only: [:show, :update, :destroy] do
resources :characters, only: [:create, :index]
end
You also really should be using assocations to fetch the nested items.
class CharactersController
before_action :set_movie, only: [:index, :create]
# GET /api/v1/movies/1/characters.json
def index
@characters = @movie.characters
end
private
def set_movie
@movie = Movie.find(params[:movie_id])
end
end
In general code like Character.where(studio_id: @studio.id)
should be avoided as it leaks the implementation details of the relationship. Your controller should not need to know how characters and studios are related - it just knows that movie
has a method named characters
.
And I don't really want to get into the details of your modeling here but Character.where(studio_id: @studio.id)
will not work with the requirement that movies most likely contains a subset of the characters of a studio - not their entire cinematic universe. Instead you need a join table that links movies and characters.
Upvotes: 1