Following from my previous post about removing ServiceLocatorAwareInterface's from my zf2 app, i am now faced with a puzzle involving object creation when using data mappers.
The current implementation of my data mapper uses a tablegateway to find specific rows, calls the service manager to obtain a domain object, then populates and returns the full object.
public function findById($userId){
$rowset = $this->gateway->select(array('id' => $userId));
$row = $rowset->current();
if (!$row) {
throw new \DomainException("Could not find user with id of $userId in the database");
}
$user = $this->createUser($row);
return $user;
}
public function createUser($data){
$userModel = $this->getServiceManager()->get('Model\User');
$hydrator = $this->getHydrator();
if($data instanceof \ArrayObject){
$hydrator->hydrate($data->getArrayCopy(), $userModel);
}else{
$hydrator->hydrate($data, $userModel);
}
return $userModel;
}
The model needs to be called from the service manager because it has other dependencies, so calling $user = new App\Model\User() from within the mapper is not an option.
However, now i am removing instances of the servicemanager from my code, i am unsure of the best way to get the model into the mapper. The obvious answer is to pass it in the constructor and save the instance as a property of the mapper:
public function __construct(TableGateway $gateway, \App\Model\User $userModel){
$this->_gateway = $gateway;
$this->_userModel= $userModel;
}
public function createUser($data){
$userModel = $this->_userModel;
//....snip....
}
This works to a degree, but then multiple calls to createUser (such as when finding all users, for instance) over writes each instance with the last objects data (as to be expected, but not what i want)
So i need a "new" object returned each time i call createUser, but the dependency being passed into the constructor. With the model passed into the constructor I can clone the object eg.
public function createUser($data){
$userModel = clone $this->_userModel
//....snip....
}
...but something about it doesn't seem right, code smell?
You are right, it doesn't smell good.
Designing an ORM isn't easy. There is and probably always will be discussion about the way an ORM should be designed. Now, when I'm trying to understand your design I noticed you are pointing out that your models contain the data but also have "other" dependencies. This is wrong, the models containing your data should work without any layer in your application.
Entities should work without the ORM
In my opinion you should separate your business logic (dependencies) from your data. This will have many advantages:
More expressive
Easier to test
Less coupling
More flexible
Easier to refactor
For more information about how to design your ORM layer I highly recommend browsing through these slides.
DataMaper
Lets make the UserMapper responsible for separating the in-memory objects (containing only data) from the database.
class UserMapper
{
protected $gateway;
protected $hydrator;
public function __construct(TableGateway $gateway, HydratorInterface $hydrator)
{
$this->gateway = $gateway;
$this->hydrator = $hydrator;
}
public function findOneById($id)
{
$rowset = $this->_gateway->select(array('id' => $id));
$row = $rowset->current();
if(!$row) {
throw new \DomainException("Could not find user with id of $id in the database.");
}
$user = new User;
$this->hydrator->hydrate($row, $user);
return $user;
}
public function findManyBy(array $criteria)
{
// $criteria would be array('colum_name' => 'value')
}
public function save(User $user)
{
$data = $this->hydrator->extract($user);
// ... and save it using the $gateway.
}
}
For more information about the responsibility of data mappers check out Martin Fowler's definition.
Buniness Logic
It's recommended not to place any model related business logic directly into the Controller. Therefor lets just create a simple UserService which will handle validation. If your fond of form objects you could also use Zend\Form\Form in this process.
class UserService
{
protected $inputFilter;
protected $hydrator;
public function __construct(InputFilter $inputFilter, HydratorInterface $hydrator)
{
$this->inputFilter = $inputFilter;
$this->hydrator = $hydrator;
}
protected function validate(array $data)
{
// Use the input filter to validate the data;
}
public function createUser(array $data)
{
$validData = $this->validate($data);
$user = new User;
$this->hydrator->hydrate($validData, $user);
return $user;
}
}
Data Object
Now lets make the objects containing the data Plain Old PHP Objects, not bound by any restriction. This means they are not coupled with any logic and we could use them anywhere. For instance if we decide to replace our ORM with another like Doctrine.
class User
{
protected $name;
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
}
More information about the concept of Plain Old PHP Objects can be found on Wikipedia's explanation of POJO.
Related
I'm just getting to grips with OOP, so sorry if this question seems a little all over the place, its how my head is feeling right now.
I have looked at constructors in the PHP docs but it does not seem to cover dependency injection.
I have a class called DatabaseLayer, this class simply creates a connection to my database
//php class for connecting to database
/**
* Class DatabaseLayer - connects to database via PDO
*/
class DatabaseLayer
{
public $dbh; // handle of the db connexion
/**
* #param $config Config- configuration class
*/
private function __construct(Config $config)
{
$dsn = $config->read('db.dsn');
$user = $config->read('db.user');
$password = $config->read('db.password');
$this->dbh = new \PDO($dsn, $user, $password);
}
public function getConnection()
{
if ($this->dbh)
{
return $this->dbh;
}
return false;
}
}
Q1: i have private function __construct(Config $config)
I'm not sure i fully understand the reason of having __construct(Config $config) instead of just using __construct($config)
Does (Config $config) automatically create $config as a new Config instance?
or do i have to do the following:
$config = new Config();
$dbLayer = new DatabaseLayer($config);
I want to extend the DatabaseLayer class and include methods for interacting with the database relating to my GameUser which is another class i have created, when i extend the DatabaseLayer class i need to inject the GameUser class
I know my new class DatabaseLayerUser inherits the methods and properties from the DatabaseLayer class.
Q2: as the new DatabaseLayerUser class is based on the 'DatabaseLayer' class it needs to have the Config class, because i used __construct(Config $config) does this get it automatically?
or do i have to pass both the Config and GameUser to DatabaseLayerUser
class DatabaseLayerUser EXTENDS DatabaseLayer
{
private $config; /** #var Config */
private $user; /** #var GameUser */
/**
* #param Config $config
* #param GameUser $user
*/
private function __construct(Config $config, GameUser $user){
$this->config = $config;
$this->user = $user;
}
/**
* profileExists - Checks to see if a user profile exists
* internal #var $PDOQuery
* #var $PDOStatement PDOStatement
* #throws Exception - details of PDOException
* #return bool
*/
private function profileExists()
{
try{
$PDOQuery = ('SELECT count(1) FROM userprofile_upl WHERE uid_upl = :uid');
$PDOStatement = $this->dbh->prepare($PDOQuery);
$PDOStatement->bindParam(':uid', $this->_userData->uid);
$PDOStatement->execute();
return $PDOStatement->fetchColumn() >0;
}catch(PDOException $e){
throw new Exception('Failed to check if profile exists for '.$this->_userData->uid, 0, $e);
}
}
}`
Q3: i saw there is a parent::__construct();
does this mean i should use:
private function __construct(Config $config, GameUser $user){
parent::__construct($config);
$this->user = $user;
}
I think you're really mixing up means and purposes here. The goal is never to use dependency injection per se, but to solve programming problems that you encounter with it. So let's first try and correct the dependencies between the classes and their responsibilities a little.
In my opinion, it is best to start this excercise from the application domain, start small, and then refactor if you want to introduce abstraction. At least when learning or as a thought experiment. When more experienced and when implementing the real thing, it is probably smarter to look a couple of steps ahead.
So for now I'll simply assume you're making an online game, with different users that are represented by GameUser objects, and no further than that.
The sole responsibility of the GameUser class should be to represent the domain data and logic, i.e.: it contains a couple of properties (let's say username and score) and methods (let's say incrementScore) that have to do with the application domain itself (game users). It should not be responsible for practical implementation details, most notably: how it gets persisted (written to file/db/whatever). That's why a DatabaseLayerUser that is responsible for storing itself is probably a bad idea. So let's start instead with a nice and clean GameUser domain class and use encapsulation (private properties to prevent tampering from the outside):
class GameUser {
private $_username;
private $_score;
public function __construct($username, $score) {
$this->_username = $username;
$this->_score = $score;
}
public function getUsername() { return $this->_username; }
public function getScore() { return $this->_score; }
public function incrementScore() {
$this->_score++;
return $this->_score;
}
}
We can now create a new GameUser ($user = new GameUser('Dizzy', 100);), but we cannot persist it. So we need some kind of repository where we can store these users and fetch them again later. Let's call it just that: the GameUserRepository. This is a service class. Later when there is more than one type of domain object and repository, we can create a DatabaseLayer class that will group these and/or act as a facade, but we start small now and will refactor later.
Once again, the responsibility of the GameUserRepository class is to allow us to fetch and store GameUser objects, and to check if a profile for a given username exists. In principle, the repository could store the GameUser objects in a file or somewhere else, but for now, we're making making the choice here to persist them in a SQL database (start small, refactor later, you get it.)
The responsibility of the GameUserRepository is not management of the database connection. It will however need a database object so it can pass the SQL queries to it. So it delegates the responsibility of setting up the connection and actually executing the SQL queries it will create.
Delegation rings a bell. Dependency injection comes into play here: we will inject a PDO database object (service). We'll inject it in the constructor (i.e. constructor DI as opposed to setter DI). It is then the responsibility of the caller to figure out how to create the PDO service, our repository really couldn't care less.
class GameUserRepository {
private $_db;
public function __construct(PDO $db) {
$this->_db = $db;
}
public function profileExists($username) {
try {
$PDOQuery = ('SELECT count(1) FROM userprofile_upl WHERE uid_upl = :uid');
$PDOStatement = $this->dbh->prepare($PDOQuery);
$PDOStatement->bindParam(':uid', $username);
$PDOStatement->execute();
return $PDOStatement->fetchColumn() >0;
} catch(PDOException $e) {
throw new Exception('Failed to check if profile exists for '. $username, 0, $e);
}
}
public function fetchGameUser($username) { ... }
public function storeGameUser(GameUser $user) { ... }
}
To answer Q1 & Q2 in one go: function __construct(PDO $db) simply expresses a type constraint: PHP will check that the $db parameter value is a PDO object. If you're trying to run $r = new GameUserRepository("not a PDO object");, it will throw an error. The PDO type constraint has nothing to do with dependency injection.
I think you're confusing this with the functionality of DI frameworks that actually inspect the constructor signature at run-time (using reflection), see that a PDO-type argument is needed, and then indeed create such an object automagically and pass it to the constructor when creating the repository. E.g. the Symfony2 DI bundle can do this, but it has nothing to do with PHP itself.
Now we can run code like this:
$pdo = new PDO($connectionString, $user, $password);
$repository = new GameUserRepository($pdo);
$user = $repository->fetchGameUser('Dizzy');
But this begs the question: what is the best way to create all these objects (services), and where do we keep them? Surely not by just putting the above code somewhere and using global variables. These are two clear responsibilities that need to be located somewhere, so the answer is that we create two new classes: the GameContainer class and the GameFactory class to create this container.
The responsibility of the GameContainer class is to centralize the GameUserRepository service with other services that we'll create in the future. The responsibility of the GameFactory class is to set up the GameContainer object. We'll also create a GameConfig class to configure our GameFactory:
class GameContainer {
private $_gur;
public function __construct(GameUserRepository $gur) { $this->_gur = $gur; }
public function getUserRepository() { return $this->_gur; }
}
class GameConfig { ... }
class GameFactory {
private $_config;
public function __construct(GameConfig $cfg) {
$this->_config = $cfg;
}
public function buildGameContainer() {
$cfg = $this->_config;
$pdo = new PDO($cfg->read('db.dsn'), $cfg->read('db.user'), $cfg->read('db.pw'));
$repository = new GameUserRepository($pdo);
return new GameContainer($repository);
}
}
We can now imagine a game.php application with basically the following code:
$factory = new GameFactory(new GameConfig(__DIR__ . '/config.php'));
$game = $factory->buildGameContainer();
One crucial thing is still missing however: the use of interfaces. What if we want to write a GameUserRepository that uses an external web service to store and fetch GameUser objects? What if we want to provide a MockGameUserRepository with fixtures to facilitate testing? We can't: our GameContainer constructor explicitly demands a GameUserRepository object as we have implemented it using the PDO service.
So, now is the time to refactor and extract an interface from our GameUserRepository. All consumers of the current GameUserRepository will now have to use the IGameUserRepository interface instead. This is possible thanks to dependency injection: references to GameUserRepository are all just used as type constraints that we can replace by the interface. It would not be possible if we hadn't delegated the task of creating these services to the GameFactory (which will now have the responsibility to determine an implementation for each service interface.)
We now get something like this:
interface IGameUserRepository {
public function profileExists($username);
public function fetchGameUser($username);
public function storeGameUser(GameUser $user);
}
class GameContainer {
private $_gur;
// Container only references the interface:
public function __construct( IGameUserRepository $gur ) { $this->_gur = $gur; }
public function getUserRepository() { return $this->_gur; }
}
class PdoGameUserRepository implements IGameUserRepository {
private $_db;
public function __construct(PDO $db) {
$this->_db = $db;
}
public function profileExists($username) {...}
public function fetchGameUser($username) { ... }
public function storeGameUser(GameUser $user) { ... }
}
class MockGameUserRepository implements IGameUserRepository {
public function profileExists($username) {
return $username == 'Dizzy';
}
public function fetchGameUser($username) {
if ($this->profileExists($username)) {
return new GameUser('Dizzy', 10);
} else {
throw new Exception("User $username does not exist.");
}
}
public function storeGameUser(GameUser $user) { ... }
}
class GameFactory {
public function buildGameContainer(GameConfig $cfg) {
$pdo = new PDO($cfg->read('db.dsn'), $cfg->read('db.user'), $cfg->read('db.pw'));
// Factory determines which implementation to use:
$repository = new PdoGameUserRepository($pdo);
return new GameContainer($repository);
}
}
So this really puts all pieces together. We can now write a TestGameFactory injecting the MockGameUserRepository, or, better still, extend GameConfig with a "env.test" boolean and have our existing GameFactory class decide whether to construct a PdoGameUserRepository or MockGameUserRepository based on that.
The connection with DI practices should now be clear as well. The GameContainer, of course, is your DI container. The GameFactory, is the DI container factory. It is these two that are implemented with all bells and whistles by DI frameworks such as the Symfony2 DI bundle.
You can indeed imagine extending the factory and its config up to the point where all services are completely defined in an XML file, including their implementation class names:
<container env="production">
<service name="IGameUserRepository" implementation="PdoGameUserRepository">
<connectionString>...</connectionString>
<username>...</username>
<password>...</password>
</service>
</container>
<container env="test">
<service name="IGameUserRepository" implementation="MockGameUserRepository"/>
</container>
You can also imagine generalizing the GameContainer so that services are fetched like $container->getService('GameUserRepository').
As for passing constructor arguments to parent classes in PHP (which has little to do with DI, except that it'll be needed sooner or later if you use constructor injection), you can do this as you suggest yourself:
class A {
private $_someService;
public function __construct(SomeService $service) { $this->_someService = $service; }
}
class B extends class A {
private $_someOtherService;
public function __construct(SomeService $service, SomeOtherService $otherService) {
parent::__construct($service);
$this->_someOtherService = $otherService;
}
}
$b = new B(new SomeService(), new SomeOtherService());
But you'll have to stay away from the private constructors. They reek of singletons.
Trying to get a grasp on MVC, I wrote the following:
//Model/Model.php
class Model
{
private $dbh;
public function __construct()
{
$this->dbh = dbConnect();
}
private function dbConnect()
{
//return database connection
}
public function crudMethod()
{
//interact with databse using $this->dbh
}
}
//Controller/Controller.php
class Controller
{
public $modelConnection;
public function __construct()
{
$this->modelConnection = new Model();
}
}
//View/index.php
$obj = new Controller;
$obj->modelConnection->crudMethod();
?>
Looking at my code, I feel like I'm completely missing the point here.
The controller doesn't serve real value, I might as well instantiate the Model class directly.
Should a Model class ever be instantiated? Inside or outside the controller? Or is it bad practice altogether?
How would I improve this structure to support the MVC paradigm?
Your problem is that you are putting information in your layers that should not be there. Your Data Access layer does not need to know HOW it will be connected, they just need to know that there is a properly configured driver to get data from.
In the same way, you are giving your controllers responsibilities they doesn't need: creating models.
Why this is bad?
Imagine that for some reason, you change the dependencies of your model classes. Like if you now have to store data from the model in two different databases at the same time for redundancy.
How would you do this? Change all of your controllers and alter the way you instantiate models inside them?
That's a lot of work! And bug prone.
So, how can I solve this?
I like to do this using Dependency Injection Principle and the Factory Pattern.
Why a factory?
To decouple the creation of my Model from who uses it. My default model factory creates Models that requires only one database connection to work:
namespace MyApplication\Model\Factory;
use MyApplication\Storage\StorageInterface;
interface FactoryInterface {
public function create($modelClassName);
}
class WithStorage implements FactoryInterface {
const MODEL_NAMESPACE = '\\MyApplication\\Model\\';
private $storage;
public function __construct(StorageInterface $storage) {
$this->storage = $storage;
}
public function create($modelClassName) {
$refl = new \ReflectionClass(self::MODEL_NAMESPACE . $modelClassName);
try {
return $refl->newInstance($this->storage);
} catch (\ReflectionException $e) {
throw new RuntimeException("Model {$modelClassName} not found");
}
}
}
Now, for your model classes that requires a storage, you can create a structure like this:
namespace MyApplication\Storage;
interface StorageInterface {
public function insert($container, array $data);
public function update($container, array $data, $condition);
// etc..
}
class PdoStorage implements StorageInterface {
private $dbh;
public function __construct(\PDO $dbh) {
$this->dbh = $dbh;
}
public function insert($container, array $data) {
// impl. omitted
}
public function update($container, array $data, $condition) {
// impl. omitted
}
}
So, if you have the following class:
namespace MyApplication\Model;
use MyApplication\Storage\StorageInterface;
// Entity impl. will be omitted for brevity.
use MyApplication\Entity\EntityInterface;
abstract class AbstractApplicationModel {
private $containerName;
private $storage;
protected function __construct($name, StorageInterface $storage) {
$this->containerName = (string) $name;
$this->storage = $storage;
}
public function save(EntityInterface $entity) {
// impl. omitted
}
public function delete(EntityInterface $entity) {
// impl. omitted
}
}
class UserModel extends AbstractApplicationModel {
public function __construct(StorageInterface $storage) {
parent::__construct('users', $storage);
}
}
With this, we solve our problem with the coupling within the model. Ok.
So, with all this, how can I get a Model component that is ready to store my data from my controller?
Directly:
namespace MyApplication\Controller;
use MyApplication\Model\UserModel;
use MyApplication\Storage\PDOStorage;
use MyApplication\Entity\User;
class UserController {
public function onCreate() {
$model = new UserModel(new Storage(new \PDO(...))); // Here's our problem
$entity = User::createFromArray([
'name' => 'John',
'surname' => 'Doe',
]);
try {
$model->save($entity);
} catch (Exception $e) {
echo 'Oops, something is wrong: ' . $e;
}
}
}
If you have just one model to deal in your whole application, you are ready to go. But you have several of them, then you will have problems.
What if I no longer want to use PDO as my storage driver and work with MySQLi? What if I don't want to use a RDBMS anymore, instead I want to store my data in plain text files?
This way, you'd have to change the implementation of all controllers (which violates the OCP). Lots of repetitive work to do. I hate this!
Oh wait! We have the freaking factory to create models for us! So, let's use it!
namespace MyApplication\Controller;
use MyApplication\Model\Factory\FactoryInterface;
use MyApplication\Entity\User;
abstract class AbstractController {
private $modelFactory;
public function __construct(ModelFactory $factory) {
$this->modelFactory = $factory;
}
public function getModelFactory() {
return $this->modelFactory;
}
}
class UserController {
public function onCreate() {
$model = $this->getModelFactory()->create('UserModel'); // now it's better
$entity = User::createFromArray([
'name' => 'John',
'surname' => 'Doe',
]);
try {
$model->save($entity);
} catch (Exception $e) {
echo 'Oops, something is wrong: ' . $e;
}
}
}
Now, to have all this working together, you have to do some setup on your bootstrap/front controller:
use MyApplication\Storage\PDOStorage;
use MyApplication\Model\Factory\WithStorage as ModelFactory;
$defaultPDODriver = new \PDO(...);
$defaultStorage = new PdoStorage($defaultPDODriver);
$defaultModelFactory = new ModelFactory($defaultStorage);
$controller = new UserController($defaultModelFactory);
Now, what do I do if I want to change my storage engine to plain text files?
$defaultStorage = new PlainFileStorage('/path/to/file'); // just this
Now, what do I do if I want to change my storage engine to one custom implementation that have 2 distinct database to save the same data?
$master = new PdoStorage(new \PDO(...));
$slave = new PdoStorage(new \PDO(.......));
$defaultStorage = new RedundancyStorage($master, $slave);
See? Now the way you store information is irrelevant to your models.
The same way, if you have some crazy business logic that changes the way your models do things based on the setup, you can also change your model factory:
$defaultModelFactory = new SomeCrazyModelFactory(...);
Your controllers are not even aware that your models changed (of course, you'd have to respect the same interface to be allowed to do this interchangeably).
That's a possible way, it's more or less how I do, but there are a few other possibilities.
You are mixing concerns in your interpretation of a Model. In Model-View-Controller, a Model is some type of knowledge, which could be a single class representing a domain concept or a more complex structure e.g. a Page or Document in the context of a CMS.
However in your example, your Model class is really just a Singleton holder for your database connection, that is not its purpose.
A database connection is a service that is provided across the MVC tiers, which should preferably be wired using dependency injection. You should not instantiate services in Controller code.
Whether instantiating Model objects within a controller makes sense depends largely on the context, but there is no rule that forbids it.
I have such model for my company with have method setOptions for creating object from array or Zend_DB_Table_Row
<?php
class Model_Company
{
protected $c_id;
protected $c_shortname;
protected $c_longname;
public function __constructor(array $options = null)
{
if(is_array($options)) {
$this->setOptions($options);
}
}
public function setOptions($data)
{
if($data instanceof Zend_Db_Table_Row_Abstract) {
$data = $data->toArray();
}
if(is_object($data)) {
$data = (array)$data;
}
if(!is_array($data)) {
throw new Exception('Initial data must be object or array');
}
$methods = get_class_methods($this);
foreach($data as $key => $value) {
$method = 'set'.ucfirst($key);
if(in_array($method, $methods)) {
$this->{'c_'.$key} = $value;
}
}
return $this;
}
I also have company manager with param like model and adapter for different db/soap/rest
class Model_CompanyManager
{
private $_adapter;
public function __construct(Model_Company $model,$adapter)
{
$this->_adapter = $adapter;
$this->_model = $model;
return $this;
}
/**
* Save data
* #var Model_Company $data
*/
public function save(array $data)
{
$data = $this->_model->setOptions($data);
$this->_adapter->save($data);
}
}
And DBTable in DBTable
class Model_DbTable_Company extends Zend_Db_Table_Abstract
{
protected $_name = 'company';
protected $_id = 'c_id';
public function save(Model_Company $data)
{
try {
if(isset($data['c_id'])) {
$this->update($data, 'c_id = :c_id',array('c_id' => $data['c_id']));
} else {
$this->insert($data);
}
} catch (Zend_Db_Exception $e) {
throw new Exception($e->getMessage());
}
}
}
How can I insert into db because model properties are protected and I can't do (array)$data
$this->_companyManager = new Model_CompanyManager(new Model_Company,new Model_DbTable_Company);
$this->_companyManager->save($values);
I don't what to create array with fields name inside save like this:
$data = array(
'c_shortname' => $company->getShortname(),
'c_longname' => $company->getLongName(),
'c_description' => $company->getDescription(),
'c_salt' => $company->getSalt(),
'c_password' => $company->getPassword(),
'c_updated_at' => date('YmdHis')
);
Because when I gonna change model fields names and other stuff I have to remember also to change here... Is there is simple approach , pattern that model keep everything and it clean
If you simply want to get a list of all your objects properties with reflection you could use get_class_vars. With a recent version of PHP this will return all the class variables regardless of scope. But since you are tossing in this Manager, usually called a Data Mapper, I assume you expect your model objects will not align exactly to a database table. Otherwise you should just stick to the table-row-gateway pattern of the Zend_Db_Table classes.
I personally am a big fan of the Data Mapper pattern in conjunction with ZF applications. Only the most simple apps are going to line up to relational database tables. It encourages richer objects and writing models and business logic before database schemas.
The job of the mapper is exactly what it suggests, to map your entity to a persistence layer, so handling in the mappers save() method, the actual assignments for the SQL statement (probably building an array with your tables field names and assigning the values, maybe even saving to multiple tables) is perfectly acceptable.
If some of your objects are more simple and do align better with a table, as will certainly be the case, what I like to do is have a __toArray() method for the object. It can be responsible for returning a representation suitable for building an insert/update statement. Also useful for needing a JSON representation for serving it via AJAX. You can be as lazy as you like in writing these - use that get_class_vars function or other reflection. I usually have two mapper base classes. One with CRUD functions that essentially do what Zend_Db_Table does, and another more skeletal where I am responsible for writing more of the code. They should follow a common interface. Kinda gets you the best of both worlds.
I found this link to be a good resource for some ideas.
It is being said that "static methods are death to testability". If that is so, what is a viable alternative pattern for the below?
class User {
private $phone,
$status = 'default',
$created,
$modified;
public function __construct($phone) {
$this->phone = $phone;
$this->created = new DateTime;
$this->modified = new DateTime;
}
public static function getByPhone(PDO $pdo, $phone) {
$stmt = $pdo->prepare('SELECT * FROM `users` WHERE `phone` = :phone');
$stmt->execute(compact('phone'));
if (!$stmt->rowCount()) {
return false;
}
$record = $stmt->fetch(PDO::FETCH_ASSOC);
$user = new self($record['phone']);
$user->status = $record['status'];
$user->created = new DateTime($record['created']);
$user->modified = new DateTime($record['modified']);
return $user;
}
public function save(PDO $pdo) {
$stmt = $pdo->prepare(
'INSERT INTO `users` (`phone`, `status`, `created`, `modified`)
VALUES (:phone, :status, :created, :modified)
ON DUPLICATE KEY UPDATE `status` = :status,
`modified` = :modified');
$data = array(
'phone' => $this->phone,
'status' => $this->status,
'created' => $this->created->format('Y-m-d H:i:s'),
'modified' => date('Y-m-d H:i:s')
);
return $stmt->execute($data);
}
...
}
This is just a cut down example. The class has a few more methods and properties and there's more validation when writing to the database etc. The guiding design principle behind this class is that it models a user as an object. Some of the object's properties cannot be modified after it has been created, like the phone number (which acts as a primary id), the date the user was created and so on. Other properties can only be altered according to strict business rules, which all have strictly validating setters and getters.
The object does not represent a database record per se, the database is only seen as one possible form of permanent storage. As such, a database connector is not stored in the object but rather needs to be injected every time the object needs to interact with the database.
When a new user is created, this looks like:
$user = new User('+123456789');
When an existing user is restored from permanent storage, that looks like:
$pdo = new PDO('...');
$user = User::getByPhone($pdo, '+123456789');
If I were to take the "death to testability" line seriously, this is supposedly bad. I am perfectly able to test this object though, since it is fully dependency injected and the static methods have no state. How could I do this any differently and avoid the use of static methods? Or rather, what exactly argues against static in this case? What makes this particular use of static methods so hard to test?
This is mostly a summary of (my perspective of) the chat that ensued between me and #zerkms:
The point of contention is actually this:
public function doSomething($id) {
$user = User::getByPhone($this->pdo, $id);
// do something with user
return $someData;
}
This makes it hard to test doSomething since it hardcodes the User class, which may or may not have a lot of dependencies. But this is in fact the same as instantiating the object using a non-static method:
public function doSomething($id) {
$user = new User;
$user->initializeFromDb($this->pdo, $id);
// do something with user
return $someData;
}
We're not using a static method, but it's still unmockable. Actually, it got worse.
The answer is to use a factory:
public function doSomething($id) {
$user = $this->UserFactory->byPhone($id);
// do something with user
return $someData;
}
Now the factory can be dependency injected and mocked and the User class is no longer hardcoded. You may or may not think this overkill, but it certainly improves mockability.
That does not change the fact though that this factory may very well instantiate the actual user object using a static method:
public function byPhone($id) {
return User::getByPhone($this->db, $id);
}
There's no difference between using a static method or a regular constructor here.
$user = new User($db, $id);
$user = User::getByPhone($db, $id);
Both expressions return an instance of User and both "hardcode" the User class. Which simply needs to happen at some point anyway.
For my use case, a static constructor method makes the most sense for the object. And as was demonstrated, static methods are not the problem. Where to call them is the point of contention, not that they exist at all. And I have yet to see a convincing argument for not using static constructors, since they can be wrapped in a factory, which alleviates any problem with mockability, the same as it does for regular object instantiation.
As long as OP asked about general issue, and not asked how to improve his particular code - I'll try to answer using some abstract and tiny classes:
Well, it is not more difficult to test static methods themselves, but it is more difficult to test the methods that use static methods.
Let's see the difference on small example.
Let's say we have a class
class A
{
public static function weird()
{
return 'some things that depends on 3rd party resource, like Facebook API';
}
}
It does some work that requires setting up additional environment (specifying API keys in this case) and internet connection to FB API services. It will take some time to test this method (just because of network and API lags), but it is definitely easy enough to test it.
Now, we implement a class that uses A::weird() method:
class TestMe
{
public function methodYouNeedToTest()
{
$data = A::weird();
return 'do something with $data and return';
}
}
For now - we cannot test TestMe::methodYouNeedToTest() without additional steps required to make A::weird() worked. Yes, instead of testing methodYouNeedToTest we also need to do things that are not directly related to this class, but to another.
If we followed another way from the very begin:
class B implements IDataSource
{
public function weird()
{
return 'some things that depends on 3rd party resource, like Facebook API';
}
}
you see - the key difference here is that we implemented the IDataSource interface and made method normal, not static. For now we could rewrite our code above in this way:
class TestMe
{
public function methodYouNeedToTest(IDataSource $ds)
{
$data = $ds->weird();
return 'do something with $data and return';
}
}
And now we don't rely on specific implementation but we do on an interface. And now we can easily mock datasource.
Such abstractions help keeping our tests focusing more on the testing itself rather than on creating necessary environment.
Such steps helps us to have our unit tests fast. While we still could have acceptance, load and functional tests (but it is another story) that test that our application works as expected
Static methods are only "death to testability" if they depend on state. If you avoid writing such methods to begin with (which you should), then this issue simply goes away.
The Math.abs() example given is one of a good use of a static method. It does not depend on state, therefor it is super easily tested.
That said, whether or not you think static methods should be used is another story. Some people dislike their seemingly procedural nature. I agree with those who say that OOP is a tool, not a goal. If writing "proper" OO code doesn't make sense for a particular situation (e.g. Math.abs()), then don't do it. I promise the boogey man won't eat your application just because you used a static method. :-)
As mentioned in comments, I'd implement a repository pattern for this case.
For example, User would be a simple model with read-only properties
class User {
private $phone,
$status = 'default',
$created,
$modified;
public function __construct($phone) {
$this->setPhone($phone);
$this->created = new DateTime;
$this->modified = new DateTime;
}
private function setPhone($phone) {
// validate phone here
$this->phone = $phone;
}
public function getPhone() {
return $this->phone;
}
public function getCreated() {
return $this->created;
}
public function getModified() {
return $this->modified;
}
}
Your repository interface could then look like this
interface UserRepository {
/**
* #return User
*/
public function findByPhone($phone);
public function save(User $user);
}
A concrete implementation of this interface could look something like this
class DbUserRepository implements UserRepository {
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function findByPhone($phone) {
// query db and get results, return null for not found, etc
$user = new User($phone);
// example setting the created date
$reflectionClass = new ReflectionClass('User');
$reflectionProperty = $reflectionClass->getProperty('created');
$reflectionProperty->setAccessible(true);
$created = new DateTime($res['created']); // create from DB value (simplified)
$reflectionProperty->setValue($user, $created);
return $user;
}
public function save(User $user) {
// prepare statement and fetch values from model getters
// execute statement, return result, throw errors as exceptions, etc
}
}
The cool thing here is that you can implement many different repositories, all with different persistence strategies (XML, test data, etc)
I think the citation you give has a good point but takes too hard a line.
Your static method is what he calls a "leaf" method. In this case I think you are fine, as long as your static method doesn't have any external dependencies.
The alternative is a data mapper, an object which is aware of the relationship between User and how it is stored in the database. Example:
class UserDBMapper {
protected $pdo;
protected $userclass;
function __construct(PDO $pdo, $userclass) {
$this->db = $db;
// Note we can even dependency-inject a User class name if it obeys the interface that UserMapper expects.
// You can formalize this requirement with instanceof, interface_exists() etc if you are really keen...
$this->userclass = $userclass;
}
function getByPhone($phone) {
// fetches users from $pdo
$stmt = $this->db->query(...);
$userinfo = $stmt->fetch()....
// creates an intermediary structure that can be used to create a User object
// could even just be an array with all the data types converted, e.g. your DateTimes.
$userargs = array(
'name' => $userinfo['name'],
'created' => $userinfo['created'],
// etc
);
// Now pass this structure to the $userclass, which should know how to create itself from $userargs
return new $this->userclass($userargs);
}
function save($userobj) {
// save method goes in the Mapper, too. The mapper knows how to "serialize" a User to the DB.
// User objects should not have find/save methods, instead do:
// $usermapper->save($userobj);
}
}
This is a very powerful pattern (e.g., you need no longer have 1-1 type<->table, instance<->row correspondence like the Active Record pattern requires), and you can completely change your serialization method without altering your domain objects at all. It should also be obvious how much easier the mapper is to test. But in many cases this pattern is also over-engineered and more than you need. After all, most websites use the much simpler Active Record pattern.
Firstly, the DateTime class was a good (tricky) class to choose because it is a horrible class. All of its important work is done in the constructor and there is no way to set the date/time after it has been constructed. This requires us to have an objectGenerator that can build the DateTime object at the correct time. We can still manage this without calling new in the User class though.
I have kept things very simple to solve the problem at hand, but they can be extended easily to handle an arbitrarily complex problem.
Here is a simple objectGenerator to remove the coupling you get with new.
class ObjectGenerator {
public function getNew($className) {
return new $className;
}
}
Now we inject all of the dependencies into the constructor. The constructor shouldn't do real work, only set up the object.
class User {
private $phone,
$status = 'default',
$created,
$modified,
$pdo,
$objectGenerator;
public function __construct(PDO $pdo, $objectGenerator) {
$this->pdo = $pdo;
$this->objectGenerator = $objectGenerator;
$this->created = $this->objectGenerator->getNew('DateTime');
}
public function createNew() {
$this->phone = '';
$this->status = 'default';
$this->created = $this->objectGenerator->getNew('DateTime');
}
public function selectByPhone($phone) {
$stmt = $this->pdo->prepare('SELECT * FROM `users` WHERE `phone` = :phone');
$stmt->execute(compact('phone'));
if (!$stmt->rowCount()) {
return false;
}
$record = $stmt->fetch(PDO::FETCH_ASSOC);
$this->phone = $record['phone'];
$this->status = $record['status'];
$this->created = $record['created'];
$this->modified = $record['modified'];
}
public function setPhone($phone) {
$this->phone = $phone;
}
public function setStatus($status) {
$this->status = $status;
}
public function save() {
$stmt = $this->pdo->prepare(
'INSERT INTO `users` (`phone`, `status`, `created`, `modified`)
VALUES (:phone, :status, :created, :modified)
ON DUPLICATE KEY UPDATE `status` = :status,
`modified` = :modified');
$modified = $this->objectGenerator->getNew('DateTime');
$data = array(
'phone' => $this->phone,
'status' => $this->status,
'created' => $this->created->format('Y-m-d H:i:s'),
'modified' => $modified->format('Y-m-d H:i:s')
);
return $stmt->execute($data);
}
}
Usage:
$objectGenerator = new ObjectGenerator();
$pdo = new PDO();
// OR
$pdo = $objectGenerator->getNew('PDO');
$user = new User($pdo, $objectGenerator);
$user->setPhone('123456789');
$user->save();
$user->selectByPhone('5555555');
$user->setPhone('5552222');
$user->save();
So, no new or static in the user class. Try testing both solutions. The test code is a pleasure to write without the calls to new. All classes that use the User will also be easy to test without static calls to it.
The differences in test code are:
new/static - Require a stub for each new or static call to stop the unit from reaching outside of itself.
dependency injection - Mock objects can be injected. It is painless.
I'm working on creating a domain layer in Zend Framework that is separate from the data access layer. The Data Access Layer is composed to two main objects, a Table Data Gateway and a Row Data Gateway. As per Bill Karwin's reply to this earlier question I now have the following code for my domain Person object:
class Model_Row_Person
{
protected $_gateway;
public function __construct(Zend_Db_Table_Row $gateway)
{
$this->_gateway = $gateway;
}
public function login($userName, $password)
{
}
public function setPassword($password)
{
}
}
However, this only works with an individual row. I also need to create a domain object that can represent the entire table and (presumably) can be used to iterate through all of the Person's in the table and return the appropriate type of person (admin, buyer, etc) object for use. Basically, I envision something like the following:
class Model_Table_Person implements SeekableIterator, Countable, ArrayAccess
{
protected $_gateway;
public function __construct(Model_DbTable_Person $gateway)
{
$this->_gateway = $gateway;
}
public function current()
{
$current = $this->_gateway->fetchRow($this->_pointer);
return $this->_getUser($current);
}
private function _getUser(Zend_Db_Table_Row $current)
{
switch($current->userType)
{
case 'admin':
return new Model_Row_Administrator($current);
break;
case 'associate':
return new Model_Row_Associate($current);
break;
}
}
}
Is this is good/bad way to handle this particular problem? What improvements or adjustments should I make to the overall design?
Thanks in advance for your comments and criticisms.
I had in mind that you would use the Domain Model class to completely hide the fact that you're using a database table for persistence. So passing a Table object or a Row object should be completely under the covers:
<?php
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();
$db = Zend_Db::factory('mysqli', array('dbname'=>'test',
'username'=>'root', 'password'=>'xxxx'));
Zend_Db_Table_Abstract::setDefaultAdapter($db);
class Table_Person extends Zend_Db_Table_Abstract
{
protected $_name = 'person';
}
class Model_Person
{
/** #var Zend_Db_Table */
protected static $table = null;
/** #var Zend_Db_Table_Row */
protected $person;
public static function init() {
if (self::$table == null) {
self::$table = new Table_Person();
}
}
protected static function factory(Zend_Db_Table_Row $personRow) {
$personClass = 'Model_Person_' . ucfirst($personRow->person_type);
return new $personClass($personRow);
}
public static function get($id) {
self::init();
$personRow = self::$table->find($id)->current();
return self::factory($personRow);
}
public static function getCollection() {
self::init();
$personRowset = self::$table->fetchAll();
$personArray = array();
foreach ($personRowset as $person) {
$personArray[] = self::factory($person);
}
return $personArray;
}
// protected constructor can only be called from this class, e.g. factory()
protected function __construct(Zend_Db_Table_Row $personRow) {
$this->person = $personRow;
}
public function login($password) {
if ($this->person->password_hash ==
hash('sha256', $this->person->password_salt . $password)) {
return true;
} else {
return false;
}
}
public function setPassword($newPassword) {
$this->person->password_hash = hash('sha256',
$this->person->password_salt . $newPassword);
$this->person->save();
}
}
class Model_Person_Admin extends Model_Person { }
class Model_Person_Associate extends Model_Person { }
$person = Model_Person::get(1);
print "Got object of type ".get_class($person)."\n";
$person->setPassword('potrzebie');
$people = Model_Person::getCollection();
print "Got ".count($people)." people objects:\n";
foreach ($people as $i => $person) {
print "\t$i: ".get_class($person)."\n";
}
"I thought static methods were bad
which is why I was trying to create
the table level methods as instance
methods."
I don't buy into any blanket statement that static is always bad, or singletons are always bad, or goto is always bad, or what have you. People who make such unequivocal statements are looking to oversimplify the issues. Use the language tools appropriately and they'll be good to you.
That said, there's often a tradeoff when you choose one language construct, it makes it easier to do some things while it's harder to do other things. People often point to static making it difficult to write unit test code, and also PHP has some annoying deficiencies related to static and subclassing. But there are also advantages, as we see in this code. You have to judge for yourself whether the advantages outweigh the disadvantages, on a case by case basis.
"Would Zend Framework support a Finder
class?"
I don't think that's necessary.
"Is there a particular reason that you
renamed the find method to be get in
the model class?"
I named the method get() just to be distinct from find(). The "getter" paradigm is associated with OO interfaces, while "finders" are traditionally associated with database stuff. We're trying to design the Domain Model to pretend there's no database involved.
"And would you use continue to use the
same logic to implement specific getBy
and getCollectionBy methods?"
I'd resist creating a generic getBy() method, because it's tempting to make it accept a generic SQL expression, and then pass it on to the data access objects verbatim. This couples the usage of our Domain Model to the underlying database representation.