How complex should models be? - php

When implementing models using the MVC pattern, how complex should my models be?
Let's say I have got a few tables like so:
User (id, password, created ...)
Emails(user_id, email ...)
Addresses (user_id, address ...)
I have got a controller called UserController. This controller should allow me to log users in, create users, etc.
<!-- language: php -->
class UserController{
public function create($array){
...
}
public function login($email, $password){
...
}
}
Should my models be very primitive, implemeting only CRUD operations via ORM? This would result in code like:
<!-- language: php -->
class UserController{
public function create($array){
$userModel->username = 'blah';
$userModel->blah = 'blah';
$id = $userModel->save();
$emailModel->id = $id;
$emailModel->email = "emailhere";
$emailModel->save();
//Do the same for addresses
}
public function login($email, $password){
...
}
}
Or, alternatively, I could have models that are more complex:
<!-- language: php -->
UserModel{
public function login($email, $password){
//Do the joining and checking here, then return true or false to the controller
}
}
Then in my controller:
<!-- language: php -->
userModel->login($mail, $password);
So, which is the better way? To stuff all the logic into models, or should I have models doing only the basic CRUD operations? Finally, how do I deal with table joins? Should they be dealt within the model, or in the controller?
Cheers

Most people think of the "Fat Models, skinny controllers" paradigm, and it works better in the long run.
A great rule of thumb is to think about your Models as their own entity, in the sense that if you were to move your models to a different framework for example, they would still be functional.
A simple example would be saving information about an order on an e-commerce website. Let's say you want to save information about Tax, namely how much Tax is on the order. An abstract way of doing this is...
$tax_amount = $order_amount * (TAX_PERCENTAGE / 100);
Should we do this in the controller or the model? Suppose we did it in the controller, well... in our create action and our update action we would be calculating the tax, which makes for harder to maintain code and should the business rules of the e-commerce website change (say, we start selling to Tax exempt businesses or overseas business) then we would have to change any controller that saves order information.
If we were to calculate Tax in our Order model however, we would be doing it once in our save() method, which would be called when editing and adding orders.
public function save() {
//calculate tax first
$q = $this->db->query($sql);
}
Imo, it's better to enforce business rules in your model because it makes for more portable code and far less headaches when it comes to maintaining your code. Of course, there are people who will disagree, and this is a very subjective area.
EDIT:
Applying this to the specific question you asked, think about whether you would ever need the login() method anywhere else but your User model? It's possible you might want to split your models into different, more specific ones. But you could extend from your User model in that case.
What about if you were to take away your controller completely? Or if you wanted to interface with your models in an entirely different way (say via a different framework in the future). Thinking in this way you would be far better off with your login method in your User model.
Personally, I would be creating a login method on my model because it is an operation on data and that is what our models are for. I would also be creating a loginAction() method on my controller which would initiate the login() method on our model and perform any other action (for example, log failed attempts/redirect) that has to happen on post-login, should it be successful or unsuccessful. An example loginAction() may look as follows...
class UserController extends GenericController {
public function loginAction() {
$post = $this->form->getPost();
if(UserModel::login($post)) {
//do something
} else {
//do something else
}
}
}

All functionality which will or could be reused at different parts in the application should be accessbile globally, so that the coupling is low and there is no need to redeclare it.
I think you need an additional model for authorization and/or the current system parameters. The AuthModel could store information about the different authorization roles and the SysModel stores application parameters like the default login settings (e.g. use cookies yes or no).
The login method could be placed in the AuthModel then, which should be a good place in my opinion. Also the models are responsible to validate the inputted data, so the creation of new users should be in the UserModel.

Related

What should the service layer of an MVC web application look like?

