I have a CourseCategory model and a Course Model along with corresponding controllers course_categories and courses. A course belongs to a category and a category has many courses.
I want to have a page that shows courses in a particular category. Which controller does this function belong in? Should I nest these resources? Just looking for the best way to model this.
CourseCategories. Think of your URLs, thats the easiest way to make a decision like this:
example.com/course_categories/1
--or, better, use slugs for categories instead of ids and you'd get--
example.com/course_categories/awesome_courses/
However, it might be worthwhile to consider simple having a "categories" model rather than a "course_categories" model. Then you could nest categories under courses, and you'd get something like:
example.com/courses/categories/1 or example.com/courses/categories/hard
I would say you are going to be displaying a coursecategory listing - therefore would belong in the coursecategory controller.
You can add this so the courses controller and take a parameter saying which category to respond with.
Or, if it makes sense, you can make courses a nested attributed of course_categories. Then this will be the default behavior.
EDIT Since I gave the opposite answer of someone else. The first suggestion would let you
/courses?course_category=math
or
/courses/math
Then look for the course_category in the controller
The second idea would let you do
/math/courses
An alternative would be to do something like
routes.rb
resources :course_catgeories do
resources :courses
end
courses_controller.rb
def index
#course_category = CourseCategory.find(params[:course_category_id])
#courses = #course_category.courses
end
The logic here being, if you're listing courses for a category, then the conventional action for that is the index action. Except in this case, you're scoping the listing to a particular category. So if you think about it in those terms, I think this approach better models your domain.
This approach effectively produces the following routes:
/course_categories/ (category index page)
/course_categories/1 (category show page)
/course_categories/1/courses (course index page)
/course_categories/1/courses/1 (course show page)
Related
I have a rails app where a user can both create products and purchase products through orders.
There is a field called usermode in my usertable that sets the user to either a store_owner or customer.
IF usermode = 'Store_Owner'
I want my products index to show the products that are created by the current_user if
OR
IF usermode = 'Customer'
I want my products index to show the products purchased by a user through orders>line items>products
I am new to rails and not sure whether I need to put this logic in the model, create separate controllers, or do some kind of if statement inside my existing product controller index. I think it should go in the controller index.
How can I achieve this?
According to my opinion,
I would put the same logic in same controller. In both case (Store_Owner or Customer) you are showing the same datatype (Products). The only change b/w these two case is that your data is different. ROR is about DRY. So I think there is no need to create new view template for this. However some believe that each action in controller must represent single functionality (to reduce the clutter in controller). That means less conditional statements in controller. So you have this two choices:
I think the first one is better approach
Create single view file. write a method in model like following
def self.product_data_of(user) # The self indicate class methods
# check with if else that user is Store_Owner or Customer
# return appropriate data
end
Call this method from controller like this(I have assumed the class name is Product)
#products = Product.product_data_of(current_user)
And you can use this #products variable in view file.
Create single view file write two different methods in controller but render the same view file. In this case no need to create new method in model. But still I would prefer the previous approach.
Edit
The general way is
1) View: The logic related to how we will show will be here
2) Model: The logic related to What data or changes on data will be here.
3) controller: The logic related to the flow like fetch data from model and decide which page to render/redirect
The solution was to do something like this in my product controller:
if current_user.usermode =="Store_Owner" then
#dashboards = current_user.dashboards.active.order(:title)
elsif current_user.usermode =="Customer" then
#dashboards = Dashboard.joins(line_items: :order).where('orders.user_id' => current_user.id).all.uniq
end
I have an project with many Books. Each book has many Pages. I would like each page to have a pageIndex that makes it unique in relation to the book, so that each time I build a page through the book this index is incremented. Obviously all models have their own id, but that id is unique across all Pages. I would like the first Page built by a Book to have a a pageIndex of 0, the second to have a pageIndex of 1, the third; 2 etc.
I could do something like this:
book = User.books.last
pageIndex = book.pages.count
book.pages.create(:pageIndex => pageIndex)
But it seems to me that it would be much nicer if this index was set automatically by the book when it created a dependant resource of type Page. Is there a way of doing this?
Please note that I have seen this question but what I am looking for is a hook in the parent/owning Resource that allows me to do something to the dependant resource it has created after it has created it and before it saves it. Something like the before_save callback but related to creation of a dependent resource: after_dependent_create.
Associations also have some callbacks. See here http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html "Association callbacks". Possible callbacks are: before_add, after_add, before_remove and after_remove.
Also I would add a counter_cache on the book so you'll know how many pages you have and when creating a new page you won't have to count the existing pages
In my model I have relationships like this:
[Person]
PersonId (PK)
...
[Orders]
OrderId (PK)
PersonId (FK to Person.PersonId)
...
A person can have multiple orders.
I generated PersonController with Create/Details/List/Edit views. I want to be able to manipulate Orders also, but within the context of a Person. In other words, I'd like the workflow to be
User browses a list of people (/Persons)
User selects 'View Orders' link next to a specific Person (/Persons/4/Orders/)
User sees a list of orders with Create/Details/Edit as well (/Persons/4/Orders/Edit/38)
Is this the right way to set up my controllers/routes?
Should I just access orders at routes like (/Orders/Edit/38) instead?
Right now I have:
PersonController
OrderController
Should I create a PersonOrderController or can I achieve what I want using only the two controllers I already have?
You should probably use just the two Controllers you already have. It is a judgment call however. Two different excellent MVC designers might do this two different ways. Unless you already see a lot of other work for your OrderController, you should start with two controllers. If OrderController becomes bloated, you can refactor.
My url http://server/region/section/item
Now if someone goes to http://server/us/ I want to display a page to choose a section
if someone goes to http://server/us/beer/ I want to display a list of all beers.
My question is should I just have 1 route with defaults and then return different views depending on how much the URL is filled in or should I create multiple routes going to the same controller different actions? or even different controllers, I just want to know what the best practice is.
The typical route looks like this:
http://www.domain.com/controller/action/id
[domain/controller/action/id]
In your case, it's short one part:
http://server/us/beer
[domain/controller/action?/?]
As Robert Harvey said, you wouldn't want to have an action for every product. So how about something like:
http://server/us/product/beer
[domain/controller/action/id]
domain = server
controller = us (tho, this doesn't seem like it would be a good name for the controller)
action = product
id = beer
Then you'd develop a product view that would show the beer data to your visitors.
This isn't architect-ed very well, but without knowing your situation it would be difficult for anyone to answer this. I hope this helps though.
In your particular case, "beer" can probably be a filter to a controller action, rather than another controller action, in which case you need only one route entry.
It doesn't seem wise to create a strategy that will require you to add new routes, controller methods and/or views every time you add a product category. Unless, of course, you want to highly customize each category's look and behavior.
Imagine a blog engine in ASP.NET MVC where I want strongly-typed Views. I have a Model for BlogPost and a Model for Comments. A typical page would display a blog post and all of that post's comments. What object should I pass to the View()? Should I create a Presentation Model? Maybe I'm using that term incorrectly, but by that I mean should I create a composite model just for a View(), something like BlogAndComments? Should my controller create an instance of BlogAndComments and pas that?
Or, should I somehow pass both a BlogPost and Comments object to the View?
I think you're on the right track with your understanding of Presentation Models. As to when you should create a View Model, the answer is probably 'it depends'. In your example, you can probably get away with passing the BlogPost and Comments in the ViewData object. It's not gorgeous, but hey, it gets the job done.
When and if that starts to feel ugly or unwieldy, then I would start thinking about making a View Model. I usually end up with the notion of some sort of 'Page', which includes the page title, common data, and then specific stuff for a particular page. In your case, that might end up as a BlogViewPage, which includes Title, BlogPost and List comments.
The nice thing about that approach is that you can then test that controller by making a request and testing the BlogViewPage to ensure that it contains the expected data.
In my opinion comments belong to the view as much as the post itself.
Make a BL class for your comments like:
class CommentBO
{
Guid UserID;
string Text;
}
Then you have a BO for your post.
class PostBO
{
Guid UserID;
List<CommentBO> Comments;
}
Then your model can be really simple.
class BlogModel
{
string AuthorName;
string BlogTitle;
List<PostBO> Posts;
}
Pass it to the view and render it.
You may be tempted to omit all BO and just fill the model directly from the database. It is an option, but not exactly the right one. A model is just a package of things for a view to display. These things however should be prepared somewhere else, namely at the business logics level with just a nominal participation of the controller.
I always use strongly typed views and create a presentation model class for each view or view user control. The advantage is that by looking at the presentation model class alone I know exactly what are the values that the view uses. If I were passing domain models then that would not be obvious because domain models may contain many properties that the view does not use. I would have to look at the view's markup to figure out what values it needs. Plus the presentation model usually adds certain properties that are not available in the domain model. It seems a bit tedious but I think it saves time and makes the code better in the long run.
In my opinion if comments belong to a blog post why not create a collection of comments on the blog post model? That makes perfect sense from a domain modeling stand-point and chances are whatever ORM you are using would support lazy-loading that collection which simplifies your controller logic as well.