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
Related
I have two different controllers with their own feed(index) actions and I want them to be displayed on a single page (home). The posts and requests models are not similar and hold different types of data (images, video links, strings, etc.).
In my posts controller I have this action. It uses two API actions in the same controller and fills a #post_array with specified record information which then gets displayed in the posts/feed.html.erb view:
def feed
if user_signed_in?
post_feed_authenticated
else
post_feed_not_authenticated
end
end
In my requests controller I have a similar action that returns a #request_array with all relevant request records and displays them in requests/feed.html.erb.
def feed
request_feed
end
The methods the actions call filter all records by location, authentication, and IDs.
I've already created separate views for these actions that work correctly, but I want to combine them so both feeds are shown on the same page (home.html.erb). How do I go about doing this? I've read into using partials but I am not sure if that is applicable here since the data is different.
Edit:
The home controller is:
def home_page
end
The home_page.html.erb currently has buttons to the corresponding post/request feed. I want to put both feeds on the home_page.
As I understand it - you want to reuse both the views and the code that populates #post_array and #request_array in your home view. I see these as two different concerns with different solutions:
For reusing the views you can use partials. By having partials for the posts feed and the request feed you could reuse the view in the home view.
And for reusing the way you populate the #post_array you could try and place that logic in somewhere where your HomeController has access to, ideally your Post model (i.e. Post.feed(current_user, location)) or a service object. Another, hacky way to do this is by placing these methods in the ApplicationController, making them available in all controllers.
You can create instances of each class in the index action of the home controller:
#posts = Post.all
#requests = Requests.all
Then you have access to all variables and methods in each class.
(Change index from above to whatever you named your home page.)
In my rails model, i define a class and a method to return me an array of Project names. Now my question is, should I, is it correct to use the Project.names array in my view, or should I generate an array in the controller, and pass with an instance variable.
class Project < ActiveRecord::Base
...snip...
def self.names
Project.select(:name).map {|x| x.name }
end
end
I'll definitely stick to fill the instance variable in the controller. BTW, a word about your code:
Project.select(:name).map {|x| x.name }
Can be refactored to:
Project.select(:name).map(&:name)
An this, can be refactored to:
Project.pluck(:name)
Love Ruby.
From practical perspective, it is always a hard decision where to draw the line between view layer and controller logic. It could be questionable to fill your controller with the likes of:
# controller
#project_names = Project.names
# view
<%= #project_names.join(", ") %>
Only to use the #procject_names in the view.
But keeping that code out from views would give you later the opportunity to do this without changing the view:
# Controller is updated
#project_names = Project.names
# Show only matching projects
if params[:search]
#project_names = #project_names.select{|n| n =~ /#{params[:search]}/}
end
# view - still the same
<%= #project_names.join(", ") %>
Also, take look at the Draper gem which builds upon the
Decorator pattern and ViewModel of the MVVM pattern.
With Draper, you can keep even your controllers cleaner and have multiple decorators for same object depending on the need (e.g one for web, other for mailer) while still using same view code to render output.
Common thing to place in the Decorator is localized dates which depend on logged in user, therefore don't fit into view, but would clutter the controller with view-layer logic.
It's generally considered best practice to separate the pulling of data and the display of data appropriately, where the view is responsible for only displaying that data. I'd definitely say it would be a better idea to generate the array in your controller and pass it in to your view.
MVC is a quite a large topic - for more information check out the Rails Guides: http://guides.rubyonrails.org/getting_started.html
I am trying to come up with a clean design for this -
I am using MVC to process orders, so I have an 'order' entity, with its own controller and views.
From the Create Order view I would like the user to add a 'Customer' entity. I have a controller and CRUD operations for 'customer'.
When someone creates a new Order I would like them to either
1) enter a customer name to see if that customer already exists, and if so, add that Customer to the Order, or
2) Create a new Customer then add that new Customer to the Order.
My problem is I am not sure of a good way to access the Customers from within the Order.
-do I create a partial view for Create Customer, then use that view in the Customer Create AND Order Create?
-then would I create a partial view 'SearchCustomers' that passes params to an action on Customer controller and that returns results? Would I be able to reuse this across the site?
You can see I am not sure about a few things - are partial views the way to reuse things? can partial views be reused across controllers and access different controllers from the ones theyre in?
I have gone through an MVC book and online tutorials but they all seem to use beginner examples, where Model objects don't contain other Model objects.
Thanks for help
Views never "access controllers".
The flow is:
Controller Action Method -> Creates View Model -> Hands it off to a View -> View displays it -> View can pass sub-models on to other partial views
Decomposing everything into smaller models and partial views (or using Editor templates) is a good approach. You can then assemble those smaller models into a larger view model for a complete page, or you can use the ViewBag to assemble them in a non-strongly typed manner.
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)
I want to create a form for creating an order. So I have an 'Order' controller. One of the first steps will be to select an existing person to send this order to.
I'm thinking the Create() action method on OrderController will put a new Order object in session. Then I'll need a link that will redirect to another controller, then return a customerID int to this Create() method.
I will have either a SearchController with a FindCustomer() action method, or a Search() action method on the CustomerController.
I have tried the first way. But what I am doing looks pretty messy. Especially considering that I'll need to do this at least one more time on this form, redirecting to the ItemsController to return items to add to the order.
What's the best way to design communication like this between controllers?
I'm not sure why you think you need other controllers for this. In your GET Create action, put the available Customers and Items in to ViewData, and then in your view put some controls for the user to select values.
Then they will be POSTed to your POST Create action, and you can bind & save it in your Order object. You could have a separate action for adding Items to the Order if that gets too complex. But it could still be on the same OrdersController.