PHP MVC with OOP - php

I started to read symfony book and try to implement on my own a really basic mvc framework for learning purpose. I use some components of symfony.
By now i have an index.php file like this
<?php
require_once 'vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$uri = $request->getPathInfo();
$frontController = new FrontController($uri);
Then i have a frontController.php file that have a basic routing
<?php
class FrontController
{
private $uri;
public function __construct($uri)
{
$this->uri = $uri;
$this->setRoute();
}
private function setRoute()
{
$request = explode("/", trim($_SERVER['REQUEST_URI']));
$controller = !empty($request[1])
? ucfirst($request[1]) . "Controller"
: "IndexController";
$action = !empty($request[2])
? $request[2] . "Action"
: "indexAction";
$parameters = !empty($request[3])
? intval($request[3])
: null;
$response = call_user_func(array($controller, $action), $parameters);
$this->sendResponse($response);
}
private function sendResponse($response)
{
$response->send();
}
}
Then i have a series of controller (i.e. ordersController.php)
<?php
use Symfony\Component\HttpFoundation\Response;
class OrdersController
{
public function ordersAction()
{
//take data from model
$order = new OrdersModel;
$orders = $order->get_all_orders();
$html = self::render_template('templates/list.php', array('orders' => $orders));
return new Response($html);
}
public function render_template($path, array $args = null)
{
if(!empty($args))
extract($args);
ob_start();
require $path;
$html = ob_get_clean();
return $html;
}
}
Now, although i know that what i have done can be improved , i would like to inject the right model when i instantiate a controller class in the FrontController class without instantiate the model instance in the method where i use that.
How can i achieve this?
And if you have some suggests to give me, please do that because i want to improve my skills.

What are you looking for is a Dependency Injection Container (DIC). It's not only about injecting "models" (actually I hate this word because of ORMs confusion), but rather about injecting any kind of services. DIC incapsulates a service instantiation logic. Then, you can use the DIC inside your FrontController to get required services and pass it to the controller. Also, the DIC introduces some kind of indirection. You don't have to know about actual service implementations, but only about its names.
The simplest DIC implementation for PHP is Pimple. It's very small project, I strictly recommend to look at its source code. Mentioned in the comments Silex uses Pimple under the hood.
To conclude, you have to do only two things:
Register a service in the DIC.
Inject this service (or a few services) to the controller.
Basic example of registering a service is:
$container['orders'] = function($c) {
return new OrdersModel();
};
Usually, FrontController has an access to such $container object as a member or FrontController can be a container by itself. Pretty often such object is called Application. Registering of all existing services have to be done before a dispatching code. Since registering a service doesn't require service instantiation (registering a service is just creation of a closure) you can freely register hundreds of such services without an extra load.
Now, you need to somehow specify controller's dependencies. One way to do it - using static field in the controller class.
class OrdersController {
public static $dependencies = ['orders', 'otherService'];
private $ordersModel;
private $otherService;
public function __construct($ordersModel, $otherService) {
$this->ordersModel = $ordersModel;
$this->otherService = $otherService;
}
}
And then you have to read list of dependencies of each controller before instantiation in the FrontController:
...
$ctrlClass = !empty($request[1])
? ucfirst($request[1]) . "Controller"
: "IndexController";
$services = [];
foreach ($ctrlClass::dependencies as $serviceName) {
$services[] = $this->container[$serviceName];
}
// Here could be better way - PHP is not my native lang.
$reflection = new ReflectionClass($classname);
$controller = $reflection->newInstanceArgs($services);
...
That is, by the end you'll get a controller instance with all required dependencies inside. Such code very easy to test.
UPD: Also, a service A can require a service B as internal dependency. Such dependency easy to satisfy in the Pimple approach:
$container['serviceA'] = function($c) {
return new ServiceA($c['serviceB']);
};
$container['serviceB'] = function($c) {
return new ServiceB();
};
In such case if your controller requires ServiceA it indirectly depends on ServiceB too. However, you don't have to resolve such indirect dependency manually.
UPD2: Further reading:
Dependency Injection with Pimple
Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler

Related

Using Dependency Injection in MVC PHP

