views:

417

answers:

3

I've read a lot of trivial MVC examples of calculators and thermometers but I can't seem to map the pattern to real world applications.

Suppose you have a more complicated scenario. Say you have a website shopping cart which requires users to login before adding to the cart. First, the user sees the product page (/product/detail) and clicks on add an item (/cart/add/207366). The user is not logged-in yet so they need to visit the login page (/user/login) and then, being smart about the flow, takes them to the shopping cart view (/cart/list). From there, they can link back to the original product detail page to continue shopping.

Let's say we have 3 database tables: users, usercart, and products. What is/are the model(s) in this situation? Would this entire flow be encapsulated into the addProductToCartFlow function of the ShoppingCart model? That would seem to be a bit messy, since it would need to access the users table for login/authentication and access the products table for pulling the product details/price into the cart.

Instead, would you say the ShoppingCart model is SELF-CONTAINED and only deals with adding items, removing items, etc. from the cart? The "logic" of the user being logged-in would then be checked elsewhere: perhaps in the controller itself? This would make the controllers very BUSY with quite a bit of "business logic" such as checking if the user is logged-in, checking if the shopping cart is empty, etc. and the model just becomes a pretty name for the database table.

Or maybe, the very fact of being logged-in or logged-out is part of a UserAuthentication model that deals with such functions. Or maybe we need a UserPageState model to tell us whether the user should be on a login page, or a cart page, or a product detail page?

What is the best MVC design for this situation in your opinion?

A: 

I will have a model for each table in my simple sites. I can use any number of models in my controllers. In more complex examples, where a customer and a cart aren't separated, I have a model that brings those together with methods that know about both tables.

Have lots of models, very slim controllers and only HTML in your views. Your controllers should have a lot of the "if" statements that get results from the controllers. Your views can have "fors" that loop and repeat results, but no other logic. No HTML tags anywhere but in your views.

So to answers your question, you can have the following models

  • Users [gets into the users table]
  • Cart [get into the cart table]
  • product table and the users table]
  • Products [gets into the product table]
  • Orders [users, carts and products table (as a basic simplistic example)]
  • PageState a model that tracks your users state in your site

You have the following controllers

  • Cart
  • users
  • products

The controllers are maybe very long, but each action is a small one, with a specific job. I think it is ok to have a controller with 10 actions, so long as those actions are nice and short themselves.

The models that those actions call can be fairly long, and usually will be to account for the business logic. I also use helper classes to do things that are not business logic, but still critical. Think like security and validation.

And a whole slug of views. Views for the cart, for the login box, for the product summary, product detail, each phase of checkout, the receipt, views for the HTML formatted email receipt, category listing, menus, footers. Lots and lots of views.

You would want to have some ApplicationClass that all your controllers inherit so you can do somethings over and over.

You mentioned php, so you have views that make up your template, but if you did ASP you would have a masterpage that does partial views. I don't know exactly what you do in Ruby or Python, but it is similar.

MrChrister
+3  A: 

Your models are, essentially, business objects. You'll have ShoppingCarts, Users, and Items (probably one cart per customer in most cases, but who's to say?). You'll have controllers that drive your flow - the controller method for /cart/add/207366 will check to see if the user is authorized, and pass them to the controller for /login if not. The login controller should be smart enough to pass the right info back to the controller for /cart/add/207366, which should then add the item to the cart.

The controllers would call Cart.AddItem(), but the business logic is contained inside the shopping cart model - it might look up the price of the item, a preferred customer discount based on the User, etc. Controllers wouldn't know or care about this. Controllers do need to know if a user is logged in (if that matters to the application), as this affects their job (determining what View to render). They don't need to know if a customer is preferred, or on credit hold, or whatever other conditions matter to the business logic. That's all handled by the models.

Harper Shelby
A: 

I think that your worry about busy controllers shouldn't be a worry: that's their purpose. The cart controller that has the add action will rely on the users controller or the authentication helper to see if the user is logged in. The above two replies captured this pretty well.

Also, instead of creating another model for pages, I would just use a session variable or another field in the table. Another model will really just complicate things, and it doesn't actually represent an entity that you need to have self contained. When do you see a need for a stack of pages visited? Its really just a string of the url the user came from to get to the login form, which you can keep track of in a much less code and memory intensive fashion with session variables. Just have addItem() check to see if it is logged in, and if not redirect to the login page and store the current request. Then once logged in, check to see if the redirect variable is set, and if so go to it and reset it. I don't see a need for anything more complex than this.

Also, it looked like your pastebin code was CakePHP. Good choice. If you can, take advantage of the bake console and the scaffolding code generation. It does so so so much work for you, and just leaves the good stuff for you.

hornairs