I can't find an answer to this anywhere. I've seen code examples in C# with ASP.NET MVC, but nothing in PHP outside of a CMS or framework.
If I have two tables, students and classes, how do I get that data rendered in the view?
If I query two different tables in one model (same function/method, however), does that somehow violate the principles of MVC? What about 20 different tables? That sounds like a lot of overhead for nothing a one off table you might only query once or twice. Do I really need 20 models?
Can someone please show me this in straight PHP with no frameworks and no CMS?
EDIT: This is for building my own MVC, so "straight" PHP means me building this component.
My approach would probably be to use a repository for classes and users in this case.
StudentRepository
Retrieves instances of students class based on some criteria. This might have a method called getStudentsByClass($classID) that would retrieve the students for a class by it's class id.
SchoolClassRepository
Retrieves instances of a school class based on some criteria. For instance, classes for a semester.
getClassesBySemester($semesterID)
Then in your regular "SchoolClass" class, I would have a function that uses the StudentRepository to retrieve classes for the current class. For instance:
<?php
class SchoolClass(){
private $id;
private $students;
public function getStudents(){
$repo = new StudentRepository();
return $repo->getStudentsByClass($this->id);
}
}
?>
This would mean that your queries to find students for a class would not be exposed in your SchoolClass model. The only thing that the SchoolClass knows about is that the StudentRepository returns the users that it needs.
You would then pass a SchoolClass model as your data model to grab information in your view.
Related
One of the things that I don't fully understand about the MVC model is loading multiple data tables from the model.
Let's say we have a Model called user.
public class User{
protected $username;
protected $email;
protected $id;
public function setUsername($name, $id) {
$db->set->name($name)
$db->set->id($id)
}
public function getUsernameById($id) {
return $db->get->name($id);
}
As far as I've learned, the model is equivalent to a single row in the database (called user).
Now I have written two functions. The first is for setting the username and the other is for retrieving the username through an ID.
Suppose you want to retrieve all users. Let's say through the getAllUsers() method. This method does not fit on the Model as it is not a single object.
Now I understand that for example, you can call this method (function) in your controller itself. But where can you define this method (function)? Since you don't do this on the model.
I also like to hear it if I am wrong :)
Basically your definition is correct:
Model objects hold data and define the logic for manipulating that
data. For example, a Student object in the Basic sample application is
a model object. It holds data describing facts about the object like
the first and last name of the student and has methods that can access
and change this data (getters & setters). Model objects are not
directly displayed. They often are reusable, distributed, persistent
and portable to a variety of platforms.
(from: http://best-practice-software-engineering.ifs.tuwien.ac.at/patterns/mvc.html)
So where to put the getAllUsers() function in case you do not just put it into the model?
Well first of all when we are talking about the term "model" we do not always mean model. In MVC the model (M) contains all of the business logic. This means it contains the domain model which is the model you are meaning in this question. But it also contains the service layer.
The service layer usually consists of various classes which provide functionalities like for instance getAllUsers() or getAllUsersWhichOwnAGreenTruck().
Here is a great graphic demonstrating this:
(Source: Domain Model and Service Layer patterns in P of EAA)
In modern applications e.g. built with the Laravel PHP Framework you usually generate the database as well as the model and the database mapping/communication automatically (Example Eloquent ORM: https://laravel.com/docs/5.8/eloquent). But you start implementing complex application logic within the service layer.
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.
OK, I am building an MVC framework. Let's say for example, I have a SongsController. In a database, for each song I have the song's owner id (a user). What if I want to access the users information by getting the user id and selecting from the users table from that id and getting things such as name, email etc. I understand that there are joins, but for this example lets pretend there isn't any. What could I do? Could I create a UsersRepository class? Also, would I want to call it statically or create a new instance of it.
There a multiple ways to structure this.
For instance, you could use a UserModel, which is an object representing the user table.
This object does not provide any functions to launch queries with joins.
Then you could add a repository class, which provides additional functions related to queries, spanning over multiple tables (with joins).
UserModel
getUserName($userId)
UsersRepository
getSongsOfUser($userId)
You can think of this as:
the UserModel being "object 2 table" and
the UserRepository being "object 2 tables".
--
How to work with Models in the Controller? Should i call them statically or dynamically?
The general rule applies: avoid static functions in OOP. So: instantiate your models.
$userRepo = new UserRepository();
$result = $userRepo->getSongsOfUser($userId);
This is mainly because static stuff is really hard to test.
A static function is isolated and you have to pull all the depenencies in, for example the database instance. This leads to other static functions calls, like Database::getInstance() or to static properties access, which has to be populated (somehow, somewhere, before). You see, this gets messy really fast. This often leads to poorly designed spaghetti code applications, where procedural code sauce is mixed with object oriented noodles. Such an architecture is hard to maintain and it's hard to implement new features.
If this is just a hobby project, then $result = UserRepository::getSongsOfUser($userId); would suffice.
Let's say I have two models:
class Book
{
public $book_id;
public $book_author_id;
public $title;
}
class Author
{
public $author_id;
public $author_name;
}
I'm used to writing something like this:
$DB->query("SELECT book_id, title, author_name
FROM book
LEFT JOIN author
ON book_author_id = author_id
");
Let's assume, that I'm not interested in having separate queries for this association. How do I proceed? Here are some things I've heard:
create a MySQL VIEW of the JOIN
create a model class of the VIEW
The application I'm working on involves dozens of tables and was highly optimized in procedural code (almost no SELECT * anywhere, for instance). I'm refactoring to make it more maintainable (I'm the original creator, too), but I would like to have the flexibility of using joins when I need to without compromising the structure of my files and DB calls.
A possibly related question I have is related to including other models:
class Author
{
public $author_id;
public $author_name;
/* #var Book */ //<--don't really fully understand this but I've seen something like it somewhere
public (array) $authors_books;
}
I'm still searching for answers, but if you could send a link my way, that would be appreciated.
What you are calling "models" are actually domain objects. They should be responsible for dealing with domain business logic, and have nothing to do with storage.
The storage-related logic and interaction should be handled by separate group of objects. One of most sensible solution is using data mappers. Each mapper can deal with multiple tables and complicated SQL.
As for your query, the result of such query would contain information, appropriate for passing to a collection of domain objects.
BTW, that query is quite useless. You forget that each book can have multiple authors. Take for an example this book - it has 4 different authors. To make this query useful, you should have to do GROUP_CONCAT() based on either author_id or book_id.
When implementing such JOIN statements, the database response most likely will be a collection:
$mapper = $factory->buildMapper('BookCollection');
$collection = $factory->buildCollection('Book');
$collection->setSomeCondition('foobar');
$mapper->fetch( $collection );
foreach ( $collection as $item )
{
$item->setSomething('marker');
}
$mapper->store( $collection );
P.S. Your code example seem to be leaking abstraction. It is a bad practice to let other structures to access object's variables directly.
P.P.S. It seems that your understanding of model part of MVC is quite different from how i see it.
Database joins are an artefact of relational databases, you don't need to model them. You need to model what your data is and how it behaves, e.g. you might have a getBooks() method in your Author instances, or a static getByAuthor() method in your Book class (generally, $author->getBooks() should be implemented as Book::getByAuthor($this), so your Author class shouldn't be concerned with Book's implementation details). It's not always a good idea to automatically instantiate all related data (e.g. instantiate Book instances for all books by a given Author instance, as you seem to be considering with your $author_books property), since this might easily degrade into a "load the entire database to memory for each request" scenario.
If you're trying to model your DB in classes, this is a problem that's already been solved.
I suggest you try out the Doctrine framework, which is a full ORM framework for PHP.
Hope that helps.
What are some of the ways you have implemented models in the Zend Framework?
I have seen the basic class User extends Zend_Db_Table_Abstract and then putting calls to that in your controllers:
$foo = new User;
$foo->fetchAll()
but what about more sophisticated uses? The Quickstart section of the documentation offers such an example but I still feel like I'm not getting a "best use" example for models in Zend Framework. Any interesting implementations out there?
EDIT: I should clarify (in response to CMS's comment)... I know about doing more complicated selects. I was interested in overall approaches to the Model concept and concrete examples of how others have implemented them (basically, the stuff the manual leaves out and the stuff that basic how-to's gloss over)
I worked for Zend and did quite a bit of work on the Zend_Db_Table component.
Zend Framework doesn't give a lot of guidance on the concept of a "Model" with respect to the Domain Model pattern. There's no base class for a Model because the Model encapsulates some part of business logic specific to your application. I wrote a blog about this subject in more detail.
Persistence to a database should be an internal implementation detail of a Model. The Model typically uses one or more Table. It's a common but improper object-oriented design to consider a Model as an extension of a Table. In other words, we should say Model HAS-A Table -- not Model IS-A Table.
This is an example of IS-A:
class MyModel extends Zend_Db_Table_Abstract
{
}
This is an example of HAS-A:
class MyModel // extends nothing
{
protected $some_table;
}
In a real domain model, you would use $some_table in the methods of MyModel.
You can also read Martin Fowler's take on the Domain Model design pattern, and his description of the Anemic Domain Model antipattern, which is how many developers unfortunately approach OO programming.
I personally subclass both Zend_Db_Table_Abstract and Zend_Db_Table_Row_Abstract. The main difference between my code and yours is that explicitly treat the subclass of Zend_Db_Table_Abstract as a "table" and Zend_Db_Table_Row_Abstract as "row". Very rarely do I see direct calls to select objects, SQL, or the built in ZF database methods in my controllers. I try to hide the logic of requesting specific records to calls for behind Zend_Db_Table_Abstract like so:
class Users extends Zend_Db_Table_Abstract {
protected $_name = 'users';
protected $_rowClass = 'User'; // <== THIS IS REALLY HELPFUL
public function getById($id) {
// RETURNS ONE INSTANCE OF 'User'
}
public function getActiveUsers() {
// RETURNS MULTIPLE 'User' OBJECTS
}
}
class User extends Zend_Db_Table_Row_Abstract {
public function setPassword() {
// SET THE PASSWORD FOR A SINGLE ROW
}
}
/* CONTROLLER */
public function setPasswordAction() {
/* GET YOUR PARAMS */
$users = new Users();
$user = $users->getById($id);
$user->setPassword($password);
$user->save();
}
There are numerous ways to approach this. Don't think this is the only one, but I try to follow the intent of the ZF's design. (Here are more of my thoughts and links on the subject.) This approach does get a little class heavy, but I feel it keeps the controllers focused on handling input and coordinating with the view; leaving the model to do the application specific work.
Don't ever use Zend_Db_Table as your model. It just gets you into trouble. Either you write your own model classes which use Zend_Db_Table to talk to your database or you can read my blog post here for a hack that allows you to somewhat combine the "Model" class and Zend_Db_Table.
The main thing to not is that when you use Zend_Db_Table directly in your controllers you end up doing the same things in multiple places. If you have to make a change to some of that logic, you have to make a change in multiple places. Not good. My first professional project was done like this because I was the one in the company who had to learn how to use ZF and it's a total mess now.
I also tend to write helper functions into my classes for sophisticated fetches. Somthing like $table->doNameFetchAll() or $table->doOrderFetchAll().
I've been doing some research on Models for ZF and came across an interesting series of articles by Matthew Weier O'Phinney which are well worth checking out:
Using Zend_Form in your Models
Applying ACLs to Models
Model Infrastructure
It's not "production code" and a lot is left to the imagination, but it's a good read and has helped me quite a bit.
A model has nothing to do with the database. What if I am fetching data from an RSS feed or a SOAP service or reading files from the FS?
I put all these kinds of things in models. In that case, my model class might not extend anything. I'm about to write a model that uses methods of other models.
Skip ZF for the models part, there are much better solutions. The "M" in ZF's "MVC" is pretty much absent. Reading their docs they don't really mention models at all -- which is a good thing, it means you can use just about anything you want without writing lots of adapter code.
Take a look at Doctrine for models instead. It is quickly becoming the de-facto ORM for PHP.
You can do more complicated queries, check the Advanced usage section in the Zend_Db_Table manual page.
$select = $table->select();
$select->from($table,
array('COUNT(reported_by) as `count`', 'reported_by'))
->where('bug_status = ?', 'NEW')
->group('reported_by');
you can extend the Zend_Db_Table_Abstract class and add some useful methods to it. for example you can add a changePassword() method to your user class and manipulate it's data. or you can change the default __toString() method of your class, so you'll have a customized __toString() method that, let's say returns the whole contact information of the user (name, address, phone number) in a well formatted string. in your constructor you could populate your data into properties of your object. then use them like:
public function __toString() {
$data = $this->_name . ', ' . $this->_adderss . ', call: ' . $this->_phone;
return $data;
}
your model extends the Zend_Db_Table_Abstract just to ease the process of accessing its data, but the functionality you could have on that data is all up on your creativity and need.
I recommend you the book "php|architect's guide to programming with zend framework" by Cal Evans. the book is very informative and easy to read. chapters 4 and 6 are going to be useful for this matter.
A database entity is not the only kind of model component. As such, it doesn't really make sense to speak of models (in plural) - Your application has one model, which contains a multitude of components. Some of these components could be table gateways (And thus extend from Zend_Db), while others would not.
I recommend that you get hold of the book Domain Driven Design by Eric Evans, which does an excellent job of explaining how to construct an object model.
I use Propel 1.3 instead of Zend_Db_Table.
It's tricky to setup, but awesome.
It can examine your database and auto-generate all your models.
It actually generates 2 levels and 2 types of model.
Examples for 'user' table:
Level 1: BaseModel & BasePeer: these get overwritten every time you regenerate your ORM. i.e. BaseUser.php & BaseUserPeer.php
Level 2: StubModel & StubPeer: these don't get overwritten. They're the ones you customize. i.e. User.php & UserPeer.php
Type 1: Model - for basic CRUD operations, not queries i.e. User.php
Type 2: Peer -- for queries. These are static objects. i.e. UserPeer.php
So to create a user:
$derek = new User();
$derek->setFirstName('Derek');
$derek->save();
To find all dereks:
$c = new Criteria();
$c->add(UserPeer::FIRST_NAME, 'Derek');
$dereks = UserPeer::doSelect($c);
http://zfsite.andreinikolov.com/2008/08/zend_db_table-time-overhead-about-25-percents/
Bit of a catch 22, Zend_Table is nice in principle, but generates some performance overheads (without caching)...