I am currently looking to redesign a feature on my web application.
The web application utilizes Yii (version 1) for the back-end.
In this instance I have a model and controller. The model is used to store all the userTracking data and is appropriately a userTracking model however the actual logic for the model is in a controller called UserController. I have a function called actionTrackUser($id) which is used to implement various tracking logic for a particular user and create a model for that user.
I however now need to extrapolate this functionality from the UserController to a seperate trackingController which will implement tracking for various models.
I need to be able to utilize this functionality however in the new controller and old controller. I was wondering as to the best approach for this in Yii 1 that implements MVC correctly. I thought about making a trackingModel and having the userTracking model extend that but then I would have a lot of business logic in a model in order to use it in two places.
I am fairly new to MVC and Yii so I was wondering as to the best approach to take here?
I have purposely left code out of this question as it is more a theoretical question regarding the implementation of such functionality in Yii.
Any help is appreciated.
Thanks!
I am not sure that there may be a 'best' approach. However, you need to choose an approach that works best for you. Be guided by what will work for your situation, and what will make manageable code for you and others in the future.
There is nothing wrong with your current userTracking model and controller. You state that the "actual logic for the model is in a controller." You may want to relook at that to move appropriate data management, constraints and validation rules to the model at least.
You can move functions to a separate trackingController and still use the userTracking model. There is no rules that says you have to have a matching model and controller set, and it is quite possible for a controller to manage more that one model at a time.
I generally start with activeRecord models, which map against data stores (database tables in most cases). I use corresponding Form models to reflect forms that are markedly different from activeRecord models, and I use domain models to reflect complex business objects that are feature rich, and reflect lots of runtime attributes (like calculated fields) and state data (like publish state, which are calculated from other fields - for example a user could have validation status, paid status, active status, account level and so on that all work together to indicate what activity is possible for the user).
Related
This is a general question about MVC, but the context is CakePHP.
The Cake documentation largely avoids discussing optimal placement of model-related code, such as queries. Perhaps for simplicity's sake, or to avoid imposing any ideas on how the developer wants to structure their app, they simply always document queries as being in the controller. Essentially, a Fat Controller pattern.
e.g.
The page requires 4 large queries to show different areas of data in the view "XYZ". The end result of the queries is a very specific set of data just for view XYZ.
Fat Controller
Controller handles request
Controller runs query on model 1
Controller runs query on model 2
Controller runs query on model 3
Controller runs query on model 4
Controller performs additional logic on data
Controller sends data to view XYZ
In this situation, the controller is doing all the work and is simply leveraging Cake's built-in model framework features to query the correct tables. The model layer itself contains no custom code.
So the alternative is to use a Fat Model pattern, but then I don't feel that's correct either, particularly where the model is determining which data will be sent to the view.
Fat Model
Controller handles request
Controller calls on model 1 to retrieve data
Model 1 queries and performs additional logic on data, sends back to Controller
Controller calls on model 2 to retrieve data
Model 2 queries and performs additional logic on data, sends back to Controller
Controller calls on model 3 ...
Controller calls on model 4 ...
Controller sends data to view XYZ
This keeps the controller nice and clean, and it only serves to handle the request, delegate the retrieval of the data it needs and then forward it to the view.
The problem with this, for me, is that the model layer (probably in table classes) now contains very specific queries which retrieve data specifically formatted for view XYZ. If I want to change the data shown in view XYZ I make those changes in the model layer by modifying the methods which return the fields the view needs.
This seems wrong, and leads to large models which are bloated with tons of very specific functionality.
Question
Which approach is better?
Is there a third solution to this which allows the following separation of concerns:
Controller
Handles the request
Decides which data the view will need, but delegates the job to the model
Forwards the data from the model to the view
Stays thin
Model
Handles all business logic and database querying
Database query logic is ignorant of what the view requires
Is not bloated with specific one-use functionality for specific views
Is there another layer which can bridge the gap between the controller and the model tables, or is it pointless? What would it be in a Cake context? Custom classes outside of Tables or Entities? I fully understand that the model layer does not just mean active records or database querying.
The Cake documentation largely avoids discussing optimal placement of model-related code, such as queries.
This is not required nor the duty of the framework to educate people about design patters - IMHO. The layers and their duties are explained here. For further information use Google or read Martin Fowlers publications about design patterns.
Perhaps for simplicity's sake, or to avoid imposing any ideas on how the developer wants to structure their app,
Yes. You're free to do whatever you want but it is expected that people are at least familiar with the basics of MVC, OOP and design patterns in general. It is not the job of the frameworks documentation to teach these topics.
they simply always document queries as being in the controller. Essentially, a Fat Controller pattern.
Who are "they"? I'm sure the documentation does't encourage bad practice or anti patterns.
The problem with this, for me, is that the model layer (probably in table classes) now contains very specific queries which retrieve data specifically formatted for view XYZ. If I want to change the data shown in view XYZ I make those changes in the model layer by modifying the methods which return the fields the view needs.
Your methods are very likely to fat then and to specific. You won't be able avoid specific methods (there is always something that can't be made generic) but you should compose what they do using many smaller methods that are generic. A method should always do just one thing and do it well and the best not longer than ~80 lines.
Custom finders in Cake3 are a great way to compose queries:
public function findIndex(Query $query, array $options = [])
return $query->find('active')->find('index')->find('somethingElse');
}
We have a lot "active" checks in our app so I put the active finder into a trait and use it where I need it. The other two finders add additional stuff to the query. I'm combining three finds that can be used alone or combined differently for whatever is needed.
In your controller then just call $query = $this->Articles->find('index'); and pass it to your paginator.
Also never forget that "model" describes a complete layer and not just table objects. Cake2 probably caused that less experienced developers might have gotten the impression that model == table. We have several namespaces in App\Model that aren't related to DB actions at all or use table objects for something to get their task done.
Also there are Components for collaborating controller codes in classes and helpers to collaborate view codes in classes. So to make your controller slim you can add components for each job and use them in controller instead of doing all the stuff in the controller actions.
It has another advantage. If you put repeatable code in components then you don't need to duplicate them in each view and you stay observing the DRY (don't repeat yourself) principle.
A quick question this time about the M in Mvc.(using codeigniter, but the question is general)
Suppose I have several models that are responsible for accessing several tables in my database, Is it frowned upon making one flexible model that can deal with everything?
specifically in php-codeigniter, for example:
$this->MY_model->getByID($table,$ID)
So this example code can be used to get by ID of any table I want thus saving me time and I'm able to reuse most of my model code.
This of course can be expanded further
$this->MY_model->update($table,$info) as an example.
So my question is, If all of the above is okay, Where is the 'limit' to such operations? Naturally we wouldn't want something really specific in this type of model, like this method:
$this->MY_model->getAllActiveUsers()
My basic approach is this:
Any method that can be used in more than one table should be used in the extended model.
Any method that should be used in only one table, should be in it's own model.
Just to be clear i'm not looking for an opinion, But rather wondering what's the standard approach to these issues.(for example, imagine an application with 50 models, should CRUD be written for all 50 of them?)
Thanks in advance!
What you are mulling about is pretty standard operation for most Codeigniter developers. If you are going to be doing a lot of CRUD in the DB, then it makes sense to add a set of generic CRUD methods to your base MY_model and then extend it. If you do a search on Google...you'll likely get results for 100s of people who have setup their own base models including CRUD and other common methods used by most Codeigniter projects...here are just a couple:
https://github.com/jamierumbelow/codeigniter-base-model
https://github.com/jenssegers/CodeIgniter-My-Model
should CRUD be written for all 50 of them?
No. You would extend your base model and use it's generic CRUD methods where they make sense and just override certain properties (table name). Then, if you needed something more specific you could create additional, more granular methods in your new model as you suggested.
Sticking to MVC basics in Yii, I am trying to embed my business rules in the model class but facing problem in implementing it. The problem at hand is to stop the user from making duplicate entries and code the function in model class that checks if the entry already exists in table. I want to write a method in my model class which would query the sames model's underlying table and if the new business entity exists, it simply returns false. If I code this in controller, I can achieve the desired functionality but I want to keep this in model so that where ever the model is used, I can access the method and also stick to MVC basics which dictates Thin Controllers and Thick Models. Thanks in advance.
The best way would be to avoid the use of active record instances (at least) directly in the controller.
Instead you should create service-like structures, which contains the interaction between your CActiveRecord and CFormModel instances. This would let you better isolate the presentation layer (views, controllers and templates) from model layer.
Such services also would be able to hold (and sometimes, react upon) errors/exceptions thrown by CActiveRecord and CFormModel instances, that it utilizes.
Cross-site Request Forgery Prevention may be what you are looking for? unless your idea of 'duplicate entries' is directly related to your business model, in which case you can override the CActiveRecord.beforeSave() and put your logic in there, if this method returns false, the record won't be saved to the database.
If you use the later method, and you want to pass the error to the view and display to the user, you can always use the CModel.addError() method, in this case in your beforeSave method.
There is another option though, which is using custom validators.
which is more appropriate? depends on your business logic.
I am building a CRM using a framework (codeigniter) for the first time and I am having trouble determining where a certain module should go while maintaining the MVC methodology. The module automatically generates a new user (when a new company is created) and emails the log in details out to the supplied email address.
I am familiar with the idea of skinny controllers and fat models but to compile all the information needed the module must request data from several different tables as well as inserting data into several tables.
The scenarios I have considered so far:
The logic is in the model where most of the information comes from.
Create a totally new model that deals with just this module and the multiple tables required.
Place the logic in the controller that deals with creating a company.
Create a new library or helper and call the module when it is needed.
Skinny controllers and fat models seem to suggest that one or two are the right options but I was lead to believe that a model should only deal with one table in the database.
What is the right approach to ensure adherence with MVC?
Codeigniter allows you to be flexible with your MVC approach. So the answer is which option is:
Easiest for you (or your team) to understand
Easiest to code maintain
Easiest for someone else to understand
There's no point putting your code into a library, if you dont have any other libraries and dont understand libraries. Same as if all your models are "fat", but only point to one table, do you want this model to be the only one that also points to 4 other tables?
Personally, if this "logic" only ever happens in one place, then I would place it into the controller, and call the 4x models you need to do each bit of the code.
If this "logic" occurs in multiple places, I would place it into a library and call it when needed.
My understanding of the MVC is as follows (incase it's horribly wrong, I am afterall new to it)
Models are the things that interface with the database
Views are the design/layout of the page
Controllers are where everything starts and are essentially the page logic
I'm using CodeIgniter but I would hazard a guess it's not just limited to that or possibly even just to PHP frameworks.
Where do I put global classes?
I may have a model for Products and I then run a query that collects 20 products from the database. Do I now make 20 models or should I have a separate class for it, if the latter, where do I put this class (other controllers will need to use it too)
Model is the wrong word to use when discussing what to do with products: each product is a value object (VO) (or data transfer objet/DTO, whatever fits in your mouth better). Value objects generally have the same fields that a table contains. In your case ProductVO should have the fields that are in Products table.
Model is a Data Access Object (DAO) that has methods like
findByPk --> returns a single value object
findAll --> returns a collection of value objects (0-n)
etc.
In your case you would have a ProductDAO that has something like the above methods. This ProductDAO would then return ProductVO's and collections of them.
Data Access Objects can also return Business Objects (BO) which may contain multiple VO's and additional methods that are business case specific.
Addendum:
In your controller you call a ProductDAO to find the products you want.
The returned ProductVO(s) are then passed to the view (as request attributes in Java). The view then loops through/displays the data from the productVO's.
Model is part of your application where business logic happens. Model represents real life relations and dependencies between objects, like: Employee reports to a Manager, Manager supervises many Employees, Manager can assign Task to Employee, Task sends out notification when overdue. Model CAN and most often DO interface with database, but this is not a requirement.
View is basically everything that can be displayed or help in displaying. View contains templates, template objects, handles template composition and nesting, wraps with headers and footers, and produces output in one of well known formats (X/HTML, but also XML, RSS/Atom, CSV).
Controller is a translation layer that translates user actions to model operations. In other words, it tells model what to do and returns a response. Controller methods should be as small as possible and all business processing should be done in Model, and view logic processing should take place in View.
Now, back to your question. It really depends if you need separate class for each product. In most cases, one class will suffice and 20 instances of it should be created. As products represent business logic it should belong to Model part of your application.
In CakePHP there are 3 more "parts" :
Behaviors
Components
Helpers
Logic that are used by many models should be made as a behavior. I do not know if CodeIgniter have this logic or not, but if it doesnt, I would try to implement it as such. You can read about behaviors here.
(Components helps controller share logic and helpers help views in the same way).
The simplest way is to:
Have a model class per database table. In this case it would be an object that held all the Product details.
Put these classes into a package/namespace, e.g., com.company.model (Java / C#)
Put the DAO classes into a package like com.company.model.dao
Your view will consume data from the session/request/controller In this case I would have a List<Product>.
Oh, you're using PHP. Dunno how that changes things, but I imagine it has a Collections framework like any modern language.
#Alexander mentions CakePHPs Behaviors, Components and Helpers. These are excellent for abstracting out common functionality. I find the Behaviors particularly useful as of course the bulk of the business logic is carried in the models. I am currently working on a project where we have behaviors like:
Lockable
Publishable
Tagable
Rateable
Commentable
etc.
For code that transcends even the MVC framework i.e. code libraries that you use for various things that are not tied in to the particular framework you are using - in our case things like video encoding classes etc. CakePHP has the vendors folder.
Anything that effectively has nothing to do with CakePHP goes in there.
I suspect CodeIgniter doesn't have quite as flexible a structure, it's smaller and lighter than CakePHP, but a quick look at the CakePHP Manual to see how Behaviors, Components, Helpers, and the Vendors folder may be helpful.
It should be an easy matter to just include some common helper classes from your models keep nice and DRY