MVC - How does it work in the real world? [closed] - php

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
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?

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.

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.

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.

Related

Is it possible (and how to?) divide application to backend (admin part) and frontend (user part) parts?

I am developing web application, using zend framework 2. In my app, I want some modules. News, for example, users etc.
Is it possible, to divide whole application to two big parts: front-end (for users) and back-end (for admins).
In front-end there will be part of News module, which render news list, full news item etc.
In back-end there will be part of News module, which will render list of all news, tools for editing, deleting etc.
At the moment, I have simple structure:
I have MyZfcAdmin module, with routes configurated so, that when I am writing /admin/news/edit/5 it will call NewsController edit action with id 5 and render it. So now, I have all actions (for admins and for users) in one controller. Is it possible to create something like this:
module
frontend
News
SomeMoreModule
backend
News
SomeMoreModule
I found topic on google questions, in that suggested to use this CMS, but I want to try it by myself.
Different people have different idea in managing application, for me I will have news module that handle all news related calls both admin parts and user accessible parts. The idea you have described have some side effects like duplication of code between modules and crossing of borders between isolate modules like calling action in another modules(it's ok if there are related or fall in hierarchy), which may have issues in long term maintenance of the applications.
IMHO I might suggest you could create modules like News, users, etc where it can have combined functionality of both admin and user access. You can later include user authorization plug ins using bootstrap events and restrict action accessible by admin and other users
There is not concept of submodules inside any module. But you can give them proper naming conversations. If code is not formed in oops properly, then there can be code duplication into your project. As per #raj's answer, it's good to give restrictions based on user role. If any action is not intersecting admin and user role then you can separate those actions, but actions like add or edit news content are same for both role then you should point to same action by router.

Structuring classes and objects in CodeIgniter for future upgrades?

I'm new to Codeigniter and pretty new to OOP as of the last month or so. I've been playing around and have been trying to make a blog page with a list of 10 posts and have different functions and possibilities for each.
For example, in the future I want to be able to show different buttons below posts depending on whether they are a logged in user, or if they are the post creator, etc.
Would I be best off making a new class 'blog_posts' and making an instance of it for every post I show on the page? like:
$this->load->library('blog_posts'); // in library or models, I'm not too sure?
$new_inst = new blog_posts;
$new_inst->show_post();
My end goal is to get it all as easily updated, changed, or modified in future if needed, I've seen simple tutorials online:
http://blog.pisyek.com/2011/03/create-a-simple-blog-using-codeigniter-2-0-part-1/
But I can't see them being easily updated and modified in future. I haven't seen a lot of other CI apps using new instances of classes, so I'm thinking there may be a different way in CI? Are new instances used in CI a lot?
Start with the controller. With Codeigniter -- anything you can do in a model, you can do in the controller. The easiest way to start -- just put your methods in the controller and set up a simple view file.
Always do a simple sanity check to make sure the basics are proper. Set up a controller and echo out some text.
In the controller try:
Get blog posts from your database using active record commands
Pass the blog posts result to $Data
With each step, Echo out something in the controller to confirm that its working
Next -- try using $Data to pass the blog results to the view.
After its working and you understand the basics -- then start pushing your methods to a Model. As you refactor and clean, your controller will get 'thinner'.
typically for blog posts you would have one model, that would return the blog posts and might also have your insert / update / delete methods. i say might because it usually makes more sense to have a public blog controller, that just shows blog posts. and then a separate blog admin controller -- that is only available to users who are logged in.
These two controllers - can share the same blog model. So for example to show blog posts on an admin screen - you could use the same method as showing the posts on a public page. Whereas you might have an admin blog model -- which has the create / update / delete methods.
That way you enforce a very clear separation of User Roles from the start -- people who are viewing the public blog should not be able to delete posts. The public show blog class is not "responsible" for editing.
This is something that i'm grappling with -- like the normal way to demonstrate a blog is to have all the methods together. Which makes sense in terms of "what can we do with a blog post" -- but it doesn't make any sense in terms of user roles.
ok Next - work on your create and update and delete methods.
Next try creating your log in methods. Make the Login process separate controller / model / view.
Eventually you could have one method call in the constructor of your blog admin class that checks to make sure that the user is logged in - otherwise it redirects them to a login page. That way you dont have to check over and over again in the view files etc to make sure they are authorized.
( highly recommend the free codeigniter tutorial series on net tuts website )
more rambling:
looking at the process from the point of view of the actor (user,role) -- for me helps to clarify what the chain of control should be. really critical roles - like an admin that has the power to delete all blog posts - the system should know that role at the top of the class.
best 'worst example' is when there are "is this a super admin?" checks in the view code. the view should not know anything about admin roles. BUT MUCH WORSE when you put a reference to an admin role in some view file -- what happens when you want to change that admin role in some way? now you have to go into all your view files!
class and method names - i'm moving towards not revealing any business or crud or field or any other type of business information in the URL. no nouns no verbs no ids. as simple as possible, with very few public url 'doors' into the app.
whats wonderful is that codeigniter makes it really easy to make your methods private. for critical pages: only use the public index method in the class for redirecting and make all other methods private.
use routes that directly to private methods. or public methods with broad names, that if successful -- like you have confirmed their credentials - go to private methods in the class like $this->_showAdminPage()
this is where defining a role like - blog editor - makes things very clear from a functionality point of view, AND keeps the valuable business knowledge private.

Symfony 1.3: Need to override backend filter field's set of data

My project is using Symfony 1.3.11, PHP version is 5.3.3.
Let me explain my case and my problem:
We wanted to add another kind of user, not just "user" and "admin" (using sfGuardPlugin). I did add "business" and I did give him permissions to only ONE admin module -- "purchases" (accessing own and under some conditions -- other people's purchases made on the site).
The "business" user can be associated to one or more Partners (firms) registered in the site, effectively allowing us to handle business owners or representatives a wee bit differently.
When the "business" user accesses the admin section, the list of purchases he sees must be this: All Purchases for all Products of all Partners he has associated with him. (Not exactly a trivial query).
I did that, in my opinion, in a stupid manner, but it does work -- namely overriding the "apps/backend/modules/purchase/actions/actions.class.php#executeIndex". I had to first copy-paste some code from the version of that method in the cache.
In short, I did hook myself to the "executeIndex" and added a code to do the proper Criteria, and the code works.
The problem now is this:
The filter field above the list (the one containing the Product list) does contain all possible Products. We want it to only contain all Products for all Partners the current user is associated with.
Thing is, it's my first time having to override the admin's filters and I am lost as to where should I plug my code.
Any help is appreciated. Thanks for your time.
UPDATE: Strangely very unpopular question. Nobody ever had to do this? Wow.
You have to unset the product_list and set another widget with the filtered choices.
Check this post where I explain something like that but using doctrine. In your case the sfGuardUserForm is your somthingFilterForm.
You could also filter data using this method. This is a little more elegant but both would work.

One-To-Many member-to-articles relationship

We are new to Expression Engine and could maybe use some help from anyone who has built a few sites using this CMS.
We have a small dashboard system where users log in and read articles that are submitted from other authors. We are wanting to have a form which allows a member to chose which categories of articles he would like to be on his home page when he logs in. Then we'd like to figure out how to actually create that home page so that only the categories of articles that the user has chosen are visible.
From a DB standpoint this seems to be a one to many relationship between a member and categories, but we don't have a clear idea how to accomplish this inside of EE (without raw PHP/queries), especially using the Member module, since it isn't a channel and doesn't seem to allow creating relationships in the member custom fields.
We looked at creating a custom field for every category type, but there are about 95, and as we add or remove them, it would be extremely cumbersome to try to keep up with them all. Even if we did we still aren't sure how to connect 1 member to many categories or many entries via a category.
We'll update the question later to add some of the code we (if any) that we've come up with
In short, there isn't going to be a built in way to handle this. That isn't to say however that this can't be handled with some modules that are already out there. Let me ask you this. When you have users logging in, are they logging in to Expression Engine itself's backend, or the login system that you have created on a public facing website.

Adding interactions to admin pages generated by the admin generator

I am using Symfony 1.2.9 (with Propel ORM) to create a website. I have started using the admin generator to implement the admin functionality.
I have come accross a slight 'problem' however. My models are related (e.g. one table may have several 1:N relations and N:N relations). I have not found a way to address this satisfactorily yet. As a tactical solution (for list views), I have decided to simply show the parent object, and then add interactions to show the related objects.
I'll use a Blog model to illustrate this.
Here are the relationships for a blog model:
N:M relationship with Blogroll (models a blog roll)
1:N relationship with Blogpost (models a post submitted to a blog)
I had originally intended on displaying the (paged) blogpost list for a blog,, when it was selected, using AJAX, but I am struggling enough with the admin generator as it is, so I have shelved that idea - unless someone is kind enough to shed some light on how to do this.
Instead, what I am now doing (as a tactical/interim soln), is I have added interactions to the list view which allow a user to:
View a list of the blog roll for the
blog on that row
View a list of the posts for the blog on that row
Add a post for the blog on tha row
In all of the above, I have written actions that will basically forward the request to the approriate action (admin generated). However, I need to pass some parameters (like the blog id etc), so that the correct blog roll or blog post list etc is returned.
I am sure there is a better way of doing what I want to do, but in case there isn't here are my questions:
How may I obtain the object that relates to a specific row (of the
clicked link) in the list view (e.g. the blog object in this example)
Once I have the object, I may choose to extract various fields: id etc.
How can I pass these arguments to the admin generated action ?
Regarding the second question, my guess is that this may be the way to do it (I may be wrong)
public function executeMyAddedBlogRollInteractionLink(sfWebRequest $request)
{
// get the object *somehow* (I'm guessing this may work)
$object = $this->getRoute()->getObject();
// retrieve the required parameters from the object, and build a query string
$query_str=$object->getId();
//forward the request to the generated code (action to display blogroll list in this case)
$this->forward('backendmodulename',"getblogrolllistaction?params=$query_string");
}
This feels like a bit of a hack, but I'm not sure how else to go about it. I'm also not to keen on sending params (which may include user_id etc via a GET, even a POST is not that much safer, since it is fairly sraightforward to see what requests a browser is making). if there is a better way than what I suggest above to implement this kind of administration that is required for objects with 1 or more M:N relationships, I will be very glad to hear the "recommended" way of going about it.
I remember reading about marking certain actions as internal. i.e. callable from only within the app. I wonder if that would be useful in this instance?
I'm assuming your application is called backend. Suppose there are two models, BlogPost and BlogPostComment. These are managed using admin generated modules called blog_post and blog_post_comment.
I believe you want a link against each BlogPost displayed on the list page at backend.php/blog_post. The links take you to backend.php/blog_post_comment, which should only show comments related to the relevant BlogPost.
Under apps/backend/blog_post/templates, create a file called _commentslink.php and put this in it:
View Comments
Then in apps/backend/blog_post/config/generator.yml, you need to include this partial in the fields for the list view:
....
param:
config:
list:
display: [ id, title, _commentslink ]
Note the _commentslink - the _ tells it to use a partial instead of looking for the field in the model. Your object is available in this partial as $<name of model> - $blog_post in this case.
Essentially, all this method does is links to the same action as the filter on the comments list normally goes to, passing the relevant condition to it to make it filter by blogpost_id.
If you've got CSRF protection enabled in the backend, you'll need to disable it, or this method won't work. This is set in apps/backend/config/settings.yml. There will be a setting in there called csrf_secret - it should be set to false to disable csrf.
You should try symfony 1.3/1.4 out if you need support for 1:N relationships in forms. 1.3 is in my experience a relatively hassle free upgrade from 1.2.x - 1.4 is the same, but with deprecated features removed.

Categories