I have been reading about how to make my controllers clean and simple by moving business logic into a service layer. The problem I am encountering is that if I:
Align the service layer towards models e.g. UserService, AuthenticationService, then that reduces redundancy but it also assumes that each method of the UserService e.g. update() is going to be used in the same way but business logic as we know is different in different parts of the application. Methods that serve multiple scenarios are usually dirty because if 3 different parts of the application use the same method then you have to satisfy all 3 parts.
Align the service layer to reflect the controllers and their methods. This way has the potential to increase redundancy but it also makes it so you don't build single golden bullet methods that are supposed to encompass any scenario they're needed.
I have shown 2 examples below:
Reflecting models
class UserController extends Controller {
public function update(Request $request){
// Load user service and create history
$user_service = new UserService();
$user_service->update($request);
// Load user history service and create history
$user_history_service = new UserHistoryService();
$user_history_service->createHistory($request);
// json response with success
return response()->json([]);
}
}
Reflecting controllers
class UserController extends Controller {
public function update(Request $request){
// Load user controller service, update user and create history
$service = new UserControllerService();
$service->updateUser($request);
$service->createHistory($request);
// return json response with success
return response()->json([]);
}
}
The 2 ways I have identified are just what I have been gathering from reading and also a bit of my own thinking. Which way scales properly and I guess has the least sacrifice?

Are CakePHP models actually controllers?

