I have read a lot in the past few days about domain objects, data mappers, and a bunch of other stuff I had no idea about.
I have decided to try and implement this in a bit of code I am writing (partly for learning purposes, and partly because I want to create a REALLY simplified framework to build a few projects quickly...with code that I can easily understand and modify).
After reading this and this, I was planning on creating a SINGLE data mapper, with a connection to the DB inside of it, and then use a factory to pass the data mapper into every domain object (well, the ones that would need it). I include some sample code below
class data_mapper {
private $dbh;
function __construct()
{
$this->dbh = new PDO(DB_STRING, DB_USER, DB_PASS);
}
public function createUser($data) ...
public function updateUser($user_id, $data) ...
public function createCategory($data) ...
}
class user {
private $data_mapper;
public $user_id;
public $data;
function __construct($dm)
{
$this->data_mapper = $dm;
}
function someFunction() {
/* some code */
$this->data_mapper->updateUser($user_id, $data);
/* some more code */
}
}
class factory {
private $data_mapper = null;
function __construct($dm)
{
$this->data_mapper = $dm;
}
public function create($name)
{
return new $name($this->data_mapper);
}
}
/* USAGE */
$dm = new data_mapper();
$factory = new factory($dm);
$user = $factory->create('user');
I am left with two questions:
A lot of recent examples I've looked at create a different data_mapper for each model. Should I be doing this? And if I do, wouldn't that make the factory much more complex (i.e. I would need to create single PDO object and pass that into each data mapper, and then pass the right data mapper into each model)?
If my above code exposes some flaw in the understanding of models, data mappers or anything else, please enlighten me (not really a question, i know)...
As far as I can tell, "data mapper" pattern implemented in modern frameworks in the form of prototype Model class, from which all application models are inherited.
In this prototype model you can implement CRUD methods and thus your models will possess it.
Speaking of passing pdo around, local scholars will tell you that you should pass PDO object as constructor parameter. But if you'll take a look at any modern framework - they are using some sort of singleton that contains a PDO instance
So, you want a REALLY simplified PHP framework. Data mappers sound like over-engineering.
Over the years i made a few KISS frameworks in PHP, this is what i did:
Use templates (aka view) such as Smarty. Great for outsourcing your webdesign.
Make a folder named pages (aka controller). Pages are called by index.php only.
Make a folder named models. Only models talk with your DB.
Make a index.php (aka router). Has a ?page=dog parameter.
Strict MCV (aka MVC) terminology is not the holy grail, the above is a nice implementation for a simple website/app/CMS.
The parts
/pages/page_dog.inc.php
A page loads the model(s) he needs, manipulates and shows it:
<?php if(!defined('YOURFRAMEWORK')){die('External access denied');}
// Page init
require './models/model_dog.inc.php';
$id = $_GET['id']; // todo fix injection attacks
$ModelDog = new ModelDog($DB);
// Page main
$ModelDog->Load($id);
echo $ModelDog->item['breed'];
For listings (a page where user selected the $id) you may not want seperate models representing each result. Make a lister class instead, much like the model but returning multiple items in one array. Its tempting to DRY and make the ListerDog class use the ModelDog but there is no readability gain just performance pain.
/index.php (aka router) calls a page (via require_once()) after auth and init ($DB):
<?php
define('YOURFRAMEWORK', 1); // disable "External access denied" error.
require_once('config.inc.php'); // todo have this hold the $config[] array.
$DB = #new mysqli( // or your derative, so you can log each query() call.
$config['db']['host'],
$config['db']['user'],
$config['db']['pasw'],
$config['db']['database']
);
if ($DB->connect_error) { die('db error: ' . mysqli_connect_errno()); }
// Load page requested by user. For now, its dog hardcoded.
require_once('./pages/page_dog.inc.php');
$DB->close;
/models/model_dog.inc.php (aka model) talks to the DB for you, processes and sanitizes data. I also use this put form processing functions.
<?php if(!defined('YOURFRAMEWORK')){die('External access denied');}
class ModelDog extends BaseModel {
private $tablename = 'dogs';
/**
* Load last (or specific) item.
* #param integer $id
* #return boolean Returns false when failed.
*/
public function Load($id=null) {
$query = "SELECT * FROM `".$this->tablename."` WHERE `id`='".$this->DB->Sanitize($id)."';";
// TODO .. $this->item =
}
public function ItemDefaults() {
return array(
'id' => 0,
'breed' => 'unknown',
'height' => 0
);
}
// TODO ..
}
/models/basemodel.inc.php extend every model class from something common like:
abstract class BaseModel
{
protected $item = array(); // Here is all the data!
protected $DB = null;
public function __construct($aQDB) {
parent::__construct();
$this->DB = $aDB;
$this->Reset();
}
public function Reset() {
$this->item = ItemDefaults();
}
public function Item() { return $item; }
// As seen in dog
abstract public function Load($id);
abstract public function ItemDefaults();
// But descendants (models) must also implement:
abstract public function Save($id = NULL);
abstract public function Delete($id);
// You may want to add Validate() and other internal things here.
}
All of the above is a bare-minimum version of what i build myself when i need another tiny framework. You gain more in proper subclassing than making more classes doing more things. A website is a simple thing in essence, until one overcomplicates it..
The gist / TLDR;
If you really want a REALLY simplified PHP framework, dont read to much. Just write code and you'll discover what it needs to make you work better.
Related
In an application I'm building there's a CLI entry point class:
class CLIEntryPoint {
protected $factory;
public function __construct(ApplicationObjectFactoryInterface $factory) {
$this->factory = $factory;
}
public function run(...$args) {
$choice = $args[1];
$appObject = $this->factory->makeApplicationObject($choice);
$appObject->doApplicationRelatedStuff();
}
}
This entry point is created using Dependency Injection in my "front controller" script and it receives an ApplicationObjectFactoryInterface implementation (actually the current implementation of ApplicationObjectFactoryInterface is injected by the DI container, which in turn reads it from its configuration file, but that's not the point).
The current implementation of ApplicationObjectFactoryInterface also uses DI and depends on other factories which help it building the resulting application object:
class CurrentImplementationOfApplicationObjectFactory implements ApplicationObjectFactoryInterface {
protected $someComponentFactory;
protected $anotherComponentFactory;
public function __construct(SomeComponentFactoryInterface $someComponentFactory, AnotherComponentFactoryInterface $anotherComponentFactory) {
$this->someComponentFactory = $someComponentFactory;
$this->anotherComponentFactory = $anotherComponentFactory;
}
/**
* Interface's method
*
* #return ApplicationObjectInterface
*/
public function makeApplicationObject($choice) {
$component = $this->someComponentFactory->makeSomeComponent();
$anotherComponent = $this->anotherComponent->makeAnotherComponent();
switch ($choice) {
case 1:
return new CurrentImplementationOfApplicationObject1($component, $anotherComponent);
case 2:
return new CurrentImplementationOfApplicationObject2($component, $anotherComponent);
default:
return new DefaultImplementationOfApplicationObject($component, $anotherComponent);
}
}
}
Here CurrentImplementationOfApplicationObject1, CurrentImplementationOfApplicationObject2 and DefaultImplementationOfApplicationObject all implement the ApplicationObjectInterface interface and therefore they all have the doApplicationRelatedStuff method.
I would like to know whether it's good practice or not to write code like I did and if not how can I improve it.
Basically here I am creating a component which depends on other components in order to function properly using a factory which in turn needs inner factories to build the component which implements the ApplicationObjectInterface interface.
Is it considered good practice?
Thanks for the attention, as always!
EDIT: I looked at the article of Steven and tried to refactor CLIEntryPoint. The only problem now seems to be how to pass the $choice parameter to the factory which now is inside of the proxy when the run() method is called. Is this code structure better than the one I posted above? Of course, SomeComponentFactoryInterface and AnotherComponentFactoryInterface should follow the same behaviour (the factory that uses them should not use them directly, but through two proxies which implement, in order, SomeComponentInterface and AnotherComponentInterface). I hope I get it right, anyway, here is the code:
class CLIEntryPoint {
protected $applicationObject;
public function __construct(ApplicationObjectInterface $applicationObject) {
$this->applicationObject = $applicationObject;
}
public function run(...$args) {
$choice = $args[1]; // How do I deal with different choices when I am using a Proxy? I should have different application objects depending on input.
$this->applicationObject->doApplicationRelatedStuff();
}
}
interface ApplicationObjectInterface {
public function doApplicationRelatedStuff();
}
class ApplicationObjectProxy implements ApplicationObjectInterface {
protected $applicationObjectFactory;
protected $applicationObjectImplementation = NULL;
public function __construct(ApplicationObjectFactoryInterface $factory) {
$this->applicationObjectFactory = $factory;
}
public function __call($method, $args) {
// Calling interface's
$implementation = $this->getImplementation();
$methodOfInterfaceToCall = preg_replace('/Proxy$/', '', $method);
return $implementation->{$methodOfInterfaceToCall}(...$args);
}
/**
* Laxy loading method.
*/
protected function getImplementation() {
if (is_null($this->applicationObjectImplementation)) {
$this->applicationObjectImplementation = $this->applicationObjectFactory->makeApplicationObject(); // Choice should go here somehow...
}
return $this->applicationObjectImplementation;
}
public function doApplicationRelatedStuff() {
// This will call the PHP's magic `__call` method, which in turn will forward the call to the application object's
// implementation returned by the factory.
return $this->doApplicationRelatedStuffProxy();
}
}
Actually yes, this is a pattern called the Abstract Factory Pattern. So an example that I used to present it in front of my class during my undergrad:
So if you are building a video game first person shooter, you might want to create three concrete factories like:
FlyingMonsterFactory
SwimmingMonsterFactory
WalkingMonsterFactory.
All these factories would implement an abstract MonsterFactory.
With this, you can have your video game create a level in which you want waves of the same type of monsters, so you can have a randomWaveMonsterGenerator method return a MonsterFactory which might have returned a concrete SwimmingMonsterFactory. So then you will have a wave of SwimmingMonster(s) generated by the SwimmingMonsterFactory.
So answer your description more directly, looking at your code above, you asked the question on choice for Dependency Injection. With Dependency Injection, I believe for this type of pattern, you will have to inject every concrete class before your code even attempts to get the implementation class.
So for example:
Your code above says the run method gives an argument called
choice.
With this choice, you will have to use it as a parameter into a getImplementation method.
All the concrete objects that the getImplementation method that rely upon Dependency
Injection have to be created BEFORE you call the getImplementation method.
But since you don't know which implementation class will be called, I believe you have to inject ALL the implementation classes before hand.
Then you can use the choice variable as a parameter to get the correct implemented factory class.
Hope this helps!
I'm moving onto teaching myself OOP in PHP.
I'm creating a couple of little web apps and have followed a lot of tutorials that either create the database (using PDO) via a Singleton, or via passing the global around. I've read that these are pretty much the same thing and are both to be avoided like the plague.
So I've watched the Google Tech Talks on clean code, and read almost every SO article on dependency injection and the like. I have a couple of questions.
The clean code videos suggest you shouldn't do 'work' in your constructors. Is this 'work' in reference to business logic. Ie. If my class's job is to create another object, is that an OK kind of 'work'?
For example, in trying to conform to single repsonibility classes I created three.
Class DB - which actually connects to the database.
Class DBFactory - which creates the DB object which connects to the database.
Class DBInstance - which returns a single instance of the DBFactory created PDO object.
Please note that I'm trying to create a single instance, without creating a Singleton pattern.
So I try and pass my dependencies for each class up the chain. I find myself in a position where I have to create all of the objects (from DB down) so I can inject the dependencies. For some reason I thought it would work the other way, I'd create the first object, which would create the second for me etc. I'm clearly missing something?
Hopefully this helps others as well - there seems to be a myriad of questions relating to this stuff and databases but very little good examples.
(I should mention this does work, I do get a list of hotel names out of the database!)
TestCode.php
include './classes/DB.php';
include './classes/DBFactory.php';
include './classes/DBInstance.php';
include './classes/Location.php';
$db = new DB;
$dbfactory = new DBFactory($db);
$dbinstance = new DBInstance($dbfactory);
$dbh = $dbinstance->getDbInstance();
//Example business logic
$location_names = Location::getLocationNames($dbh);
print_r($location_names);
Class DB.php:
class DB {
private $_dbhost = 'myhost';
private $_dbname = 'myname';
private $_dbuser = 'myuser';
private $_dbpass = 'mypass';
private $_error;
public function connect() {
try {
return new PDO("mysql:host=$this->_dbhost;dbname=$this->_dbname",
$this->_dbuser, $this->_dbpass);
}
catch (PDOException $e) {
$this->_error = 'Error! ' . $e->getMessage() . '<br />';
die();
}
}
public function getError() {
if (isset($this->_error)) {
return $this->_error;
}
}
}
Class DBFactory.php
class DBFactory {
private $_dbh;
public function __construct(DB $db) {
$this->_dbh = $db;
}
public function Create() {
return $this->_dbh->Connect();
}
}
Class DBInstance.php
class DBInstance {
private static $_dbinstance;
public function __construct(DBFactory $dbfactory) {
if (!isset(self::$_dbinstance)) {
self::$_dbinstance = $dbfactory->Create();
}
}
public function getDbInstance() {
return self::$_dbinstance;
}
}
Your code seems to do what you want it to.. but maybe we can use less object instantiation using inheritance and maybe we can avoid static properties in instanciated classes.
Also in regard to using a pattern of dependency injection that is able to handle multiple connections, but support using a single instance of it. exemple first, classes after
$params = array
('host'=>'localhost',
'db'=>'ice',
'user'=>'kopitar',
'pass'=>'topnet',
'charset'=>'utf8'); // passing the charset explicitely is great
$handle = new handle($params);
$db = $handle->getInstance();
we can either pass the $db to our functions
$location_names = Location::getLocationNames($db);
or the whole $handle. as long as $handle is not reconstructed, it will always return the same database connection.
$location_names = Location::getLocationNames($handle);
if I want to reconstruct I need the whole $handle
$handle->__construct(/* params but with another database infos */);
$db2 = $handle->getInstance();
As for the classes, I think we want the params to arrive from the instanciated class, so we can change them later.
class db {
function __construct($params) {
foreach ($params as $param => $value) {
$this->{$param} = $value; // assigns the connections infos
}
}
protected function connect() {
$dsn = 'mysql:host='.$this->host.';dbname='.$this->db.';charset='.$this->charset;
return new PDO($dsn,$this->user,$this->pass);
}
}
the factory creates a connection from params and passes it to something else, good factory
class factory extends db {
protected function create() {
return $this->connect();
}
}
now we want to have our object to keep it's connection as long as we do not rebuild it. so we give it to instance
class instance extends factory {
function instantiate() {
$this->instance = $this->create();
}
}
and last but not least, our handle which returns the instance. it could be in instance class.....................
but I feel like having four and find no real reason not to.
class handle extends instance {
function __construct($params) {
db::__construct($params);
$this->instantiate(); // when we construct a handle, we assign an instance to the instance property
}
function getInstance() {
return $this->instance;
}
}
KISS
Don't make things more complex than they are, of course this is just my opinion, but as I see it you are building a complex solution for a problem that someone else says might exist is some cases.
Php is not multi threaded so there goes one of the biggest arguments overboard. (in very rare-occasions it might be)
I'm using singletons for my database connections for about 15 years now and never ever had a problem with them, I do play around with different connections having one singleton handle several connection instances, but whatever... it works great and everyone that looks at the code.. understands it directly.
I'm not using globals because they can be overwritten and are kind of hard to predict (when it holds the correct object, and when/why they don't)
Use OOP to make your code cleaner, easier to work with and more flexible.
Don't use it to fix problems that aren't there and make your code more complex because others tell you to.
An very simple example of a db-connection singleton class handling several different connections.
class singleton{
private static $_instances=array();
public static function getInstance($connectionName){
if(!isset(self::$_instance[$connectionName]){
self::$_instance[$connectionName]=self::_getConnection($connectionName);
}
return self::$_instance[$connectionName];
}
}
just my 2 cents
Why do you have a factory if you have a singleton? This is needless.
This is a never-ending debate, but I'm advocate of do not use singletons for database connections.
As far as in most applications, you have only one data channel, you can consider your database connection unique, but this might not be always true.
In deed, the effort made to create a singleton database connection is even bigger than just create a regular one.
Also, your class DB is not configurable, therefore, you need to change it when your connection parameters change. And I think DB is a very bad name for this.
I'd rather call this Storage and do something like:
inteface Storage {
public function insert($container, array $data);
public function update($container, array $data, $where);
public function delete($container, $where);
public function getAll($container);
public function getOne($identifier);
}
final class PdoStorage implements Storage {
private $dbh;
private $dsn;
private $user;
private $pswd;
public function __construct($dsn, $user, $pswd) {
$this->dsn = $dsn;
$this->user = $user;
$this->pswd = $pswd;
}
// Lazy Initialization
private function connect() {
if ($this->dbh === null)
$this->dbh = new PDO($this->dsn, $this->user, $this->pswd);
}
public function insert($container, array $data) {
$this->connect();
// ... omitted for brevity
}
}
Now, when you need a database storage, you do:
$someObj = new SomeClass(new PdoStorage(...));
Now you might be wondering if you will need to create an PdoStorage for each single object that depends on it.
The answer is: no!
Now you can use a factory to simplify your life.
class SomeFactory {
private $defaultStorage;
public function __construct(Storage $storage) {
$this->defaultStorage = $storage;
}
public function create($type) {
// Somehow fetches the correct class to instantiate and put it into $class variable , for example... and then
return new $class($this->defaultStorage); // Or you'd better do this with reflection
}
}
$factory = new SomeFactory(new PdoStorage(...));
$factory->create('SomeClass');
This way, you can have just one database connector or more if you need.
I'm writing my own OOP framework, partially as a learning exercise, but knowing this codebase isn't going to disappear - only evolve as I learn. Below is some psuedocode of what my setup looks like. In this setup, how would I share DB connections? Do a ctrl+f for "connection" to find the places in the code I'm specifically unsure of how to structure.
A routing file would look like this:
switch($urlParameters['action']){
case 'lading':
//etc etc
break;
case 'userCP':
//etc etc
break;
case 'dashboard':
default:
$page = new dashboard();
$page->route($urlParameters);
break;
}
A page would look like
<?php
abstract class page{ // Page is a mixture of controller and view. Most view logic is frontend, so I do all my controller-view logic here
public var $models;
public var $session;
public var $user;
function __construct(){
$this->models = new models();
$this->session = $this->models->session(new MongoID($_COOKIE['session'])); // Plus some code to prevent session hijacking
$this->user = $this->models->users($this->session->userID);
}
function outputMongoData(){} // Wrapper function used by pages to output Mongo datatypes as regular json (Dates to RFC, IDs to string, rounding floats, etc)
function template($file, $data){} // This uses output buffering and extract() to use php its self as the templating language.
}
class dashboard extends page{
public var $dashboardView;
function __construct(){
parent::__construct();
$this->dashboardView = $this->models->dashboardView($this->user->dashboardViewID);
}
function route($urlParams){
$this->session->updatePageViewCountOrSomething();
echo $this->template('dashboard.php', [
'importantData' => $this->dashboardView->someTypeOfDataFromDashboardView,
'viewCount' => $this->session->pageViewCount
]);
}
}
And the models look like
<?php
class models{
private var $cache = [
'users' => [],
'dashboardViews' => [],
'sessions' => [],
];
// Cache to prevent collisions since we're doing save() vs individual updates, plus saves overhead of querying multiple times (potentially hundreds of times for certain models)
function __construct(){
// Potential connection sharing stuff here
}
private function modelFactory($collection, $modelName, $identifier){
$stringID = (string) $identifier;
if(!isset($this->cache[$collection][$stringID])){
$this->cache[$collection][$stringID] = new $modelName($identifier);
}
return $this->cache[$collection][$stringID];
}
public function session(MongoID $sessionID){
return $this->modelFactory('sessions', 'session', $sessionID);
}
public function user(MongoID $userID){
return $this->modelFactory('users', 'user', $userID);
}
public function dashboardView(MongoID $dashboardViewID){
return $this->modelFactory('dashboardViews', 'dashboardView', $dashboardViewID);
}
}
abstract class model{
public var $db;
public var $collection;
private var $data = [];
function __construct($collectionName){
$this->db = // ??? Not sure how to connect so that I don't have a new connection for every single model..
$this->collection = $this->db->{$collectionName};
}
function __destruct(){
$this->collection->save($this->data);
}
function __get($fieldName){
return $this->data[$fieldName]; // if isset, etc etc
}
}
class session extends model{
// Explanation of schema here
function __construct(MongoID $sessionID){
parent::__construct('sessions');
$this->data = $this->collection->findOne(['_id' => $sessionID]); // or if not found, create..
}
function updatePageViewCountOrSomething(){
$this->data['pageViewCount'] += 1;
$this->data['orSomething'] = ['something' => 'or another'];
}
}
class dashboardView extends model{
// Explanation of schema here
function __construct(MongoID $dashboardViewID){
parent::__construct('dashboardViews');
$this->data = $this->collection->findOne(['_id' => $sessionID]); // or if not found, create..
}
function addColumns(){}
function reorderColumns($newOrder){}
}
From your example, it looks like the models class is functioning as a factory for your model instances. In that case, you could use it as the connection container and then pass the appropriate MongoCollection instance to each model class instead of the collection name.
I'll also note that in the PHP driver, there is not really a concern with constructing multiple MongoClient objects. The driver uses persistent connections internally, so constructing five MongoClient instances all with the same host/port/user/password combination will still only create a single network socket behind the scenes. You can test this out for yourself using the example in this slide and the one following.
Some general concerns with your model implementation:
$data is private to the base model class, but you access it in the child classes. It should probably be protected.
Waiting until __destruct() to save an object back to the database seems like a bad idea, as it could happen at the end of the script or sometime earlier if the object is garbage-collected. Having a predictable point in the application to write data seems preferable.
Instead of using this model for persisting session data, you can read up on the SessionHandlerInterface in PHP 5.4+. That allows you to implement a custom storage backend (MongoDB in this case) to be used by the standard PHP session API (e.g. $SESSION).
A great starting point before creating a framework (even as a thought exercise), would be to read up on design patterns. Anthony Ferrara's blog is a great resource and starting point for the subject. I would also suggest learning about dependency injection and service containers, as that's very relevant to your original question about sharing a common resource (e.g. database connection). Fabien Potencier did a great blog series on the subject. The article is a few years old and references the Symfony 1.x implementation (Symfony2 is the current model he uses), but the early posts will likely serve as a useful introduction.
This is my problem, I have a tiny PHP MVC framework i built.
Now when Im in Controller, i should be able to load a Model.
I have Models named like database tables like Users.php, Pages.php etc.
All Controllers extend BaseController, and all Models extend BaseModel, this way I can have some methods available to all Controllers. Like from any Controller I can use loadModel method like this:
$usersModel = $this->loadModel("Users");
Now $usersModel will be object that represents users database table, and from there I should open database connection, and fetch, update, delete stuff from users table.
To get database connection from my Models, baseModel has method loadDatabase() and I use it like this:
$database = $this->loadDatabase();
This would return class that is thin layer around PDO so from there I can use something like this:
$data = $database->fetchAssoc("SELECT * FROM users WHERE name = ?", array("someone"));
This is how I would return some $data from Model to the Controller.
So basicly, Controller can load a model, and then call methods on that model that would return some data or update or delete etc.
Now, Controller can load more then one model. And each model should load database, and this is where it gets complicated. I need only one connection to the database.
This is how loadDatabase method looks:
public function loadDatabase()
{
// Get database configuration
require_once("Application/Config/Database.php");
// Build configuration the way Database Object expects it
$dns = "{$db_config['db_driver']}:host={$db_config['db_host']};dbname={$db_config['db_name']}";
$username = $db_config['db_user'];
$password = $db_config['db_pass'];
$options = $db_config['db_options'];
// Return new Database object
return new \Core\Database\Database($dns, $username, $password, $options);
}
So before I load a Database object, i must load configuration for database connection, as Database objects __constructor expects it. Then I instanciate new database object and return it.
Now Im stuck and I dont know is this the right way to loadDatabase from model?
How should I set this up, how should I load database from inside the model so there is always only one instance of database object. Beacuse if I do something like this from Controller:
$users = $this->loadModel("Users");
$pages = $this->loadModel("Pages");
$users->doSomethingThatNeedsDatabase();
$users->doSomethingThatNeedsDatabase();
$pages->doSomethingThatNeedsDatabase();
I would create 3 database objects :(
So my question is, how should I load Database from inside the Models, and how should that method look in BaseModel?
What if I would like to use 2 databases, I will have some big problems with this setup.
How can I achive this without using singleton pattern?
At the moment, I also have something like this:
public function getDatabase()
{
$reg = \Core\Registry\Registry::getInstance();
$db = $reg->getObject("database");
if(!isset($db))
{
require_once("Application/Config/Database.php");
$dns = "{$db_config['db_driver']}:host={$db_config['db_host']};dbname={$db_config['db_name']}";
$username = $db_config['db_user'];
$password = $db_config['db_pass'];
$options = $db_config['db_options'];
$db = new \Core\Database\Database($dns, $username, $password, $options);
$reg->setObject('database', $db);
}
return $reg->getObject('database');
}
This is Registry pattern, where I could hold shared objects. So when Model asks for DB connection I could check if DB Class is in Registry, and return it, if not I would instaciate and then return... The most confusing thing is that I need to load configuration array...
So what is the best way, to load Database from Models?
Thanks for reading, this was a very long question, but this is bothering me so much, i hope someone could help me with this one... Thanks!
You are going in the wrong way about solving this.
Instead of each time manually making a new "Model" and then configuring it, you should create a structure that does it for you ( extremely simplified version ):
class ModelFactory
{
protected $connection = null;
// --- snip --
public function __construct( $connection )
{
$this->connection = $connection;
}
// --- snip --
public function buildMapper( $name )
{
$instance = new {$name}( $this->connection );
return $instance;
}
// --- snip --
}
This class you would be using in index.php or bootstrap.php , or any other file you use as entry point for your application:
// at the bootstrap level
$modelFactory = new ModelFactory( new PDO(...) );
// i am assuming that you could get $controllerName
// and $action from routing mechanism
$controller = new {$controllerName}( $modelFactory );
$controller->{$action}();
The main problem you have is actually cause by misunderstanding what Model is. In a proper MVC the Model is a layer, and not a specific class. Model layer is composed from multitude of class/instances with two major responsibilities:
domain business logic
data access and storage
The instances in first group are usually called Domain Objects or Business Objects (kinda like situation with geeks and nerds). They deal with validations, computation, different conditions, but have no clue how and where information is stored. It does not change how you make an Invoice , whether data comes from SQL, remote REST API or a screenshot of MS Word document.
Other group consists mostly of Data Mappers. They store and retrieve information from Domain Objects. This is usually where your SQL would be. But mappers do not always map directly to DB schema. In a classic many-to-many structure you might have either 1 or 2 or 3 mappers servicing the storage. Mappers usually one per each Domain Object .. but even that is not mandatory.
In a controller it would all look something like this.
public function actionFooBar()
{
$mapper = $this->modelFactory->buildMapper( 'Book' );
$book = $this->modelFactory->buildObject( 'Book' );
$patron = $this->modelFactory->buildObject( 'Patron' );
$book->setTitle('Neuromancer');
$mapper->fetch( $book );
$patron->setId( $_GET['uid']);
if ( $book->isAvailable() )
{
$book->lendTo( $user );
}
$mapper->store( $book );
}
Maybe this will give you some indications for the direction in which to take it.
Some additional video materials:
Advanced OO Patterns (slides)
Global State and Singletons
Don't Look For Things!
Best way for these models to use dependency injection pattern.
public function loadModel() {
$db = $this->loadDatabase();
$model = new Model();
$model->setDatabase($db);
return $model;
}
where loadDatabase() should once init and after return simple instance of database connection.
Try this:
class Registry
{
/** #return Registry */
public static function getInstance() {}
public function getObject($key) {}
public function setObject($key, $value) {}
/** #return bool */
public function isObjectExists($key) {}
}
class Db
{
const DB_ONE = 'db_one';
const DB_TWO = 'db_two';
protected static $_config = array(
self::DB_ONE => array(),
self::DB_TWO => array(),
);
public static function getConnection($dbName) {}
}
abstract class BaseModel
{
abstract protected function _getDbName();
protected function _getConnection()
{
$dbName = $this->_getDbName();
if (!Registry::getInstance()->isObjectExists($dbName)) {
Registry::getInstance()->setObject($dbName, Db::getConnection($dbName));
}
return Registry::getInstance()->getObject($dbName);
}
}
class UserModel extends BaseModel
{
protected function _getDbName()
{
return Db::DB_ONE;
}
}
class PostModel extends BaseModel
{
protected function _getDbName()
{
return Db::DB_TWO;
}
}
Good luck!
Below I present three options for simplifying my database access when only a single connection is involved (this is often the case for the web apps I work on).
The general idea is to make the DB connection transparent, such that it connects the first time my script executes a query, and then it remains connected until the script terminates.
I'd like to know which one you think is the best and why. I don't know the names of any design patterns that these might fit so sorry for not using them. And if there's any better way of doing this with PHP5, please share.
To give a brief introduction: there is a DB_Connection class containing a query method. This is a third-party class which is out of my control and whose interface I've simplified for the purpose of this example. In each option I've also provided an example model for an imaginary DB "items" table to give some context.
Option 3 is the one that provides me with the interface I like most, but I don't think it's practical unfortunately.
I've described the pros and cons (that I can see) of each in the comment blocks below.
At the moment I lean towards Option 1 since the burden is put on my DB wrapper class instead of on the models.
All comments appreciated!
Note: For some reason, the Stack Overflow preview is showing an encoded HTML entity instead of underscores. If the post comes through like that, please take this into account.
<?php
/**
* This is the 3rd-party DB interface I'm trying to wrap.
* I've simplified the interface to one method for this example.
*
* This class is used in each option below.
*/
class DB_Connection {
public function &query($sql) { }
}
/**
* OPTION 1
*
* Cons: Have to wrap every public DB_Connection method.
* Pros: The model code is simple.
*/
class DB {
private static $connection;
private static function &getConnection() {
if (!self::$connection) {
self::$connection = new DB_Connection();
}
return self::$connection;
}
public static function &query($sql) {
$dbh = self::getConnection();
return $dbh->query($sql);
}
}
class Item {
public static function &getList() {
return DB::query("SELECT * FROM items");
}
}
/**
* OPTION 2
*
* Pros: Don't have to wrap every DB_Connection function like in Option 1
* Cons: Every function in the model is responsible for checking the connection
*/
class DB {
protected static $connection = null;
public function connect() {
self::$connection = new DB_Connection();
}
}
class Item extends DB {
public static function &getList() {
if (!self::$connection) $this->connect();
return self::$connection->query("SELECT * FROM items");
}
}
/**
* OPTION 3
*
* Use magic methods
*
* Pros: Simple model code AND don't have to reimplement the DB_Connection interface
* Cons: __callStatic requires PHP 5.3.0 and its args can't be passed-by-reference.
*/
class DB {
private static $connection = null;
public static function &getConnection() {
if (!self::$connection) {
self::$connection = new DB_Connection();
}
return self::$connection;
}
public static function __callStatic($name, $args) {
if (in_array($name, get_class_methods('DB_Connection'))) {
return call_user_func_array(
array(self::getConnection(), $name), $args);
}
}
}
Based on your examples above, I'd say option 1 is the best - simplicity always wins, and you can handle a failed connection differently depending on the method (you might want to fail differently for a stored procedure call than a simple SELECT, for instance).
Semantically speaking I think option 1 makes the most sense, if you're treating DB as a resource then the DB_Connectioin is an object that it uses but not necessarily the object itself.
However, several things I caution you against. First, don't make your DB class have all static methods as it will strongly impact your ability to test your code. Consider instead a very simple inversion of control container like this:
class DB {
private $connection;
public function &query($sql) {
return $connection->query($sql);
}
public __construct(&$db_connection) {
$this->connection = $db_connection;
}
}
class Item {
public function &getList() {
return ResourceManager::getDB()->query("SELECT * FROM items");
}
}
class ResourceManager {
private $db_connection;
private function &getDbConnection() {
if (!$this->connection) {
$this->connection = new DB_Connection();
}
return $this->connection;
}
private $db;
public static function getDB() {
if(!$this->db) $this->db = new DB(getDbConnection());
return $this->db;
}
There are significant benefits. If you don't want DB to be used as a singleton you just make one modification to ResourceManager. If you decide it should not be a singleton - you make the modification in one place. If you want to return a different instance of DB based on some context - again, the change is in only one place.
Now if you want to test Item in isolation of DB simply create a setDb($db) method in ResourceManager and use it to set a fake/mock database (simplemock will serve you well in that respect).
Second - and this is another modification that this design eases - you might not want to keep your database connection open the entire time, it can end up using far more resources than need be.
Finally, as you mention that DB_Connection has other methods not shown, it seems like the it might be being used for more than simply maintaining a connection. Since you say you have no control over it, might I recommend extracting an interface from it of the methods that you DO care about and making a MyDBConnection extends DB_Connection class that implements your interface. In my experience something like that will ultimately ease some pain as well.