1

I am going to try to explain this as best as I can, forgive me if parts don't make total sense. I am building a PHP library/framework for educational purposes. I was building it using the singleton pattern but ran into some problems and also seem to read lately that it shouldn't be used as it isn't very testable, (and although I am not worried about this being testable, I would like to learn how to do it) and instead should use dependency injection.

What I want is a shared variable, if you will, which houses many different classes. For instance $app->database; $app->views; $app->session; etc., and am not sure if I am doing this right. All classes are also being autoloaded.

I have the bootstrap/start.php which initializes the main variable (is there a name for this?) with:

$app = new App(new Database, new Views, new Session);

in the App.php class I am using essentially:

 public $database;
 public $views;
 public $session

public function __construct(Client $client, Views $views, Session $session)
{
     $this->database = $database;
     $this->views = $views;
     $this->session = $session;
}

My problem is this: Am I supposed to be initializing all of the classes in the constructor like that? And the big one...how do I use the $database variable in, for example, my Views.php class without creating a whole new instance which may overwrite some of the properties in the Database class?

Would appreciate some insight on this a lot.

  • As one of the way, you can create static methods in App which will return your database, views instances – sergio Jun 12 '15 at 08:36
  • 2
    You should look for a container like `Pimple` or the `Symfony` one, and create `Client`, `Views, `Session` services. In this way, the container contains 1 instance of each service. Now for the dependency injection, if the `View` service needs the `Database` service, in his constructor you must inject a `Database`object inside, and in his service declaration inject him the database `service` – Med Jun 12 '15 at 08:36
  • @Med thanks for the reply. I have utilized the Symfony container before, but wanted to see if I could work out some problems on my own from scratch. – PumpkinJack Jun 12 '15 at 09:44
  • I agree with @Med . If you proceed as you do, you tie, strongly, your application with an implementatio of Database / Views / Sessions (btw you might not always need sessions). Having a container that has the services you can use, factories, etc would be better. You could have an interface in your app package that all of your services would implement as a separated interface in order to garantee their usability by anyone in the app/core/whatever you call it package (for example) – PEM Jun 12 '15 at 12:10

2 Answers2

1

What you should really know about MVC-like architectures, is that the entire point they are based on is called Separation of Concerns (SoC).

Why would you ever access a database connection in views? Obviously because you want to perform a query and display results right there. That's what breaks the SoC. In order to display the data, you have to access the database connection in your model, parse somehow if needed, and then simply pass prepared array to views, so that views don't even know that such thing as database exists, they only know that there's an array, which needs to be displayed somehow. That approach introduces one major advantage - you can easily swap database engines (with SOAP/REST, Mongo, whatever), without touching a view!

As for your problem, you can solve this by creating an abstract class, which is gonna be called AbstractApplication and put all those initialization logic there. And then you can simply inherit from this base class.

Another way you have, is to approach it with so-called Service Locator. You create one wrapper, put all your dependencies there, and then you simply inject the Service Locator for classes that require it. This is not the best way, but its the only way clean way to handle a large list of dependencies (in real world-scenario, you'd have more than 4 arguments to be injected)

Yang
  • 8,580
  • 8
  • 33
  • 58
  • I believe Controller class is required for binding the database (Model) and View(Template) – Faiz Rasool Jun 12 '15 at 09:19
  • 1) A view is not a template, but a layer which consists of UI logic and templates. 2) A model is not a class, but abstraction layer which consists of storage abstractions and domain objects. 3) He didn't say this is MVC. As I said in the answer right in the beginning, **SoC is all about MVC-like architectures** – Yang Jun 12 '15 at 09:22
  • Controllers aren't responsible for fetching data from models (services) and passing them to Views. That's not MVC. What you're referring to is called Model-View-Presenter – Yang Jun 12 '15 at 09:24
  • Yes model are responsible for it but controller make the logic and call the model to do that. – Faiz Rasool Jun 12 '15 at 09:25
  • Check this: -- View: "Hey, controller, the user just told me he wants item 4 deleted." Controller: "Hmm, having checked his credentials, he is allowed to do that... Hey, model, I want you to get item 4 and do whatever you do to delete it." Model: "Item 4... got it. It's deleted. Back to you, Controller." Controller: "Here, I'll collect the new set of data. Back to you, view." View: "Cool, I'll show the new set to the user now." http://stackoverflow.com/questions/1015813/what-goes-into-the-controller-in-mvc – Faiz Rasool Jun 12 '15 at 09:27
  • Controllers do call services. They only know how to call Services. They don't know there's such thing as database exists. Therefore, they must have almost zero-logic and be thin all the time. What you're referring to is known anti-pattern which is called Fat controllers/Thin models – Yang Jun 12 '15 at 09:28
  • Yes exactly and in the most of the cases they extends the AbstractActionController but i think even in Zend you can get database object in the controller – Faiz Rasool Jun 12 '15 at 09:31
  • Of course you can (in any framework, btw), the question if you should do that or not – Yang Jun 12 '15 at 09:33
  • The framework i implemented for the medium size company i use the model with the injection of storage even, i totally understand model are not responsible for storage but it allow me to do database transactions (roll back, and commit) even if there are different model executing different queries to different tables and what i was doing i was injecting the the database PDO object and storage variable into model which is child of model class from the controller – Faiz Rasool Jun 12 '15 at 09:36
  • @DavidY I am aware of MVC, and was merely showing example variables/classes. – PumpkinJack Jun 12 '15 at 09:42
  • @PumpkinJack Good. I showed you two ways how you can handle that (one base class for them all / Service Locator), without MVC – Yang Jun 12 '15 at 09:44
  • While i'm no DI expert in PHP field, but propagating Service Locator is a bad practice since OP will probably just hide the new keywords here, and include newer dependencies. I would rather recommend using a DI container, for eg. like [PHP-DI](http://php-di.org), set up the container in the applications composition root (near entry point) and pass in the dependencies via the class's public constructor. I highly recommend to read [Service Locator is an Anti Pattern](http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern) post by @MarkSeemann. – kayess Jun 13 '15 at 17:49
  • @kayess Yeah, there are a lot of people that say that Service Locator is a bad practice since it's nothing more that just improved Registry. **Most developers know that**. And the reason they call it anti-pattern is this. But there's no another way to handle dynamic dependencies. Nobody knows how to handle dynamic dependencies without SL nowadays. Since controllers in modular application might accept user-defined services, how would you handle that without SL? – Yang Jun 14 '15 at 04:07
  • @DavidY: if theres is more than one implementations are possible of a given interface you could build an abstract factory around it, and inject that. However you would still set that up in composition root to avoid the SL pattern. If i get it right the OP asked about something like a singleton scope, which could be easily done using DI container, binding the public class and its interface in singleton way in the scoping call. – kayess Jun 14 '15 at 08:08
-1

Your code looks good but i would recommend these changes, make the variable private and produce getter and setters.

<?php
Class App {

private $database;
private $views;
private $client;
private $session;

/**
 * 
 * @param type $database
 * @param type $views
 * @param type $client
 * @param type $session
 */
function __construct($database, $views, $client, $session) {
    $this->database = new $database;
    $this->views = new $views;
    $this->client = new $client;
    $this->session = new $session;
}

/**
 * 
 * @return type
 */
function getDatabase() {
    return $this->database;
}

/**
 * 
 * @return type
 */
function getViews() {
    return $this->views;
}

/**
 * 
 * @return type
 */
function getClient() {
    return $this->client;
}

/**
 * 
 * @return type
 */
function getSession() {
    return $this->session;
}

/**
 * 
 * @param type $database
 */
function setDatabase($database) {
    $this->database = $database;
}

/**
 * 
 * @param type $views
 */
function setViews($views) {
    $this->views = $views;
}

/**
 * 
 * @param type $client
 */
function setClient($client) {
    $this->client = $client;
}


/**
 * 
 * @param type $session
 */
function setSession($session) {
    $this->session = $session;
}

}
//and in bootstrap or where you loading just do this and make database, view and all other singleton.
$database = database::getInstance();
$views = views::getInstance();
$client = client::getInstance();
$session = session::getInstance();
$App = new App($database, $views, $client, $session);
Faiz Rasool
  • 1,379
  • 1
  • 12
  • 20
  • Your changes are bad, because: You violate the Law of Demeter, You promote the bad practice which is called Tight-coupling – Yang Jun 12 '15 at 08:51
  • I totally agree with you @DavidY but I believe it is the boot part of the application where application boots up, i think there we need tight coupling. – Faiz Rasool Jun 12 '15 at 08:54
  • 1
    This can be done easily without Tight-coupling. Here comes the Service-Locator and then it gets injected – Yang Jun 12 '15 at 08:55
  • Yes i totally understand it, there is many more better ways but this is one of it and to be honest every method have pros and cons. Feel free to edit this code to add the server locator class if you want. – Faiz Rasool Jun 12 '15 at 08:57