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.
Related
This is my first go at creating an object oriented site in PHP and a question regarding constructors.
I have a character class that is generated after a user logs in. The character object will hold all the user data for the game.
My question is regarding the constructor / instantiating the class. Would it be okay to run a query in the class constructor like I have in the attached code?
Currently the argument being passed in is just the username which is obtained during login and that is what is ran in the query to get the character info.
Would this be okay or best practice be to run the query outside the construct or class and pass in the arguments to the classes constructor?
Just want to make sure I am not overlooking any potential issues with the current code I have - As long as exceptions are accounted for it should be okay to keep the query in the constructor, no?
<?php
include $_SERVER['DOCUMENT_ROOT']."/rx/includes/classes/connection.php";
class Character {
private $charid;
private $userid;
private $username;
private $class;
private $exp;
private $money;
private $attack;
private $defense;
private $hp;
private $turns;
function __construct($data){
$pdo = Connection::getInstance();
try {
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$char = $pdo->prepare("SELECT users.userid, users.username, characters.charid, characters.money, characters.attack, characters.defense, characters.class, characters.hp, characters.userid, characters.turns, characters.exp
FROM characters
JOIN users ON users.userid=characters.userid
WHERE username=?");
$char->execute(array($data));
$res = $char->fetchAll(PDO::FETCH_ASSOC);
$this->charid = $res[0]['charid'];
$this->userid = $res[0]['userid'];
$this->username = $res[0]['username'];
$this->class = $res[0]['class'];
$this->exp = $res[0]['exp'];
$this->money = $res[0]['money'];
$this->attack = $res[0]['attack'];
$this->defense = $res[0]['defense'];
$this->hp = $res[0]['hp'];
$this->turns = $res[0]['turns'];
}
catch (Exception $e){
echo $e;
}
}
public function __get($property) {
if (property_exists($this, $property)) {
return $this->$property;
}
}
public function __set($property, $value) {
if (property_exists($this, $property)) {
$this->$property = $value;
}
return $this;
}
}
No.
And it is also advised to avoid using magic setters and getters. And same goes for singletons.
A constructor shouldn't actually contain any logic, because it make the code quite hard to test. Instead your class constructor should expect the connection as a parameter and perform the logic in a separate method. Kinda like this:
class Character
{
private $connection;
public function __construct(\PDO $pdo)
{
$this->connection = $pdo;
}
public function populateByUsername($data)
{
// come code
$statement = $pdo->prepare("SELECT us ....");
$statement->execute(array($data));
$data = $char->fetchAll(PDO::FETCH_ASSOC);
// more code
}
}
To improve the code further, I would recommend actually separating the SQL code from the entity's logic (in your example - character). The best approach for that would be use of data mapper pattern - it basically means that you have two classes - one for entity and one for sql parts. It ends up looking kinda like this:
$char = new Character;
$char->setUsername($username);
$mapper = new CharacterMapper($pdo);
$mapper->fetchByUsername($char);
In this case the mapper class take your entity, uses a getter to extract the username, calls the SQL and then puts the data in the character object. Later, when you get more familiar with OOP, you will also learn to be able to call multiple mapper on the same entity, when necessary (for example, you can set it up so that there is a separate mapper for cache and separate for db .. you call the cache mapper first, and if it fails, you call the db mapper).
The downside with this is your 'Character' class constructor would be tightly coupled to your database. Which means there would be no other way to initialize your 'Character' except with getting data from the DB. You won't be able to initialize it with another source (e.g. XML files) or initialize it with test data for the purposes of unit testing.
What I would recommend is to separate the query, into something like a 'InitializationProvider' (it's just an example because I'm not sure about your app's logic). Then you could have different types of InitializationProviders like DBInitializationProvider, XMLInitializationProvider, etc. that would be dynamically injected into your Character class.
It's up to you though if that kind of flexibility would be required in the future.
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.
I'm trying to figure out how to properly work this out. I don't know if I'm applying things correctly. I think it's a good thing to separate the User object, from the database operations, meaning that User class shouldn't know how to operate with the DB. Thats why I created another class UserModel (I'm not sure of how to name this class) which will be the one handling the db queries and returning their results. Before, I was selecting, inserting, updating and deleting from the User class, but I felt like that responsability didn't belong to that class. Why a User should return a set of Users? That didn't make sense to me.
I also got doubts when using the database, as I don't know how to properly use PDO.
(In this example I've only implemented the Insert query, the other methods are implemented the same way.)
Let's get started.
User.php
class User {
private $id;
private $name;
private $email;
private $username;
private $password;
//Getters & Setters
}
UserModel.php (Should this be called UserModel?)
class UserModel {
private $db;
private $pdo;
public function __construct()
{
$db = new Database;
$pdo = $db->getPDO();
}
public function createUser($user)
{
$sql = 'INSERT INTO users (`name`,`email`,`username`,`password`) VALUES(:name, :email, :username, :password)';
$pdo->prepare($sql);
$pdo->bindValue(':name', $user->getName(), PDO::PARAM_STR);
$pdo->bindValue(':email', $user->getEmail(), PDO::PARAM_STR);
$pdo->bindValue(':username', $user->getUsername(), PDO::PARAM_STR);
$pdo->bindValue(':password', $user->getPassword(), PDO::PARAM_STR);
return $pdo->execute();
//return true if inserted, return false if not
}
public function editUser($user)
{
}
public function deleteUser($uid)
{
}
public function getUser($uid)
{
}
public function getAllUsers()
{
}
}
Database.php
class Database {
private $pdo;
public function __construct()
{
$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');
}
public function getPdo() {
return $this->pdo;
}
}
users.php
$usermodel = new UserModel;
$user = new User;
$user->setName('John');
$user->setEmail('john#john.com');
$user->setUsername('john');
$user->setPassword('1234');
$usermodel->createUser($user);
Looks good to me as a custom based user model solution. These work awesome for small projects since they're so lightweight and you're able to customize each to your taste.
As you scale up you'll notice this has to be done/customized for each database table and if you have joins... things may get hairy to say the least.
I would recommend you take a look at the ORMs used within major php frameworks: zend, laravel, cake among others.
The ORM essentially does auto-mapping based on table name/primary key. Assuming you're ok with SOME 'magic' and losing full control over data in and data out you will have the same ability you've created for the user object in any other table you add by setting the two defaults I mentioned.
On the downside is the performance hit you'll experience. Here's some stats on the line count/file count of the frameworks I mentioned:
https://stackoverflow.com/questions/28923085/should-i-use-laravel-framework-if-i-use-wordpress/28923187#28923187
I'm hoping you can shed some light to my questions guys. What I need is basically the best practise of getting and assigning properties from database rows (instance getter). It is a pretty standart issue and I have know how it works in C#, but I'm unclear how to accomplish similar results in php and havent found any solution online which covers my needs.
So lets assume I have this simplified code:
Class User{
private $Pdo;
private $UserID;
private $UserName;
function GetUserName(){
return $this->UserName;
}
function GetUserInfo(){
// return user object with given user id, in this example just use "1"
$Pdo = $this->Pdo;
$Query = $Pdo->query('select * from User where UserID = 1');
$Query->setFetchMode(PDO::FETCH_CLASS, "User", array($Pdo) ); // this is returning object
$UserInfo = $Query->fetch();
return $UserInfo;
}
}
Then when I wanted to get the user object I would call it like:
$User = new User($Pdo);
$UserInfo = $User->GetUserInfo();
echo $UserInfo->GetUserName();
This works, however I dont like to do it this way. One option would be to use static method so in the end I would end up with something like:
$UserInfo = User::GetUserInfo()
Which I suppose is called "Singleton" (edit: not a singleton:)) and is generally noted as bad practise.
My question is how it should look like? What I would like to have is this:
$UserInfo = new User($Pdo);
$UserInfo->GetUserInfo();
echo $UserInfo->GetUserName();
I know that in GetUserInfo method I can manually assign the values to current object ($this) such as:
function GetUserInfo(){
// get data from db
$this->UserName = "John"; // this value I will get from db
}
However I would like to use FetchClass so I can get all the properties based on their names in one line and not assign them manually. I hope that you understand what my issue is :) I would love to hear your opinions of what is the best way of doing this.
Thank you very much for any input.
One step in the direction to a best pratice and good design would be to separate your domain models and the persistence layer.
So you are independent of the used database or could even replace the database with a webservice for example. Look at the Data Mapper pattern.
So your User model would only consist of the properties + getters/setters and methods that use these properties in some way (business logic).
class User
{
protected $UserID;
protected $UserName;
public function getUserId()
{
return $this->UserID;
}
public function setUserId($userId)
{
$this->UserID = userId;
return $this;
}
...
}
Your mapper holds the database connection and is responsible for saving/fetching the User object.
class UserMapper
{
protected $_pdo;
public function __construct($pdo)
{
$this->_pdo = $pdo;
}
public function getUserById($id)
{
// TODO: better use prepared statements!
$query = $this->_pdo->query("select * from User where UserID = ".id);
$query->setFetchMode(PDO::FETCH_CLASS, "User");
return $query->fetch();
}
public function save(User $user)
{
// insert/update query
}
...
}
You can use it like:
$userMapper = new UserMapper($pdo);
$user = $userMapper->getUserById(1);
echo $user->getUserName();
$user->setUserName('Steve');
$userMapper->save($user);
There are other similar patterns like Table Gateway pattern. But I prefer the data mapper because of the independecy of the data source.
Look at the whole catalog from Martin Fowler: Catalog of Patterns of Enterprise Application Architecture
Another useful thread: What is the difference between the Data Mapper, Table Data Gateway (Gateway), Data Access Object (DAO) and Repository patterns?
I am pondering a few different approaches here and would really appreciate some input! I am considering the two choices below. There are 2 things going on there I have questions on.
Is it preferred to inject the dependencies into the constructor of the main "container" class, or to instead create new instances inside the container class?
In the second example, the class' dependencies are injected via constructor and then maintained within via a property of the class. Then when the methods (route(), render()) are called, the dependencies are called from within. I began with this approach, but am now favoring something more along the lines of the first example. I think the first example is preferable, but are there any benefits to using the DI approach in the second example?
There really is no need to store anything in the class as a property. I can probably rearrange everything to use that technique without much trouble, and I think I like it better. This way I can also move all of the of work out of the constructors, and simply access everything via method later. Am I on the right track here?
class App
{
private $config;
private $router;
private $renderer;
public function __construct(IConfig $config, IRouter $router, IRenderer $renderer)
{
$this->config = $config;
$this->router = $router;
$this->renderer = $renderer;
$this->run();
}
public function run()
{
$data = $this->router->route(new Request, $config->routes);
$this->renderer->render($data);
}
}
class App
{
private $config;
private $router;
private $renderer;
public function __construct()
{
$this->config = new Config;
$this->run();
}
public function run()
{
$this->router = new Router(new Request, $config->routes);
$this->router->route();
$this->renderer = new Renderer($this->router->getData());
$this->renderer->render();
}
}
It is better to inject dependencies into the constructor.
Creating instances within the constructor creates a tight coupling between the two classes. With a constructor with a clear signature like
public function __construct(IConfig $config, IRouter $router, IRenderer $renderer)
I can immediately tell what this component needs to do it's job.
Given a constructor like
public function __construct();
There is no clue what the component needs to function. It creates a strong coupling to specific implementations of your each your router, your request and to your renderer, none of which are apparent until you dig down into the guts of your class.
In summary the first approach is well documented, extendable and testable.
the second approach is opaque, highly coupled, and not easily testable.
While Orangepill makes a good point, I thought I'd chip in, too. I tend to define my constructors with a clear constructor, too, but I don't expect the required objects to be passed when creating an instance.
Sometimes, you create an instance that retrieves data either from a DB, or some sort of Http request. In your case, the first example expects three dependencies to be passed, but who's to say that you'll always need all three of them?
Enter Lazy-Loading. The code sample below is quite lengthy, but it is (IMO) well worth looking into. If I use a service, I don't want to load all dependancies unless I'm sure I'll be using them. That's why I defined the constructor so that I can create an instance in either one of the following ways:
$foo = new MyService($configObj);
$bar = new MyService($configObj, null, $dbObj);//don't load curl (yet)
$baz = new MyService($configObj, $curlObj);//don't load db (yet)
If I wanted to run some test, I can still inject the dependencies when constructing my instance, or I can rely on a test-config object or I could use the setDb and setCurl methods, too:
$foo->setCurl($testCurl);
Sticking to the first way of constructing the instance, I can safely say that, if I only invoke the getViaCurl method, the Db class will never be loaded.
The getViaDb method is a bit more elaborate (as is the getDb method). I don't recommend you working with methods like that, but it's just to show you how flexible this approach can be. I can pass an array of parameters to the getViaDb method, which can contain a custom connection. I can also pass a boolean that'll control what I do with that connection (use it for just this one call, or assign the connection to the MyService instance.
I hope this isn't too unclear, but I am rather tired, so I'm not all too good at explaining this stuff ATM.
Here's the code, anyway... it should be pretty self explanatory.
class MyService
{
private $curl = null;
private $db = null;
private $conf = null;
public function __construct(Config $configObj, Curl $curlObj = null, Db $dbObj = null)
{
$this->conf = $configObj;//you'll see why I do need this in a minute
$this->curl = $curlObj;//might be null
$this->db = $dbObj;
}
public function getViaCurl(Something $useful)
{
$curl = $this->getCurl();//<-- this is where the magic happens
return $curl->request($useful);
}
public function getViaDb(array $params)
{
if (isset($params['custom']))
{
$db = $this->getDb($params['custom'], $params['switch']);
}
else
{//default
$db = $this->getDb();
}
return $db->query($params['request']);
}
public function getCurl()
{//return current Curl, or load default if none set
if ($this->curl === null)
{//fallback to default from $this->conf
$this->curl = new Curl($this->conf->getSection('CurlConf'));
}
return $this->curl;
}
public function setCurl(Curl $curlObj)
{//inject after instance is created here
if ($this->curl instanceof Curl)
{//close current connection
$this->curl->close();
}
$this->curl = $curlObj;
}
public function setDb(Db $dbObj)
{
if ($this->db instanceof Db)
{//commit & close
$this->db->commit();
$this->db->close();
}
$this->db = $dbObj;
}
//more elaborate, even:
public function getDb(Db $custom = null, $switch = false)
{
if ($custom && !!$swith === true)
{
$this->setDb($custom);
return $this->db;
}
if ($custom)
{//use custom Db, only this one time
return $custom;
}
if ($this->db === null)
{
$this->db = new Db($this->conf->getSection('Db'));
}
return $this->db;
}
}