I am no expert in the MVC architecture, however I have been working using the mentioned architecture on some JAVA projects. Now I'm getting into PHP MVC Frameworks, (like CakePHP and Laravel) and I decided to go for CakePHP.
Going deep into it, I see Models in CakePHP are in charge of operations like querying and some others while data itself is managed in arrays (i.e. $user['User']['first_name']).
The way I am familiar with, Model classes are just for storing model data, in other words, what arrays do in CakePHP
public Class User {
private String firstName;
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getFirstName() { return this.firstName; }
}
And Controller classes are in charge of querying and other model-building operation, in other words, what models do in CakePHP:
public Class UserManager {
public User getById(int id) { /* */ }
public boolean save(User user) { /* */ }
}
So, are CakePHP, and simillar MVC Frameworks, Models actually controllers? also if yes, shouldn't this be a new type of architecture, something like Router-View-Controller, since controller class actually routes request ?
I think the official documentation explains it already pretty well:
Models are the classes that sit as the business layer in your
application. This means that they should be responsible for managing
almost everything that happens regarding your data, its validity,
interactions and evolution of the information workflow in your domain
of work.
Usually model classes represent data and are used in CakePHP
applications for data access, more specifically they represent a
database table but they are not limited to this, but can be used to
access anything that manipulates data such as files, external web
services, iCal events, or rows in a CSV file.
Actually a model in CakePHP 2.0 is basically thought to do any kind of data manipulation.
Need to turn an array into a CSV string? Model job.
Need to validate an uploaded image? Model job.
Need to save an uploaded file? Model job.
Need to somehow manipulate your POST data? Model job.
...
None of the tasks above is thought to be done in a controller, we want to respect separation of concerns (SoC). The controller just manages the request but doesn't process data, that's the models job. Also models are more easy to unit test than controllers plus you can share them in a shell, you cant do that with controllers without MVC violations: Here is an example:
You should always keep your models fat and controllers thin:
/** CONTROLLER **/
if ($this->request->is('post')) {
if ($this->Model->doSomething($this->request->data)) {
$this->Session->setFlash(__('Something was done right!'));
$this->redirect(['view' => $this->Model->data['Model']['id']);
}
}
/** MODEL **/
public function doSomething($data) {
$this->create();
$result = $this->save($data);
if ($result) {
$result[$this->alias][$this->primaryKey] = $this->getLastInsertId();
$this->data = $result;
return true;
}
return false;
}
No, models are not controllers, not in any php framework out there.
Models just present a collection of your data, a controller at the other hand controls the flow between models and view. For example, the controller calls your model to get data, then process that somehow and push to to the view, so you can pick it up there and do something with it.
Are you willing to tell why you choosed for cakePHP ?

In Magento what is the correct data flow from POST to controller to model to block to template?

When data is sent to a Magento extension via GET or POST, what is the correct way to use the MVC to validate the data, process it based on some business logic then output the results to the screen?
As I understand it, the controller is the correct place to receive and validate the data being submitted (perhaps calling on a model to do the actual validating). Models are the correct place for any business logic, taking the request parameters and processing them. The block is the correct place to prepare data for output, which it can request from a model.
I understand how a controller can receive posted data and forward it to a model
class Company_Project_IndexController extends Mage_Core_Controller_Front_Action
{
public function receivePostedData()
{
$model = Mage::getModel('project/somemodel');
if($model->validateData( $this->getRequest()->getPost('post_vars') )) {
$model->processData( $this->getRequest()->getPost('post_vars') );
}
}
}
which the model will then process
class Company_Project_Model_Somemodel extends Mage_Core_Model_Abstract
{
public function validate( $data )
{
//return true of false if data is valid/invalid
}
public function processData( $data )
{
//Do something with the data
}
public function getData()
{
//return something
}
}
I also understand how a block can instantiate a model and get data from it
class Company_Project_Block_Display extends Mage_Core_Block_Template
{
public function getData()
{
$model = Mage::getModel('project/somemodel');
return $model->getData();
}
}
The problem I have is the disconnect between the controller and the model working together and the Block and the model working together. If the controller is pushing data into the model which is changing the models state, how do I then get data into the block from the model based on that changed state?
As I see it, there are the following possible solutions:
Use Mage::getSingleton to get my model so I'm referring to the same instance in the controller and the block
Refer to the post data directly inside the model or the block
Use the registry to store some of the model state
I've know idea if any of the above are correct or which one I should be using.
I've taken a look at the core Catalogue Search module as its doing exactly what I need, but I got completely lost in the code.
I know that several other very similar questions to this have been asked and answered but I'm still in the dark.
It's a good question, but there isn't one clear answer. If you look at the Magento core itself, you'll see a variety of methods used. All that said, here's some general advice/context for the tradeoffs involved.
Based on usage, Magento's controller action methods are not designed to interact with The View (or as it's known in Magento, The Layout). Instead, a controller action is meant as the main entry point for a specific URL, which should then interact with the request and do stuff with models. When that's done, the controller action then tells the layout to render with a calls to
$this->loadLayout();
$this->renderLayout();
As designed (and this is just an opinion) The Layout is completely decoupled from the rest of the system. CodeIgniter/Kohana style systems feature "dumb views", which take variables from the controller action and do a simple template replacement on them. The Magento Layout, on the other hand, is a collection of nested block objects, and block object methods are meant to query the models directly when they need information.
For example — take a look at the Mage_Adminhtml_Block_Catalog_Product_Attribute_Set_Main_Formset block
#File: app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Set/Main/Formset.php
protected function _prepareForm()
{
$data = Mage::getModel('eav/entity_attribute_set')
->load($this->getRequest()->getParam('id'));
//...
}
The idea here is, irrespective of what happens in the controller action, the Mage_Adminhtml_Block_Catalog_Product_Attribute_Set_Main_Formset block will always grab the latest eav/entity_attribute_set model information when it renders (accessing the request object to get the request/post data, as you mentioned above). The controller action and the view are decoupled from one another.
As designed, the is The Right way to use the system. Unfortunately, it's also ineffective from a performance standpoint. For developers coming form other systems, the idea that view rendering kicks off another round of SQL queries seems crazy. Magento's famous "start shooting and let the caching layer" sort it out approach met with mixed results.
One remedy for this potential performance problems is where stashing already instantiated models in the registry comes into play. You can see an example of this in the admin console's product editing controller
#File: app/code/core/Mage/Adminhtml/controllers/Catalog/ProductController.php
protected function _initProduct()
{
//...
Mage::register('current_product', $product);
//...
}
and then its use in many of the resulting blocks which render the editing form for products
app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Js.php
public function getProduct()
{
return Mage::registry('current_product');
}
The trade-off here is better SQL performance (no extra queries for the blocks), but you've essentially tied these blocks to a controller action which defines a current_product registry entry. The Magento registry is (in essence) a fancy way of doing global variables, so you have most of the problems inherent with globals. As you noted, you can achieve similar results with the getSingleton method — but that may not be appropriate depending on the task you're trying to accomplish.
For what it's worth, the registry/singleton method appears to be favored by the core developers for both the admin console application (the Mage_Adminhtml module) and the Magento Mobile controllers (the Mage_XmlConnect module), while the more paranoid "load the model explicitly" pattern is used more in the front-end cart application. Whether this is a coincidence, a conscious choice, or just the core team itself learning as it went along is probably one of those unanswerable questions.
Finally, one technique that's not used much by the core code, but that I sort of like when I'm looking for more of a "dumb view" behavior, is this. After you load the layout, but before you render it, you're able to access specific blocks by name. This, combined with the power of magic getters and setters, lets you do something like this
$this->loadLayout();
$content = $this->getLayout()->getBlock('content');
if($content)
{
$content->setSomeValue('Hello World');
}
$this->renderLayout();
and then from your block's template, you can grab the variable with
echo $this->getSomeValue();
You're still tying your block's implementation to your controller action but doing so in a less global-ish way. Also, the decoupled layout may not contain the block you're looking for, so you'll want to be careful with this.

