Proper use of controllers with MVC (CakePHP) - php

Cake's documentation says "Most commonly, controllers are used to manage the logic for a single model." I'm finding this is uncommon for most of my code, and I don't want to break convention unless it is proper to do so.
For example, my application sends a user to their account dashboard after they log in - this uses data from probably half a dozen tables, not all of which are even related. Do I create a "dashboard" controller for this (even though there is no dashboard model or table)? Or do I create a dashboard method in an existing controller?
Thanks, Brian

I have a similar situation and how I handle it is keeping the actions that connect a lot of models in the controller that is the most centric. For instance, my user can create voicenotes, comments, has settings, has twitter and facebook information. All this information I can get from my user model $this->User->Voicenotes->find('all'), for example.
I believe creating additional controllers might just confuse you, use what cake gives you, you can specify that models are to be used in a controller either by setting the $uses variable or using loadModel in the controller action, if you have your relations set up you can just do it the way i described before, no need to create additional controllers.
I guess it depends on how you want your own app to work and what comes easier in your situation.

Related

Laravel controllers for complex pages

I have started a new laravel website project and I have hit a road block in my understanding of MVC. I need to refactor to continue but I don't know the best way.
Currently I have web pages that display the results of a single bit of logic. I.e. A page listing all users, a page listing the details of just one user etc etc - all handled by a userController. This applies to other pages being handled by other controllers.
I have created models directly relating to the tables in my database, and controllers in relation on the models. I moved the business logic from the controllers to services. The controllers use the services to perform the business logic and with the data returned, pass that data to the views.
This nicely groups similar functionality together and works fine.
userTable -> userModel -> userController -> userService
clientTable -> clientModel -> clientController -> clientService
...
In my routes, I have pages which do related functionality use the same respective controller, but individual methods depending on what the page does
/listallusers -> userController#list -> userList.blade.php
/listallclients -> clientContoller#list -> clientList.blade.php
/listdetailofoneclient -> clientContoller#details -> clientDetails.blade.php
This is ok when dealing with pages that do that functionality and (apart from using services) seems to be what is hinted at in the laravel docs.
However, I'm starting to get confused about controllers when dealing with pages that either don't really use functionality from any of the services or pages that heavily require functionality from multiple services (and the data needs complex manipulation like formatting or such).
A basic index page.
What controller would handle this? The index might link to routes that are handled by existing controllers but it probably won't need to display much functionality apart from that. That means the controller won't need to pass much complex -if any- data to the view. You could stick the logic to return the view in the route file but that is pretty tightly coupled.
A page that shows complex client and user data
You need to pass client data and user data to the view from the controller. But from what controller? This is the part that is really holding me back.
Because I have a limited number of pages but lots of logic displaying on each page, I was thinking of making a page controller (or something) which would handle the routing. Although I have looked, I have not seen any real mention of this idea anywhere which makes me think I am either reinventing the wheel or have failed to grasp some basic concept in laravel / MVC.
Would the page controller in this case handle all the routing? Would it handle only the pages with 'overlapping' existing controller functionality and the pages that don't fall into the existing controllers? Is a page controller even a good idea?
Some more points that have made me question MVC from this issue
Do controllers need an equivalent model?
Can controllers 'control' other controllers in order to separate logic?
I'll take a shot at a couple of these questions.
Having a PageController or HomeController is very acceptable. Controllers don't have to be linked to a specific model, for instance an AuthController would handle logic for logging in and out but isn't tied to a model, or PasswordController which handles setting/resetting passwords, or a PaymentController that handles billing routes. A controller is just a way to organize logic for related routes in one file. Static basic pages are related routes so a PageController makes sense to me.
Are users tied to a specific client? If so you can arrange your controllers in a nested way so you have a ClientController and a UserController and your routes look something like this:
/clients/{client}/users //list all users for this client
For heavy data formatting it's probably best to use a service provider and use dependency injection to inject it into controllers where you need to use it. This allows you to detach your data manipulation from your controller so you could change it out if needed. Say you are using a software to make charts, and you want to change it out later - you want that formatting logic to be removed from your controller.
I hope I helped in some way... sort of a train of thought here!

where to keep common code to be used in controllers and views

