I am starting an application built on zend framework; this application should be able to make use of multiple data sources other than a database; a webservice for example.
I have been reading on how to structure my model so as to allow for this scenario. I have come across various concepts that seem to provide a solution to this (DataMapper Pattern, Service Pattern, Adapter Layer, etc). However, I am still confused on how to put this all together into a reusable and scalable codebase.
I have worked with zend framework before and will normally work with a mysql table. If for example, I have a Users table...I simple have a Users class in my model that contains business logic of the the user domain and a User class extending Zend_Db_Table_Row_Abstract representing a row in the user table.
How best do I organize my model and code base such that i can still call $users->fetchAll() and get a collection of user objects regardless of what my datasource is?
It basically works the same as you did before, just that instead of a Zend_Db_Table_Gateway you use a My_UserGateway_Whatever, e.g. create an interface first:
interface UserGateway
{
/**
* #return array
* #throws UserGatewayException
*/
public function findAll();
}
We dont want Exceptions from the concrete Gateways to appear in the consuming code, so we add the UserGatewayException as a catch all:
class UserGatewayException extends RuntimeException
{}
Then add a class implementing that interface:
class My_UserGateway_Webservice implements UserGateway
{
public function findAll()
{
try {
// insert logic to fetch from the webservice
return $userData;
} catch (Exception $e) {
throw new UserGatewayException($e->getMessage(), $e->getCode, $e);
}
}
// … more code
}
Likewise, if you want to use a Database source, you can write an adapter for the Zend_Db_* classes, e.g.
class My_UserGateway_Database implements UserGateway
{
private $tableDataGateway;
public function __construct(Zend_Db_Table_Abstract $tableDataGateway)
{
$this->tableDataGateway = $tableDataGateway;
}
public function findAll()
{
try {
return $this->tableDataGateway->select()->blah();
} catch (Exception $e) {
throw new UserGatewayException($e->getMessage(), $e->getCode, $e);
}
}
// … more code
}
If you need another Data Provider, make sure they implement that interface, so you can rely on the findAll method to be there. Make your consuming class depend on the interface, e.g.
class SomethingUsingUsers
{
private $userGateway;
public function __construct(UserGateway $userGateway)
{
$this->userGateway = $userGateway;
}
public function something()
{
try {
$users = $this->userGateway->findAll();
// do something with array of user data from gateway
} catch (UserGatewayException $e) {
// handle Exception
}
}
// … more code
}
Now, when you create SomethingUsingUsers you can easily inject one or the other Gateway into the constructor and your code will work regardless of which Gateway you used:
$foo = SomethingUsingUsers(
new My_UserGateway_Database(
new Zend_Db_Table('User')
)
)
or, for the Webservice:
$foo = SomethingUsingUsers(
new My_UserGateway_Webservice(
// any additional dependencies
)
)
Related
I have a data transformer for forms to transform floats into a string that looks like a time or duration. For example, 165.3 in the database is transformed to '2:45.3' in my forms, and vice versa going back into the database. This works good for all of my forms, but I want the data to be presented in the string format in twig templates so users can see that something has a duration of '2:45.3' instead of 165.3.
In order to do this, I extended twig to have a filter secs2time, and used the data transformer (rather than rewriting the same logic) in the twig extension to change the float into the string. It looks something like this:
public function secs2time($secs)
{
return (new FloatToTimeTransformer)->transform($secs);
}
This works, but I get the feeling there is a better way. Is it bad to import and use FloatToTimeTransformer in my twig extension? Symfony transformers import at least two other classes (DataTransformerInterface & TransformationFailedException) that aren't needed in my twig extension. Will this hurt performance? What would be a better way?
I think this should be a common question, since you would almost never transform data to put into a database yet never show that data to users.
Your FloatToTimeTransformer transform method could call a static class method stocked in a TimeUtil class.
Then your twig extension could now call the same external static class method.
Note: this method should throw specific exception catched in your dataTransformer
In a Model or Utils src directory
namespace \Model
use \DateTime
class TimeUtils
{
public static secToTime($aFloat)
{
$time = "";
...// implements your transformation
return $time;
}
public static timeToSec($aFloat)
{
$time = "";
...// implements your transformation
return $time;
}
}
In your twig Extension
use Model\TimeUtils
...
public function secs2time($secs)
{
return TimeUtils::secToTime($sec)
}
In your dataTransformer
use Model\TimeUtils
...
public function transform($secs)
{
try {
return TimeUtils::secToTime($sec)
} catch (\Exception $e) {
throw new TransformationFailedException($e->getMessage());
}
}
public function reverseTransform($time)
{
try {
return TimeUtils::timeToSec($time)
} catch (\Exception $e) {
throw new TransformationFailedException($e->getMessage());
}
}
I am learning OO SOLID principles and design patterns and I want to do some practice on it. So I get the one problem from my ongoing project and try to design it. Please check whether it is implemented correctly or it's over-engineered or I implement it poorly. Your response is most important.
Problem
I have to manage sms and email campaigns in one system. I meant to say storing it in database and retrieving it etc.
So I think there will be something specific to the Campaign like created date status etc. Thus I have made the class named campaignmodel which is responsible for some common functions related to campaign
Class CampaignModel
{
public function save($data)
{
// add campaign specific data
// save the campaign.
}
public function get()
{
// select the row from database and return it.
}
}
then I make smscampaign and email campaign
class SMSCampaignModel extends CampaignModel
{
public function save($data)
{
// add sms specific data and
parent::save($data);
}
public function gets()
{
//fire the query to get the sms campaigns and returns it.
}
}
class EmailCampaignModel extends CampaignModel
{
public function save($data)
{
// add email specific data
parent::save($data);
}
public function gets()
{
//fire the query to get the email campaigns and returns it.
}
}
Now every campaign will have recipients and we have to store each recipient's status like he opens mail or mail/sms sent or failed etc. I think we will send the campaigns with many emails or numbers so I decided to create different database table for storing such details such as sms_campaign_log, email_campaign_log etc. I have created the interface for it
interface CampaignLogs
{
function insert_log();
function get_details();
}
class SmsCampaignLogs implements CampaignLogs
{
public function insert_log($data)
{
// get the number and status save it into the sms logs table.
}
public function get_details($campagin_id)
{
// get the logs from campagin table and return it.
}
}
class EmailCampaignLogs implements CampaignLogs
{
public function insert_log($data)
{
// get the number and status save it into the email logs table.
}
public function get_details($campagin_id)
{
// get the logs from campagin table and return it.
}
}
and lastly I think now I should use strategy pattern to implement it(I don't know whether it is correct or not).
class Campaign
{
private $log;
private $campaign_type;
public function __construct($campaign, $logger)
{
$this->campaign_type = $campaign;
$this->log = $logger;
}
public function save($data)
{
$this->campagin_type->save();
}
public function gets()
{
$this->campaign_type->gets();
}
public function log($data)
{
$this->log->insert_log($data);
}
public function get_campaign_details($campaign_id)
{
$this->log->get_details($campaign_id);
}
}
now Implementation code.
$campaign = new SmCampaignModel();
$logger = new SmsCampaignLogs();
$c = new Campaign($campagin,$logger);
$c->save($data);
$c->get($campaign_id);
$c->get_campaing_details();
Then I think if strategy pattern needed.
Simply, I can implement:
$campaign = new SmCampaignModel();
$logger = new SmsCampaignLogs();
$campaign->save($data);
$campaign->get($campaign_id);
$logger->get_campaing_details($campaign_id);
So I am now totally confused. I want your opinion on whether I applied SOLID principles correctly in my design (and strategy pattern is needed/used properly) or not.
In this case your Campaign class is only a Facade. No Strategy is in use.
You are actually using the Facades pattern and not the Strategy. Your Campaign class doesn't have a behavior of it own. It merely delegates its behavior to subsystem components. It is not a bad thing, but it makes your code somehow harder to maintain. It is fine in terms of information hiding.
There is not right or wrong in the aspect of OOD. Design patterns are not must to be included if no reason is presented. You should ask yourself: "What is my main problem, and does this solve it?". "Will there be a reason to the code to be changed often?".
Because we are all sometimes tempted to overuse design patterns, I would like to show you how making a simple OO relationship will do just fine, and will even be easier to read and maintain.
abstract class Campaign {
protected $ages;
protected $countries;
protected $dailyBudget;
protected $recipientsStatus = array(); // associative array or a composition of Recipients object
public function startCampaign()
{
// check there is not another run
$this->executeCampaign();
$this->collectRecipientsStatus();
$this->generateStatistics();
}
abstract protected function executeCampaign();
abstract protected function collectRecipientsStatus();
abstract protected function generateStatistics();
}
class EmailCampaign extends Campaign {
protected $addresses;
protected function executeCampaign()
{
$this->filterEmailsByCampaignData();
$this->sendEmails();
}
protected function filterEmailsByCampaignData()
{
// populate $this->addresses based on ages, countries etc.
}
protected function sendEmails()
{
// send email to addresses
}
protected function collectRecipientsStatus()
{
// collect status and fill parent $recipientsStatus
}
protected function generateStatistics()
{
// generate statistics
}
}
Campaign now is a class with data and behavior. We don't have to decouple it into components like Model and Logs. This will work just fine. However, if you ever find yourself with a bit more complicated Recipients array (too many key values or dimensional array code smells), then you might decouple it into another set of classes. But this should happen with evolution of the code. We simply cannot foresee everything in advance.
By the way, the only pattern I used is the lightweight Template Method, and inheritance feature of OOP.
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.
So I have understood how interfaces and abstraction work in PHP, I just don't see the point for example, of having a interface if it just sets a guide and requires implemented objects to have certain methods. Especially since the interface is not even getting instantiated.
This also goes with abstraction, I just can't apply it to my code and see it as such a great thing. When I am trying to create objects on a bigger scale to interact with each other in order to figure out interfaces, each class ends up passing information back and forth, but never is the interface touched.
So what I'm asking is if you guys have any advice or links to outside sources that is good at explaining this kind of thing.
Here's one simple example. Creating interfaces and abstract classes allows you to ensure an object adhears to a common API. See the example below.
interface iCar
{
function drive();
}
abstract class Car implements iCar
{
public $make = 'Generic';
public function drive()
{
printf("I'm driving in my %s%s", $this->make, PHP_EOL);
}
}
class FordTruck extends Car
{
public $make = "Ford";
}
class Porsche extends Car
{
public $make = 'Porsche';
public function drive()
{
printf("I'm speeding around in my %s%s", $this->make, PHP_EOL);
}
}
class Yugo extends Car
{
public $make = 'Yugo';
public function drive()
{
printf("I'm pushing my %s around town%s", $this->make, PHP_EOL);
}
}
function drive(iCar $car)
{
$car->drive();
}
$car1 = new FordTruck;
$car2 = new Porsche;
$car3 = new Yugo;
drive($car1);
drive($car2);
drive($car3);
Even if you don't specify the type of input parameter on the drive() function, you can check if the input is an instanceof an iCar
function drive($car)
{
if ($car instanceof iCar)
$car->drive();
}
Another example would be building a caching interface in your application. You can specify that all cache engines support the same methods for reading/writing/invalidating objects in the cache without knowing (or caring) about the actual implementation of a particular cache engine.
I could give you the simplest as possible example.
Assume you want a feature that allow your site to login with Facebook/Twitter
# here's your interface/abstract class
interface Auth_Adapter {
public function auth();
}
# now your Facebook
class Auth_Adapter_Facebook implements Auth_Adapter {
public function login() {
# include facebook-sdk and auth
}
}
# Twitter
class Auth_Adapter_Twitter implements Auth_Adapter {
public function login() {
# include twitter-oauth and auth
}
}
Imagine when someone try to use Facebook/Twitter thing They can simply call
$adapter = new Auth_Adapter_Facebook;
$adapter->login();
$adapter = new Auth_Adapter_Twitter;
$adapter->login();
As you can see both adapters use the same login interface. What's happen if in the future you have to include 'Pinterest' login? Your code still work as long as you implement the same interface.
EDIT: More explanations
Here's the reason why you have to use interface or abstract
# I use `type-hinting` here. So I can ensure that only object that implements `Auth_Adapter` will allow. Without this implementation someone might pass some other object that doesn't have `login` method in. But in our case we don't have to worry about that.
public function perform_login(Auth_Adapter $adapter) {
$adapter->login();
}
I'm working with a domain model, in which I have a Reservation class:
class Reservation
{
public function changeStatus($status) { ... }
}
Because the changeStatus() method should only be called in a context where all appropriate notifications are sent (emails, ...) I would like to restrict the call to this method to a ReservationService:
class ReservationService
{
public function confirmReservation(Reservation $reservation)
{
$reservation->changeStatus(Reservation::STATUS_CONFIRMED);
// commit changes to the db, send notifications, etc.
}
}
Because I'm working with PHP, there is no such concept as package visibility or friend classes, so my changeStatus() method is just public and therefore callable from anywhere in the application.
The only solution I found to this problem, is to use some kind of double dispatch:
class Reservation
{
public function changeStatus(ReservationService $service)
{
$status = $service->getReservationStatus($this);
$this->setStatus($status);
}
protected function setStatus($status) { ... }
}
The potential drawbacks are:
That complicates the design a bit
That makes the entity aware of the Service, no sure whether that's actually a drawback or not
Do you guys have any comment on the above solution, or a better design to suggest to restrict access to this changeStatus() method?
Use an interface which enforces the context you need:
interface INotifiable {
public function updated( $reservation );
}
class Reservation {
public function changeStatus( $status, INotifiable $notifiable ){
$this->setStatus( $status );
$notifiable->updated( $this );
}
}
class EmailNotifier implements INotifiable {
public function updated( $reservation ){
$this->sendUpdateEmail( $reservation ); //or whatever
}
}
The reservation then doesn't need to know anything about the service. An alternative would be to define events on Reservation, but that's added complexity you probably don't need.
You can send messages from one domain entity to another. Only objects that are capable of producing certain messages will call the method in question. A code snippet is below. This solution is for projects where dependency injection is a sort of religion like here PHP+DDD.
The Reservation Service gets a message factory. The factory is injected through the constructor method. Objects that don't have this factory cannot issue this sort of messages. (Of course you must restrict object instantiation to factories.)
class Domain_ReservationService
{
private $changeStatusRequestFactory;
public function __construct(
Message_Factory_ChangeStatusRequest $changeStatusRequestFactory
) {
$this->changeStatusRequestFactory = $changeStatusRequestFactory;
}
public function confirmReservation(Domain_Reservation $reservation) {
$changeStatusRequest = $changeStatusRequestFactory->make(
Reservation::STATUS_CONFIRMED
);
$reservation->changeStatus($changeStatusRequest);
// commit changes to the db, send notifications, etc.
}
}
The Reservation object checks the contents of the message an decides what to do.
class Domain_Reservation
{
public function changeStatus(
Message_Item_ChangeStatusRequest $changeStatusRequest
) {
$satus = $changeStatusRequest->getStatus();
...
}
}
Message object is a DDD value object. (Sometimes it acts like a strategy.)
class Message_Item_ChangeStatusRequest
{
private $status;
public function __construct( $status ) {
$this->$status = $status;
}
public function getStatus() {
return $this->$status;
}
}
This factory produces messages.
class Message_Factory_ChangeStatusRequest
{
public function make($status) {
return new Message_Item_ChangeStatusRequest ($status);
}
}
All domain objects are produced by this layer factory.
class Domain_Factory
{
public function makeReservationService() {
return new Domain_ReservationService(
new Message_Factory_ChangeStatusRequest()
);
}
public function makeReservation() {
return new Domain_Reservation();
}
}
The classes above can be used in your application as follows.
$factory = new Domain_Factory();
$reservationService = $factory->makeReservationService();
$reservation = $factory->makeReservation();
$reservationService->confirmReservation($reservation);
But I don't see why you don't want to use $reservation->beConfirmed() instead of passing status constants.
It actually sounds like this is missing a very important concept, namely a ProcessManager. A ProcessManager represents a distributed business transaction spanning multiple contexts. In reality it is a simple Finite State Machine.
An example workflow:
A PlaceReservationCommand is sent to the ReservationContext, which handles it and publishes a ReservationWasPlacedEvent that the ReservationProcessManager is subscribed to.
The ReservationProcessManager receives the ReservationWasPlacedEvent and validates that it can transition to the next step. I then sends a NotfiyCustomerAboutReservationCommand to the NotificationContext. It now listens to ReservationNotificationFailedEvent and ReservationNotificationSucceededEvent.
Now the ReservationProcessManager sends the ConfirmReservationCommand to the ReservationContext only when it received the ReservationNotificationSucceededEvent.
The trick here is that there is no status field in Reservation. The ProcessManager is responsible of tracking the state of this business transaction. Most likely there is a ReservationProcess Aggregate that contains the statuses like ReservationProcess::INITIATED,
ReservationProcess::CUSTOMER_NOTIFICATION_REQUESTED, ReservationProcess::CUSTOMER_NOTIFIED, ReservationProcess::CONFIRMATION_REQUESTED and ReservationProcess::CONFIRMED. The last state indicates a finite state that marks the process as done.
One of the things that the Symfony2 and FLOW3 frameworks have adopted is tagging their stable public API with an #api annotation comment.
While this is not exactly what you're looking for, it comes close. It documents the parts of your API that users can rely on. Plus your entity does not have to know about the service, avoiding the evil circular dependency.
Example:
class Request
{
/**
* Gets the Session.
*
* #return Session|null The session
*
* #api
*/
public function getSession()
{
return $this->session;
}
}