OOP + MVC advice on Member Controller

I am trying to follow good practices as much as possible while I'm learning using OOP in an MVC structure, so i'm turning to you guys for a bit of advice on something which is bothering me a little here.
I am writing a site where I will have a number of different forms for members to fill in (mainly data about themselves), so i've decided to set up a Member controller where all of the forms relating to the member are represented as individual methods. This includes login/logout methods, as well as editing profile data etc. In addition to these methods, i also have a method to generate the member's control panel widget, which is a constant on every page on the site while the member is logged in. The only thing is, all of the other methods in this controller all have the same dependencies and form templates, so it would be great to generate all this in the constructor, but as the control_panel method does not have the same dependencies etc, I cannot use the constructor for this purpose, and instead I have to redeclare the dependencies and same template snippets in each method. This obviously isn't ideal and doesn't follow DRY principle, but I'm wondering what I should do with the control_panel method, as it is related to the member and that's why I put it in that controller in the first place.
Am I just over-complicating things here and does it make sense to just move the control_panel method into a simple helper class?
Here are the basic methods of the controller:
class Member_Controller extends Website_Controller {
public function __construct()
{
parent::__construct();
if (request::is_ajax())
{
$this->auto_render = FALSE; // disable auto render
}
}
public static function control_panel()
{
//load control panel view
$panel = new View('user/control_panel');
return $panel;
}
public function login()
{
}
public function register()
{
}
public function profile()
{
}
public function household()
{
}
public function edit_profile()
{
}
public function logout()
{
}
}
What I would do, is to avoid shoving everything in one controller, and instead separate functionality accordingly - for example, you could have a Registration_Controller to deal only with members' registration, Authentication_Controller, Profile_Controller, and so on, this way is easier to visualize what each part of your application is responsible for, instead of having one single controller with lots of responsibilities, which leads to confusion and other maintainability issues, at least IMHO, it has worked for me.
Going back to your concrete question about the control panel, yes it makes more sense to take it out of the controller, specially if is not an action of it, and as you mentioned you can have a helper class for all the repeatable display logic. If something doesn't make sense within the context of the controller, take it out.
There are some cool frameworks such as Cake and Zend FW, that make life easier at designing/developing MVC application, and that come with a rich set of components that work out-of-the-box.
MVC and Fw's are not mandatory though, it all comes to the scope and your special needs, some times they are of great deal help, most of the times I use them, but sometimes overcomplicate things. Keep it simple ;)
Cheers,
M.