there are cases when i require to run the same logic both in controller and view, i thought they may have a shared code, for example when to display an edit link and the same logic should check for edit permission in controller , where should i keep such code so that same can be used in controllers as well as views, I am okay to write a a component wrapper and a view wrapper for this method but the core logic should be common.
Some mentioned bootrap is a place but putting there do i have all the cake defined parameters or constants available from that location? or there is a better place
EDIT
I gave only authentication related example but there can be more cases like, a view helper for displaying data/time based on offset time set in database (system time + offsetime), i have been forced to use of same code in controllers also (ajax output). What does it imply that in common code we not only have shared logic but also some shared data too, so for only session classes appears to be providing shared data!
On the whole, I'm inclined to say you're not going about this the right way. If you have to include the same piece of logic in both the controller and the view, chances are you're doing the same work twice, which is always a bad idea. DRY (Don't Repeat Yourself) is something you probably heard about a million times before.
However, in the case you mention (authentication): this is done in the Controller, which relies on the Model layer to find out if the user has the permissions to see/use edit links.
Based on the data the controller receives from the Model layer, it should do one of the following things:
choose to render a specific view (with/without edit links)
Present the user with an error view
redirect to the login
For the first case, an editable and non-editable view, you can choose to use the same view script, and use a helper to pass on the session information to the view. Effectively giving the view the means to check a users' session, check if the user in question has the rights required to see the edit links and render them...
Another simple fix would be to set a property of the view to true for editable and false for non-editable in the controller, and check that bool flag in the view. No extra logic required.
The main thing here is that what you're after is authenticating the user. By the time you reached the view, the route is a given, there's no way back. If you find yourself still having to validate the users' identity, you've made a mistake at an earlier point: the controller, and model layer is where this kind of core logic should reside.
A view contains no logic other than its own: a loop or 2, some if-else's... nothing more.
The rule of thumb, then is: the first thing the controller does is authenticate the user. The authentication itself is the concern of the Model layer: no actual core/business logic should be in the controller. Based on the findings the model layer returns (authentication failed, or user has rights to do X, but not Y), the controller can redirect, throw errors or choose to render a specific view.
Only after all these things are performed the view is brought in. If there's a user in play, the view can assume that this user is valid, and has the rights required to see its contents. The view does not validate data, nor does it authenticate users.
To authenticate a user the Cake-specific way there's a core Authentication component that seems to be quite well documented. See if you can't use that...
You can add simple functions to bootstrap, but another option would be to create a class in app/Lib/ using static methods. Then from any controller you can include the library and use the methods defined:-
App::uses('MyLibrary', 'Lib');
If all controllers need to use these just include the library in AppController.
For your views I'd then consider defining a View Helper that would apply the methods used in MyLibrary.

How to let only logged in users view certain pages in MVC pattern?

I've been doing PHP for years and know how to check if a user is logged in and all that but I can't find a good example of how to do it in the MVC way.
I was thinking if the Controllers for the members pages are like members/memberpage1, members/memberpage2 then I could check the start of the Controller with a regular expression for the word "members" and if it returns true I could then do my check to see if the user is logged in. It would probably work but is it the best way?
Also, for the members pages in the controllers folder is it better to put them in a subfolder called "members" within the controllers folder? I'd definitely be doing that with the views anyway.
The best option would be to check users access rights outside the controller. Authorization is not the responsibility of the controller. You would be breaking SRP. If you want to see how it can be implemented, read this topic: ACL implementation.
As for member-only areas of the site, decision about, whether to show them or to display an error, can be handled wither before accessing controller, or by the Views (you might have some restricted views, if you have active views instead of passive ones).
Though more often you would have both parts involved: authorization service changes the state in model layer, and then view reacts to that change by choosing to include error-message template in the presentation.
Everything does it a different way. I tried building a small MVC framework for PHP and this is the way I done authentication (much like the rails way really:)
Had a 'SessionsController'
The SessionsController contained a method called 'user_is_logged_in' and that returned the current state of the users session. This method was then added to a special method '_before'on any class I wanted to secure.
The main application always ran a controllers _before action before any other methods if it existed. You could also pass 'except' into it to stop it executing on certain actions so it was just a case of checking if the user was logged in in that action.
Of course there are probably more compliant ways to do it as #tereško greatly suggested but it worked for the small application I was working on.

Why is $uses considered bad practice in cakePHP?

