I\'m building a tiny MVC framework for learning/experimenting and small project purposes. I needed to find out the basics of the internals of the Model since a full MVC fram
You could have a look at the data mapper model approach referred to by the Zend framework model manual
Then you have a model containing properties, and have a mapper which does the actual database interaction to populate the model.
In my experience different frameworks interpret MVC somewhat loosely and usually with some deviations. However they usually agree that MVC is divided like this:
I use Symfony a lot and can give you a couple of small examples. Mind you, these are hugely simplified. :p
class MyUnitTable extends Doctrine_Table
{
// .. various other pieces of code added by Symfony ... //
public function getForAccount( Account $_account ) {
$q = $this
->createQuery('e')
->leftJoin('e.User u')
->leftJoin('u.Profile p')
->leftJoin('p.Account a')
->where('a.accountid = ?', $_account->getAccountid());
return $q->execute();
}
}
class myUnitActions extends sfActions
{
public function executeIndex(sfWebRequest $request)
{
$this->units = Doctrine_Core::getTable('MyUnit')->getForAccount($foo);
}
}
<div id="my-unit-list-container">
<ul>
<?php foreach( $units as $unit ): ?>
<li><?php echo $unit->getName(); ?></li>
<?php endforeach; ?>
</ul>
</div>
The controller is the place where a request gets handled. It will analyze the request and figure out which action (eg. myUnitActions::executeIndex() from above) in a model that should be called.
I'm sure you can see whats happening in the code above (a lot of convenience is added by the ORM). You have the controller dispatching a request to the model, the model operating withing your problem-domain plus actually pulling data from a database and lastly the view displaying the data.
This has many benefits to you as a developer as it allows for easier and more reliable testing, among other things.
An excellend read for you would be the 21 Days With Jobeet guide from the people behind Symfony. It's a nice piece of work.
You should also have a look at the code for both Symfony and Zend. They are both excellend. Also have a look at a couple of ORM's like Doctrine and Propel.
Also see the Wikipedia article on MVC.
Warning:
The information in this posts is extremely outdated. It represents my understanding of MVC pattern as it was more then 2 years ago. It will be updated when I get round to it. Probably this month (2013.09).
Damn it! (2017.11).
Model
itself should not contain any SQL. Ever. It is meant to only contain domain business logic.
The approach i would recommend is to separate the responsibilities, which are not strictly "business logic" into two other other sets of constructs : Domain Objects and Data Mappers.
For example, if you are making a blog, then the Model will not be Post. Instead most likely the model will be Blog , and this model will deal with multiple Domain Objects
: multiple instances of Post, Comment, User and maybe other objects.
In your model, the domain objects should not know how to store themselves in database. Or even be aware of the existence of any form of storage. That is a responsibility of Data Mappers
. All you should do in the Model is to call $mapper->store( $comment );
. And the data mapper should know how to store one specific type of domain objects, and win which table to put the information ( usually the storage of of single domain object actually affects multiple tables ).
(only relevant fragments from files):
_
in example is protected
from /application/bootstrap.php
/* --- snip --- */
$connection = new PDO( 'sqlite::memory:' );
$model_factory = new ModelFactory( $connection );
$controller = new SomeController( $request , $model_factory );
/* --- snip --- */
$controller->{$action}();
/* --- snip --- */
from /framework/classes/ModelFactory.php
/* --- snip --- */
class ModelFactory implements ModelBuilderInterface
{
/* --- snip --- */
protected function _prepare()
{
if ( $this->_object_factory === null )
{
$this->_object_factory = new DomainObjectFactory;
}
if ( $this->_mapper_factory === null )
{
$this->_mapper_factory = new DataMapperFactory( $this->_connection );
}
}
public function build( $name )
{
$this->_prepare();
return new {$name}( $this->_object_mapper , $this->_data_mapper );
}
/* --- snip --- */
}
file /application/controllers/SomeController.php
/* --- snip --- */
public function get_foobar()
{
$factory = $this->_model_factory;
$view = $this->_view;
$foo = $factory->build( 'FooModel' );
$bar = $factory->build( 'BarModel' );
$bar->set_language( $this->_request->get('lang') );
$view->bind( 'ergo' , $foo );
/* --- snip --- */
}
/* --- snip --- */
file /application/models/FooModel.php
/* --- snip --- */
public function find_something( $param , $filter )
{
$something = $this->_object_factory('FooBar');
$mapper = $this->_mapper_factory('FooMapper');
$something->set_type( $param );
$mapper->use_filter( $filter )->fetch( $something );
return $something;
}
/* --- snip --- */
I hope this will help you understand the separation between DB logic and business logic ( and actually , presentation logic too )
Model should never extend Database or ORM, because Model is not a subset of them. By extending a class, you are declaring that has all the characteristics of the superclass, but with minor exceptions.
class Duck extends Bird{}
class ForestDuck extends Duck{}
// this is ok
class Table extends Database{}
class Person extends Table{}
// this is kinda stupid and a bit insulting
Besides the obvious logic-issues, if your Model is tightly coupled with underlaying Database, it makes the code extremely hard to test (talking about Unit Testing (video)).
I personally think, that ORMs are useless and in large project - even harmful. Problem stems from the fact that ORMs are trying to bridge two completely different ways of approaching problems : OOP and SQL.
If you start project with ORM then, after short learning curve, you are able to write simple queries very fast. But by the time you start hitting the ORM's limitations and problems, you are already completely invested in the use of ORM ( maybe even new people were hired , who were really good at your chosen , but sucked at plain SQL ). You end up in situation where every new DB related issue take more and more time to solve. And if you have been using ORM based on ActiveRecord pattern, then the problems directly influence your Models.
Uncle Bob calls this "technical debt".
loosely related to subject
Obviously you don't want to create a new connection every time you create a new model, so you want to initialize the PDO object separately. (I did run across an in-house MVC framework once where Model extended their database class! As you can imagine it wasn't fast...)
Maybe something like this is what you want?
abstract class Model
{
protected $db = NULL;
public function __construct (PDO $db)
{
$this -> db = $db;
}
}
Example of usage
class Article extends Model
{
public $props = array ();
private function getPage ($id)
{
$q = $this -> db -> prepare ('select * from pages where pageId = :pageId');
$q -> execute (array ('pageId' => $id));
$ret = $res -> fetchAll ();
return ($this -> props = $ret);
}
}
$db = new PDO ('dsn goes here');
$page = new Article ($db);
$page -> getPage (1234);
As for finding examples? Try googling for "php mvc" and looking at the articles that come up