CodeIgniter: Where should a particular functionality go?

Here is a quick overview of the controllers functionality in most of the application:
controller loads a specific model, gets data from it, formats the data and passes the formatted data to the view.
Now there is a search page, which needs to do a search query over entire database (all models). It needs to show each type of data in its particular formatted output on a single page as a list.
The problem:
The search controller can do the search, dynamically load model for each record type, and get the data from model. Problem comes when the data needs to be formatted. I am trying to load the specific controller from the search controller, which is causing problems.
What to do?
PS: I tried using the 'Wick' library, but it fails when the controller's format function tries to use its own model and session object, giving errors about call to a member on a non-object.
After much refactoring and trial/error, It appears that the best way to achieve the above is this way:
Keep the format function in the base controller from which all other controllers are derived. The format options are passed to the function along with the data object as arguments.
Make a static function in each derived controller, which returns the formatting options of the data.
Inside the search controller (which is itself derived from the base controller), for each data object, call the static function of its particular controller which returns the data formatting options, then use that to format the object for the view.
I guess I can say I will stick to using the model only for interaction with the database, and let everything else be done by controller. If anyone has a better solution still, I am all ears.
It sounds like you want to use the Factory design pattern
Make this a library:
class MyModelFactory {
static public function Factory($data) {
$type = key($data);
return new $type($data);
}
}
now, in your controller, you can do something like this:
$model = MyModelFactory::Factory(array($_REQUEST['model'] => $_REQUEST));
and now you have an object of whatever model was specified in $_REQUEST['model']. Be sure to take any security precautions you may need for your application to assure the user has permissions to use the model that they request
Now, since you want to be using common methods and stuff, your models should probably be based off an abstract class / interface.. so instead of
class MyModelOne extends Model {
// stuff
}
You probably want something like this, to ensure your required methods will always be available:
abstract class MyAbstractModel extends Model {
protected $search_params;
public function __construct($data = array()) {
$search_params = $data['search_params'];
}
protected function GetSearchParameters() {
return $this->search_params;
}
abstract public function GetData();
abstract public function GetColumns();
abstract public function DefineViewOptions();
}
class MyModelOne extends MyAbstractModel {
public function GetData() {
$params = array();
$params[] = $this->db->escape_str($this->GetSearchParameters());
// return whatever data you want, given the search parameter(s)
}
public function GetColumns() {
// return some columns
}
public function DefineViewOptions() {
// return some configuration options
}
}
In general you can't load another controller from within a controller in CodeIgniter (although there are mods that allow you to do something like this).
I would try creating a class for formatting your data and add it to the application/library folder. Then load, use and re-use this class throughout your various controllers.
Here is a page from the CodeIgniter documentation Creating Your Own Libraries that explains the details and conventions.
Also, if a class is overkill, creating helper functions is an even lighter approach.
The difference between libraries and helpers in CodeIgniter is that libraries are classes, helpers are just a group of php functions.
Once you have formatted your data, you can load any view from any controller, so you should still have all the re-usability you need so you DRY (don't repeat yourself)
There are a few simple approaches based on the principle of what's simpler (versus what's perfectly DRY). Here's one alternative approach I use with CodeIgniter:
Instead of trying to load multiple controllers, reuse the view fragments from your search controller (or search route, depending which you're using). This requires using the same naming conventions for your data elements so the views are interchangeable, but you should be doing this anyway.
Instead of using multiple models for search, add a single Search model that knows about the things that can be searched on. If you want to prevent duplicate SQL, reuse the SQL between models (this can be done using constants, or loading SQL from disk).
Controllers are not great candidates for reuse from your own PHP code: they route actions and requests for resources to the things themselves. They are intended to be called via HTTP, using the URI interface you've come up with. Calling them from code is a coupling you want to avoid. That said, reusing controllers from JavaScript (or via cURL) is a great, decoupled way to reuse things in any web framework.

Categories