I have 3 tables that contain user information, one for students, one for teachers and one for administrators.
They are not related in any way. I wan't to create a dashboard for the Administrators, where a list of students and teachers shows up.
The only way I found to achieve this was using the $uses variable in the Administrators controller. However, I have read in many places that this is bad practice.
Any solutions?
Another, perhaps better practice is the use of ClassRegistry::init('MyModel')->myMethod() (more reading # Cake API)
This only loads the object when it's used, as opposed to loadModel or uses, with ClassRegistry the models are treated as singletons.
--
that you are doing something wrong: you need access to a model that has nothing to do with your current controller.
There are plenty of conditions where you would need to access all of your models data, from one controller, but never a definitive answer on how to do it without breaking convention!
You can always use another Model which is not related by using
$this->loadModel('NewModelName');
Then you can access new loaded model by:
$this->NewModelName->add(); // whatever method model has defined
Why prefer loadModel() over uses?
To gain performance. How? uses calls the loadModel function itself to load all the models you specify in uses array. But the problem is if only one of your action needs a particular model, whats the good thing to include it in every action. e.g. only add() action requires an unrelated model, but if you have specified it in uses array, no matter what action gets called a completely unrelated model is going to load. To put simply it will be inefficient. Its like you have declared variables in a C programme but never used them. In case of C compiler will warn you that you are not using your variables, but unfortunately cake couldn't tell you.
Its alright to use uses if all your actions needs to load that model, use loadModel() otherwise.
You probably didn't read my answer in your other question :))
I have 3 tables that contain user information, one for students, one for teachers and one for administrators. They are not related in any way. I wan't to create a dashboard for the Administrators, where a list of students and teachers shows up.
The problem is you are separating similar data into 3 different tables, in this case, user information. So when you try to manage this data, you hit a brick wall: because you leave out the relationships when you separate them in 3 tables.
The only way I found to achieve this was using the $uses variable in the Administrators controller.
You got the wrong idea about the controller. Each controller manage the data flow of a particular model (and related models). It doesn't mean that you have to stay in Admin controller to do administrative things. What model you want to manipulate decides what controller you need to be in.
However, I have read in many places that this is bad practice.
Now for the main question: using $uses is a red flag that you are doing something wrong: you need access to a model that has nothing to do with your current controller. Now, there're always exceptions in programming, sometimes we need to have access to that model. That's where loadModel comes in. Because it should be rare. If you need the model a lot, then you'll need to call loadModel a lot, which is cumbersome, which is what $uses is for, but then that means something's wrong with your app design :))
So, you can say using $uses is a sign of bad decision (in DB design or application structure); and so is using loadModel a lot.
Edit: Any solutions?
I gave one solution in your other question. But if you want to have them all in one place, you can have 1 users table with user information. Each User can hasOne Student, Teacher, Administrator and a 'group' field to decide what group the User is. The third solution is using $uses. Its performance impact won't be a problem really. But it will be pretty convoluted when you develop your app further. That's what you need to worry about. For example, I can say that, if you use Auth, you'll need to tweak it a fair bit to get it working with 3 models. If you use the users table, it will be a lot easier.

What would my controller be in these scenarios in a mvc web application?