For loading classes I use PSR-4 autoload. In my index.php I use FastRoute component. In that index.php I create $db connection and pass it to the controllers.
call_user_func_array([new $class($db), $method], $vars);
In the controllers I receive it and pass it to the models and use it there.
I know that is bad approach. DB connection that was created in index.php how can I use it in my models not pass it through controllers etc and without singleton instance of a connection? How can I use DI or DIC to set up db connection in index.php and get in all models?
index.php:
<?php
require_once 'vendor/autoload.php';
$db = new DbConnection(new DbConfig($config));
$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
$r->addRoute('GET', '/users', 'UserController/actionGetAllUsers');
$r->addRoute('GET', '/users/{id:\d+}', 'UserController/actionGetUserById');
});
// Fetch method and URI from somewhere
$httpMethod = $_SERVER['REQUEST_METHOD'];
$uri = $_SERVER['REQUEST_URI'];
// Strip query string (?foo=bar) and decode URI
if (false !== $pos = strpos($uri, '?')) {
$uri = substr($uri, 0, $pos);
}
$uri = rawurldecode($uri);
switch ($routeInfo[0]) {
case FastRoute\Dispatcher::NOT_FOUND:
// ... 404 Not Found
break;
case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
$allowedMethods = $routeInfo[1];
// ... 405 Method Not Allowed
break;
case FastRoute\Dispatcher::FOUND:
$handler = $routeInfo[1];
$vars = $routeInfo[2];
list($class, $method) = explode("/", $handler, 2);
$module = strtolower(str_replace('Controller', '', $class));
$class = 'Vendorname\\' . $module . '\\controllers\\' . $class;
$routeInfo = $dispatcher->dispatch($httpMethod, $uri);
call_user_func_array([new $class($db), $method], $vars);
break;
}
controller:
class UserController extends BaseApiController
{
protected $db;
public function __construct($db)
{
$this->db = $db;
parent::__construct();
}
public function actionGetAllUsers()
{
$model = new User($this->db);
echo json_encode($model->getAllUsers());
}
model:
class User
{
protected $db;
public function __construct($db)
{
$this->db = $db;
}
public function getAllUsers()
{
$stmt = $this->db->prepare('SELECT * FROM user');
$stmt->execute();
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
}
First of all, you have to understand, that "Dependency Injection" and "Dependency Injection Container" are two different things:
DI is a technique, that you use to separate two classes in OOP.
DIC is a piece of software, that assembles object graphs
It seems, that you are already, that you way of passing of DB connection is bad. The exact technical reason can be described as violation of LoD. Basically, your $db variable is crossing two layer boundaries.
But, the solution for this is not: "just add DI container". That won't solve the problem. Instead, you should improve the design of the model layer. You might do a quick read here, but I will give a bit shorter version here:
separate your persistence logic from your domain logic
controller should depend on service, which would be how controllers (that exist in UI layer) interact with the business logic.
the simplest way yo separate business logic is in three parts: datamappers (handle persistence logic), domain objects (for business rules) and services (interaction between domain objects and persistence objects)
The DB should be a dependency for a datamapper, that is specifically made for handling user instances (more here). That mapper should be either a direct dependency of your "account management service" (think of a good name) or be constructed using a factory inside that service (and this factory then would be the dependency of the service.
P.S. As for "which DIC to choose", I personally go with Symfony's standalone container, but that's purely personal preference.

ZF2 - Mocking service requested in Module.php

I am trying to test a controller of my ZF2 application. Suppose this controller is in my A module.
In the onBootstrap method of the Module.php of the module A I am using the service manager to retrieve a service of another module, say B, that I am not loading.
How can set a mock of the requested service in the service manager? Mind that I can not use $this->getApplicationServiceLocator() to do this in my test, since this is already calling the Module.onBootstrap method of my A module.
To post some code, this is what I am doing at the moment
bootstrap.php
namespace Application;
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
use RuntimeException;
class Bootstrap
{
protected static $serviceManager;
public static function init()
{
$modulePath = static::findParentPath('module');
$vendorPath = static::findParentPath('vendor');
if (is_readable($vendorPath . '/autoload.php')) {
$loader = include $vendorPath . '/autoload.php';
} else {
throw new RuntimeException('Cannot locate autoload.php');
}
$config = [
'modules' => [
'Application',
],
'module_listener_options' => [
'module_paths' => [
$modulePath,
$vendorPath
]
]
];
$serviceManager = new ServiceManager(new ServiceManagerConfig());
$serviceManager->setService('ApplicationConfig', $config);
$serviceManager->get('ModuleManager')->loadModules();
static::$serviceManager = $serviceManager;
}
protected static function findParentPath($path)
{
$dir = __DIR__;
$previousDir = '.';
while (!is_dir($dir . '/' . $path)) {
$dir = dirname($dir);
if ($previousDir === $dir) {
return false;
}
$previousDir = $dir;
}
return $dir . '/' . $path;
}
public static function getServiceManager()
{
return static::$serviceManager;
}
}
Bootstrap::init();
my actual test class
namespace Application\Functional;
use Application\Bootstrap;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
class ValidateCustomerRegistrationTest extends AbstractHttpControllerTestCase
{
public function setUp()
{
$serviceManager = Bootstrap::getServiceManager();
$applicationConfig = $serviceManager->get('ApplicationConfig');
$this->setApplicationConfig($applicationConfig);
parent::setUp();
}
public function testRegisterValidUserWithOnlyEquomobiliData()
{
$this->getApplicationServiceLocator();
}
}
Module.php simplified
namespace Application
Class Module
{
public function onBootstrap(MvcEvent $e)
{
$serviceManager = $e->getApplication()->getServiceManager();
$service = $serviceManager->get('Service\From\Other\Module');
}
}
There is not enough data here to help you directly. It would be more useful to have the function ValidateCustomerRegistrationTest->getApplicationServiceLocator() for starters.
I hope I can help you indirectly.
Refactoring could be helpful
When I am writing a unit test I start with a few personal rules.
Only test the code being testing. Mock EVERYTHING else. No need to test something that should have it's own tests already.
How a function works should not be important. Only the input/output. This keeps your test viable even when the core of a function changes drastically.
/**
* #param MvcEventInterface $event
* #param Service\From\Other\ModuleInterface $service
*
* #return boolean
*/
public function onBootstrap(MvcEventInterface $event, Service\From\Other\ModuleInterface $service)
{
return true;
}
Then in the test class:
public function testOnBootstrap(){
$eventMock = $this->getMock(MvcEventInterface::class);
$serviceMock = $this->getMock(ModuleInterface::class);
$module = new Module();
$result = $module->onBootstrap($eventMock, $serviceMock);
$this->assertTrue($result);
}
* I clearly do not know what you are trying to test
When refactoring is not an option
There are two mock types that can help out, that occur to me right off the bat, mock, and mockBuilder. Take a look at the PHPUnit documentation for Test Doubles.
$serviceMock = $this->getMockBuilder(ServiceManager::class)
->disableOriginalConstructor()
->setMethods([
'__construct',
'get'
])
->getMock();
$serviceMock
->expects($this->any())
->method('get')
->will($this->returnValue(
$this->getMock(ModuleManagerInterface::class)
));
Good luck, I hope you can let us know if we can help you more specifically. I would also recommend looking into SOLID Principles of Object Oriented Programing. One of many programming principles that should make your code clean, easy to extend, and easy to test.
One way to accomplish what you're trying to do may be to force an overwrite of the service in the Service Manager before dispatching the request to your test controller. Here's an example of how to do that (NOTE: since what you're doing is during the module's bootstrap process, the example may not translate 100% to your situation).
Like other people mentioned: you may still want to double-check whether you can refactor to a cleaner approach, but that's another story.
In order to mock the service manager and the calls made with it you can use mockery https://github.com/mockery/mockery. This library is framework agnostic so even if you use PHPUnit or an other tool it should work.
The bad way to use it but it should solve your problem fast is to overload the class using mockery('overload:myclass') where myclass is the instance of the service manager.
The best way to use the library is to use the depedency injection pattern to inject a mock of the service locator when your app is boostraping for the tests.
An easy way to go with it is to create a factory for your controller, inject the service locator inside. Then in your test you init the controller with the mock of the service locator as a parameter.

Constructor Injection in Restler

Is it possible to do pass along arguments to an API class for the sake of constructor injection? For example, in index.php I have the following:
$r->addAPIClass('Contacts', '');
And Contacts.php looks something like this:
class Contacts
{
private $v;
public function __construct(Validation v)
{
$this->v = v;
}
}
How would I do this with Restler?
Restler 3 RC5 has a dependency injection container called Scope which is responsible for creating new instances of any class from their name, it comes in handy for this purpose.
Once you register the Contacts class with its dependency using the register method, it will be lazy instantiated when asked for
<?php
include '../vendor/autoload.php';
use Luracast\Restler\Scope;
use Luracast\Restler\Restler;
Scope::register('Contacts', function () {
return new Contacts(Scope::get('Validation'));
});
$r = new Restler();
$r->addAPIClass('Contacts', '');
$r->handle();
By using Scope::get('Validation') we can register Validation too if it has any dependencies

DI container for an object that contains an object that has injected dependency

Using pimple as my DI container, I have been bravely refactoring small classes to rely on DI injection, eliminating the hard-coded dependencies I could see would be easily removed.
My methodology for this task is very simple, but I have no clue if it is proper as I have very little experience with DI and Unit-testing aside from what I learned here in past month.
I created a class, ContainerFactory that subclasses pimple, and in that subclass have created methods that simply returns container for specific object.
The constructor calls the proper creator method depending on type:
function __construct($type=null, $mode = null){
if(isset($type)){
switch ($type) {
case 'DataFactory':
$this->buildDataFactoryContainer($mode);
break;
case 'DbConnect':
$this->buildDbConnectContainer($mode);
break;
default:
return false;
}
}
}
The method signature for container object creation is as follows:
public function buildDataFactoryContainer($mode=null)
The idea being that I can set $mode to test when calling this container, and have it load test values instead of actual runtime settings. I wanted to avoid writing separate container classes for testing, and this is an easy way I thought to not have test related code all over.
I could instead have subclassed ContainerFactory ie: ContainerFactoryTesting extends ContainerFactory and override in that instead of mixing test code with app code and cluttering method signatures with $mode=null, but that is not the point of this post. Moving on, to create a container for a particular object, I simply do this:
// returns container with DataFactory dependencies, holds $db and $logger objects.
$dataFactoryContainer = new ContainerFactory('DataFactory');
// returns container with test settings.
$dataFactoryTestContainer = new ContainerFactory('DataFactory','test');
// returns container with DbConnect dependencies, holds dbconfig and $logger objects.
$dbConnectContainer = new ContainerFactory('DbConnect');
I just ran into an issue that makes me suspect that the strategy I am building upon is flawed.
Looking at the above, DataFactory contains $db object that holds database connections.
I am now refactoring out this dbclass to remove its dependencies on a $registry object,
but how will I create $dataFactoryContainer, when I add $db object that needs $dbConnectContainer?
For example, in datafactory container I add a dbconnect instance, but IT will now need a container passed to it ...
I realise my English is not that great and hope I have explained well enough for a fellow SO'er to understand.
My question is two-fold, how do you guys handle creating objects for dependencies that themselves contain dependencies, in a simple manner?
And .. how do you separate container configuration for creation of objects for testing purposes?
As always, any comment or link to relevant post appreciated.
You should not create separate containers for everything, but instead use a single container. You can create a container "Extension" which basically just sets services on your container.
Here is an extensive example of my suggested setup. When you have a single container, recursively resolving dependencies is trivial. As you can see, the security.authentication_provider depends on db, which depends on db.config.
Due to the laziness of closures it's easy to define services and then define their configuration later. Additionally it's also easy to override them, as you can see with the TestExtension.
It would be possible to split your service definitions into multiple separate extensions to make them more reusable. That is pretty much what the Silex microframework does (it uses pimple).
I hope this answers your questions.
ExtensionInterface
class ExtensionInterface
{
function register(Pimple $container);
}
AppExtension
class AppExtension
{
public function register(Pimple $container)
{
$container['mailer'] = $container->share(function ($container) {
return new Mailer($container['mailer.config']);
});
$container['db'] = $container->share(function ($container) {
return new DatabaseConnection($container['db.config']);
});
$container['security.authentication_provider'] = $container->share(function ($container) {
return new DatabaseAuthenticationProvider($container['db']);
});
}
}
ConfigExtension
class ConfigExtension
{
private $config;
public function __construct($configFile)
{
$this->config = json_decode(file_get_contents($configFile), true);
}
public function register(Pimple $container)
{
foreach ($this->config as $name => $value) {
$container[$name] = $container->protect($value);
}
}
}
config.json
{
"mailer.config": {
"username": "something",
"password": "secret",
"method": "smtp"
},
"db.config": {
"username": "root",
"password": "secret"
}
}
TestExtension
class TestExtension
{
public function register(Pimple $container)
{
$container['mailer'] = function () {
return new MockMailer();
};
}
}
ContainerFactory
class ContainerFactory
{
public function create($configDir)
{
$container = new Pimple();
$extension = new AppExtension();
$extension->register($container);
$extension = new ConfigExtension($configDir.'/config.json');
$extension->register($container);
return $container;
}
}
Application
$factory = new ContainerFactory();
$container = $factory->create();
Test Bootstrap
$factory = new ContainerFactory();
$container = $factory->create();
$extension = new TestExtension();
$extension->register($container);

How to remove multiple instances and just have one instance while multiple function calls in php?

public function getHelperInstance()
{
$user = new Helper();
$user->set($result['data']);
return $user;
}
I am calling getHelper() class multiple times and if $user is not empty than am calling getHelperInstance(), now in my case getHelperInstance() always creates a new instance of Helper() class and so every time I call getHelperInstance() function am creating a new instance of Helper() so is there any way where can I can just create one instance of Helper() and use it multiple times instead of creating a new instance everytime. Any suggestions !!!
public function getHelper()
{
$user = array();
if (!empty($user))
{
$user = $this->getHelperInstance();
}
return $user;
}
Here is what Erich Gamma, one of the Singleton pattern's inventors, has to say about it:
"I'm in favor of dropping Singleton. Its use is almost always a design smell"
So, instead of a Singleton, I suggest to use Dependency Injection.
Create the Helper instance before you create what is $this. Then set the helper instance to the $this instance from the outside, either through a setter method or through the constructor.
As an alternative, create a Helper broker that knows how to instantiate helpers by name and pass that to the $this instance:
class HelperBroker
{
protected $helpers = array();
public function getHelper($name)
{
// check if we have a helper of this name already
if(!array_key_exists($name, $this->helpers)) {
// create helper and store for later subsequent calls
$this->helpers[$name] = new $name;
}
return $this->helpers[$name];
}
}
This way you can lazy load helpers as needed and will never get a second instance, without having to use Singleton. Pass an instance of the broker to every class that needs to use helpers.
Example with a single helper
$helper = new Helper;
$someClass = new Something($helper);
and
class Something
{
protected $helper;
public function __construct($helper)
{
$this->helper = $helper;
}
public function useHelper()
{
$return = $this->helper->doSomethingHelpful();
}
}
Inside $something you can now store and access the helper instance directly. You don't need to instantiate anything. In fact, $something doesn't even have to bother about how a helper is instantiated, because we give $something everything it might need upfront.
Now, if you want to use more than one helper in $someClass, you'd use the same principle:
$helper1 = new Helper;
$helper2 = new OtherHelper;
$something = new Something($helper1, $helper2);
This list will get rather long the more dependencies you insert upfront. We might not want to instantiate all helpers all the time as well. That's where the HelperBroker comes into play. Instead of passing every helper as a ready instance to the $something, we inject an object that knows how to create helpers and also keeps track of them.
$broker = new HelperBroker;
$something = new Something($broker);
and
class Something
{
protected $helperBroker;
public function __construct($broker)
{
$this->helperBroker = $broker;
}
public function doSomethingHelpful()
{
$return = $this->getHelper('foo')->doSomethingHelpful();
}
public function doSomethingElse()
{
$return = $this->getHelper('bar')->doSomethingElse();
}
}
Now $something can get the helpers it needs, when it needs them from the broker. In addition, any class that needs to access helpers does now no longer need to bother about how to create the helper, because this logic is encapsulated inside the broker.
$broker = new HelperBroker;
$something = new Something($broker);
$other = new Other($broker);
The broker also makes sure that you only have one helper instance, because when a helper was instantiated, it is stored inside the broker and returned on subsequent calls. This solves your initial problem, that you don't want to reinstance any helpers. It also doesn't force your helpers to know anything about how to manage themselves in the global state, like the Singleton does. Instead you helpers can concentrate on their responsibility: helping. That's clean, simple and reusable.
It sounds like you are interested in the singleton pattern. If you are using PHP5+, you should be able to take advantage of PHP's OOP stuff.
Here's an article on how to implement a singleton in php4. (But I would strongly suggest updating to php5 if that is an option at all)
class Singleton {
function Singleton() {
// Perform object initialization here.
}
function &getInstance() {
static $instance = null;
if (null === $instance) {
$instance = new Singleton();
}
return $instance;
}
}
PHP 4 Singleton Pattern
FYI, if you have any control over which PHP version you use you really should migrate to PHP 5.

Categories