I have read a book about MVC last week and a general one about design patterns, but I'm still confused as to where SQL queries belong in my code: the model or in the controller?
Let's take a very simple example, where you have a /popular page that will print the 5 most popular stories on a website.
In your model, you would have a class for prepared staments, and a class for assisting in the creation of the SELECT query. In your view, you'd have the HTML elements that display the /popular page.
Where does the query "SELECT most popular stories LIMIT 5" belong to? Is that something a controller class should ask, taking query methods from the model and passing to the view, or should the query be declared instead on a model class related to the /popular page?
Is the distinction even relevant? Would placing that query on the controller or the model be both considered professional ways to build a MVC?
Thank you. It seems most people get stuck understanding what to place on controllers
Edit: thanks for help everyone. Unfortunately as a new account I can't upvote any helpful posts yet
Usually (based on my experiences with MVC frameworks) the model layer takes care of database-related stuff in MVC.
Consider following approach:
Create abstract class which covers all the DB operations (selects, updates, etc). Each table would be a PHP class extending such class. You can then define DB table name in for instance private field, constructor or depending on model name.
Each controller (or the single controller) would load desired model, and use its methods to fetch data as associative arrays or objects, delete the data, change it.
After all DB operations have been done, controller returns view and passes data as its parameters.
Note that models are great place to put all the validation rules, and some helper methods due to the fact that they can be easily tested in PHPUnit.
Related
If I really go through the MVC approach, then the queries should be in the Model, in CakePHP case the Table Classes, but when I go through the tutorials and documentation that Cake Provides they simply state that queries should be in the Controller.
As you can see in the example over here on Cake's websites: https://book.cakephp.org/3.0/en/tutorials-and-examples/blog/part-two.html
But if I go through this link or many other I have come across, the query part should be in the Models: https://www.toptal.com/cakephp/most-common-cakephp-mistakes#common-mistake-3-keeping-business-logic-in-controllers-instead-of-models
It is not just about what Cake displays in the examples or some developers opinion, but what should really be the genuine way to code in Cake when dealing with database queries. I have found almost 90% people doing query related tasks in Controllers only for Cake, as they quote "Cake mentions the same in their examples". But what about the MVC way, we create Table Classes just to mention the associations then? If Cake's own website does that, then somehow it means they have done it intentionally.
It's a good programming practice that to use your database queries in your Model because you can reuse these queries(In another controller) later by calling the method using the object of the model. However, you can also write your queries in your Controller.
For example:-
//Consider this code block is in Products Model
function totalActiveProduct(){
$totalProduct=$this->find('all', ['conditions'=>['is_active'=>'Y']]);
return $totalProduct;
}
If you want to get the total active product in any controller you can do this,
$this->Categories->Products->totalActiveProduct(); //Total procuct in category controller
$this->Products->totalActiveProduct(); //Total products in Product controller.
Actually, when you are writing the query in you controller, you have to use the object of your model(That means indirectly you are using your controller). You are thinking that you are writing this in your controller but actually, you are writing it in the model(Object of Model).
$this->Products->find('all');
Simply this means you are writing this in your model object(Where Products is the model object). Directly or indirectly you are doing your each and every database operation through the Model.
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.
Apologise if it sounds a dumb question, but if I never ask, I'll never learn. I have been recently working with the MVC implementing pattern, and I'm still learning stuff, so please be patient with me.
I've been recently working on a multi-languages website that uses a custom MVC framework for a client. In some places I'd need to display some information from more than one table, like an article (from the articles table), the name of the languages that this article is written in (from the languages table), the languages that this article is not yet written in (from the languages table also), and the author (from the authors table).
I have a model for the articles, a model for the languages, and a model for the authors, and each has its own controller.
If I only query articles tables only or the authors tables only, there is no issue, but the confusion comes when I want to display information from this different tables into a single view.
How I should do that?
Instantiate a language model object and an author model object in the article controller and call their related functions to get the data and then combine/merge their data with the data from the article model (all in the article controller as mentioned)?
Instantiate the language model and the author model in the article model and call their functions to get their data, then combine/merge the their data with the data from the article model itself?
Do a join or nested select in the article model to get the data from multiple tables, and not interact with any other model, even if that mean some queries will be repeated in more than on model?
Another approach?
What is the best way do that?
This can be done in several ways:
- Create a view (a virtual table of MySQL) which using join, nested select or whatever you want to achieve all necessary information and create a model on top of this view in MVC (this model can only retrieve info from the view not alter it, however)
- Use repository pattern. A repository wrap many models and you can implement business logic (which might be CRUD on multiple models/tables) here. The controller simply use the repository instead of model.
In summary the model, repository should wrapping all the business logic of its own never let the controller do these stuff.
Regards,
Ok, I couldn't insert the overview image, but I'm required to create a PHP app for my studies, it must allow student to register, and the administrator to edit course and student info.
We are not required to code it in an OOP style, but since its the best programming practice to code in OOP, why not learn OOP from the beginning.
I'm a beginner but I know the basics about OOP, classes, inheritance, setters and getters and all that cool lingo, but I'm struggling to decide which parts of this app should be objects, should I make course and student classes or add, edit and delete classes? Any advice on how to approach and visualize such a problem would be appreciated.
Very roughly: This is how I would do it:
Store your data in SQL or XML. You will need two SQL tables, one for Students and one for Courses. You can use one XML file containing all the data, or you can use two files (which I recommend).
Create a class called, for example, dataItem with a property like '$arr_fields' corresponding to a single data record (a single row in a SQL table, or an XML record).
The dataItem class should have the following methods (all public):
loadFromSQL() (or loadFromXML())
saveToSQL() (or saveToXML())
add(), edit() and delete()
a view() method using HTML
These methods are obviously used to read and write data between the SQL/XML data and $arr_fields of the class, and to display the data in $arr_fields. The keys of $arr_fields are the SQL column names (or XML tag or attribute names) for the specific SQL table.
Try not to call loadFromSQL() or saveToSQL() in your constructor or in any of the other methods which are used to modify only the class data. Keep these actions separate. EDIT: This is a personal preference which helps me to keep track of the state of my objects.
Create Student and Course classes that extends the dataItem class.
You can override methods, for instance the view() method, inside your extended classes if you need to.
Then you can call the methods in Students and Courses from an Admin object (like rcdmk suggested) or maybe from StudentFolder and CourseFolder classes whose view() method contains buttons for the actions that need to be performed. (Let StudentFolder and CourseFolder extend a Folder class that you create).
UPDATE:
For example: If your primary key in a SQL table is id, then dataItem's loadFromSQL($id, $tablename) should set $arr_fields so that its keys are the column names and it's values are the values from the row whose primary value is equal to $id.
In Students, you can then override loadFromSQL() as follows:
class Students extends dataItem {
// other attributes
public function loadFromSQL($id) {
parent::loadFromSQL($id, "Students");
}
}
EDIT: On reconsideration, it might be better to set $arr_fields["id"] = $id and also set $tablename with the constructor for dataItem - then you never have to override loadFromSQL() or specify parameters for it. loadFromSQL() should then load the record if it exists. saveToSQL() should save $arr_fields in SQL if $arr_fields["id"] is set and create a new record if it is not set. Anyway, you must find a consistent way of interacting with the data which works for you, these are just possibilities.
However, if you are not experienced with OOP and SQL or XML, you might be opening a can of worms for yourself and it might be better to just do your assignment using functions only and php arrays for your data. Unless you have some time to learn...
From a simple perspective:
Abstract the main objects as classes and use methods for actions of this objects:
Student (object) are deleted (action) by the Admin (object), so
Admin class will have a deleteStudent method, because Admin deletes Students.
Another aprouch is to concentrate all Student related actions in the Student class:
Student class will have a public delete method that Admin can use.
Anyone that think this in better ways of explanation can edit this wiki.
Think about which aspects of your system are actually objects, you know, something you can do something with. The methods are what you do to the objects. So, you're on the right track with course and student classes, and add, edit, and delete would be methods of those classes.
But don't get too bogged down with it. If it's not your core assignment objective, you could quickly get in over your head by trying to do everything exactly the right way. If you can formulate a clear way to get to where you need to go, then go for it, if it seems confusing, back off it a little and learn some more.
You say that you know the basis of OOP, however you ask whether you should create Course, Student classes OR Add, Delete, Edit classes. Well maybe there are other practices, but I guess the most popular one and the only I am aware of is to use nouns as classes and verbs as their methods. Hence, intuitively there is something wrong with class "Add" or "Edit". What I would have done if I were were, is to think of all "entities" that might be considered an object - like Student, Course, Lecturer, Class (Room) and depending on how advanced your model should be you can add more like Building etc. Then try to implement basic things like creating new student, registering for a course, associating teacher with a course etc. Once you have it in place and IT IS WORKING you might want to add advanced things, like inheritance. For example you might want to say, that both Teacher and Student are a Person so you might want to create such abstract class and use inheritance.
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