1) Where does the homepage of your website fit into "controllers"? I've seen some people use a "page" controller to handle static pages like, about, home, contact, etc., but to me this doesn't seem like a good idea. Would creating a distinct controller just for your homepage be a better option? After all, it may need to access multiple models and doesn't really flow well with the whole, one controller per model theory that some people use.
2) If you need a dashboard for multiple types of users, would that be one dashboard controller that would have toggle code dependent upon which user, or would you have say a dashboard action within each controller per user? For example, admin/dashboard, account/dashboard, etc.
3) It seems to me that using the whole simple CRUD example works like a charm when trying to explain controllers, but that once you get past those simple functions, it breaks down and can cause your controllers to get unwieldy. Why do some people choose to create a login controller, when others make a login function in a user controller? One reason I think is that a lot of us come from a page approach background and it's hard to think of controllers as "objects" or "nouns" because pages don't always work that way. Case in point why on earth would you want to create a "pages" controller that would handle pages that really have nothing to do with each other just to have a "container" to fit actions into. Just doesn't seem right to me.
4) Should controllers have more to do with a use case than an "object" that actions can be performed on? For all intensive purposes, you could create a user controller that does every action in your whole app. Or you could create a controller per "area of concern" as some like to say. Or you could create one controller per view if you wanted. There is so much leeway that it makes it tough to figure out a consistent method to use.
Controllers shouldn't be this confusing probably, but for some reason they baffle the hell out of me. Any helpful comments would be greatly appreciated.
1) I use a simple homebrew set of classes for some of my MVC stuff, and it relates controller names to action and view names (it's a Front Controller style, similar to Zend). For a generic web site, let's assume it has a home page, privacy policy, contact page and an about page. I don't really want to make separate controllers for all these things, so I'll stick them inside my IndexController, with function names like actionIndex(), actionPrivacy(), actionContact(), and actionAbout().
To go along with that, inside my Views directory I have a directory of templates associated with each action. By default, any action automatically looks for an associated template, although you can specify one if you wish. So actionPrivacy() would look for a template file at index/privacy.php, actionContact() would look for index/contact.php, etc.
Of course, this relates to the URLs as well. So a url hit to http://www.example.com/index/about would run actionAbout(), which would load the About page template. Since the about page is completely static content, my actionAbout() does absolutely nothing, other than provide a public action for the Front Controller to see and run.
So to answer the core of your question, I do put multiple "pages" into a single controller, and it works fine for my purposes. One model per controller is a theory I don't think I'd try to follow when working with Web MVC, as it seems to fit an application with state much better.
2) For this, I would have multiple controllers. Following the same methods I use above, I would have /admin/dashboard and /account/dashboard as you suggest, although there's no reason they couldn't use the same (or portions of the same) templates.
I suppose if I had a gazillion different kinds of users, I'd make things more generic and only use one controller, and have a mod_rewrite rule to handle the loading. It would probably depend on how functionally complex the dashboard is, and what the account set up is like.
3) I find CRUD functionality difficult to implement directly into any layer of MVC and still have it be clean, flexible and efficient. I like to abstract CRUD functionality out into a service layer that any object may call upon, and have a base object class from which I can extend any objects needing CRUD.
I would suggest utilizing some of the PHP ORM frameworks out there for CRUD. They can do away with a lot of the hassle of getting a nice implementation.
In terms of login controller versus user controller, I suppose it depends on your application domain. With my style of programming, I would tend to think of "logging in" as a simple operation within the domain of a User model, and thusly have a single operation for it inside a user controller. To be more precise, I would have the UserController instantiate a user model and call a login routine on the model. I can't tell you that this is the proper way, because I couldn't say for sure what the proper way is supposed to be. It's a matter of context.
4) You're right about the leeway. You could easily create a controller that handled everything your app/site wanted to do. However, I think you'd agree that this would become a maintenance nightmare. I still get the jibbly-jibblies thinking about my last job at a market research company, where the internal PHP app was done by an overseas team with what I can only assume was little-to-no training. We're talking 10,000 line scripts that handled the whole site. It was impossible to maintain.
So, I'd suggest you break your app/site down into business domain areas, and create controllers based on that. Figure out the core concepts of your app and go from there.
Example
Let's say I had a web site about manatees, because obviously manatees rock. I'd want some normal site pages (about, contact, etc.), user account management, a forum, a picture gallery, and maybe a research document material area (with the latest science about manatees). Pretty simple, and a lot of it would be static, but you can start to see the breakdown.
IndexController - handles about page, privacy policy, generic static content.
UserController - handles account creation, logging in/out, preferences
PictureController - display pictures, handle uploads
ForumController - probably not much, I'd try to integrate an external forum, which would mean I wouldn't need much functionality here.
LibraryController - show lists of recent news and research
HugAManateeController - virtual manatee hugging in real-time over HTTP
That probably gives you at least a basic separation. If you find a controller becoming extremely large, it's probably time to break down the business domain into separate controllers.
It will be different for every project, so a little planning goes a long way towards what kind of architectural structure you'll have.
Web MVC can get very subjective, as it is quite different from a MVC model where your application has state. I try to keep major functionality out of Controllers when dealing with web apps. I like them to instantiate a few objects or models, run a couple of methods based on the action being taken, and collect some View data to pass off to the View once it's done. The simpler the better, and I put the core business logic into the models, which are supposed to be representative of the state of the application.
Hope that helps.

Categories