I'm implementing the lazy initialization and dependency injection pattern in my PHP application at the moment and face the following question:
Every class has a bunch of getter and setter methods providing objects of foreign classes. Is there an elegant way to remove this "redundancy" to a parent class or some kind of factory?
EDIT: without loosing the advantages of testability ;-)
EDIT2: here is an example of a getter and setter method like I use them:
function getSql() {
if (is_object($this->sql) === FALSE) {
$registry = \Registry::getInstance();
$factories = $registry->get('factories');
$database = $factories['databaseSql'];
$this->sql = $database->getSql();
}
return $this->sql;
}
function setSql($sqlObject) {
// ... some checks
$this->sql = $sqlObject;
}
EDIT3 I followed the idea of using traits so here is a sample solution using a trait for the class "Registration":
trait Registration {
private $registration = null;
public function getRegistration() {
$registry = \Registry::getInstance();
$factories = $registry->get('factories');
if (is_object($this->registration) === TRUE) {
// get obect if instance already exists
$registration = $this->registration;
} else if (isset($factories['registration']) === TRUE) {
// get object using the matching factory
$registrationFactory = $factories['registration'];
$registration = $registrationFactory->getRegistration();
} else if (class_exists('\engine\classes\Registration') === TRUE) {
// get object using the default object class when no specific factory is defined
$registration = new \engine\classes\Registration();
} else {
throw new \Exception('error getting an instance of Registration');
}
$this->registration = $registration;
return $registration;
}
public function setRegistration($object) {
if (is_object($object) === FALSE)
throw new \Exception('invalid registration object');
$this->registration = $object;
}
}
Usage
class Foo {
use Registration;
public function bar() {
$reg = $this->getRegistration();
// ... use instance of Registration
}
}
Do you know OOP advantages of PHP language?
Inheritance
Traits
Magic getter and setters
May be some Aspect-Oriented Programming. For example Implementing Reusable Fluent Interface Pattern in PHP With AOP.
It's so many links because you did not provide any code for example.
Related
I have the following code (simplified and details changed for this question):
class model_to_be_tested {
// an array that holds a collection of thing A
public $array_of_thing_A;
// already doing constructor injection for the data object
public __construct($data_object) {
// details here
}
public function add_new_thing_A($has_relationship) {
$thing_A = new Thing_A();
$thing_A->is_thing = true;
$thing_A->has_relationship_with_thing_B = $has_relationship;
if ($has_relationship) {
$thing_B = new Thing_B();
$thing_A->relationship_with = $thing_B;
}
$this->array_of_thing_A[] = $thing_A;
}
}
In the above example, I have to decouple the instantiation of Thing_A and Thing_B from the add_new_thing method. However, a simple constructor injection will not do for these two classes. This is because I need fresh instances of Thing_A and Thing_B every time add_new_thing is called so that Thing_A can be added to the array_of_thing_A.
How can I make this function unit testable? And more specifically for me to use mocks of Thing_A and Thing_B in testing this function in PHPUnit?
Any suggestions with code example will be appreciated.
Additionally, I would like to mention that Thing_A and Thing_B are used elsewhere in the codebase that I am working with and the code using these classes will eventually need to be unit tested. Solutions that are too localized and would cause repeated code elsewhere will not be too ideal in my situation. Thank you.
As commenter xmike mentioned, you could use the factory pattern. You would inject a factory object through the ctor as well. Then you could have a factory that provides simplified instances of your Thing_A and Thing_B.
class ThingFactory {
public function buildThingA() {
return new Thing_A(); // or MockThing_A if you go the ducktyping route
}
public function buildThingB() {
return new Thing_B();
}
}
class model_to_be_tested {
// an array that holds a collection of thing A
public $array_of_thing_A;
// you could go the typed route and have an interface for this
private $factory;
// already doing constructor injection for the data object
public __construct($data_object, $factory) {
// details here
$this->factory = $factory;
}
public function add_new_thing_A($has_relationship) {
$thing_A = $this->factory->buildThingA();
$thing_A->is_thing = true;
$thing_A->has_relationship_with_thing_B = $has_relationship;
if ($has_relationship) {
$thing_B = $this->factory->buildThingB();
$thing_A->relationship_with = $thing_B;
}
$this->array_of_thing_A[] = $thing_A;
}
}
PHP is such a strange language, you can't assign a class to a variable. But you can do it as a string. Inject ThingA and ThingB on the constructor as strings. You can call new on the string member.
class ThingA {};
class ThingB{};
class model_to_be_tested {
// an array that holds a collection of thing A
public $array_of_thing_A;
private $_thingA;
private $_thingB;
public function __construct($data_object, $thingA, $thingB) {
$this->_thingA = $thingA;
$this->_thingB = $thingB;
}
public function add_new_thing_A($has_relationship) {
$thing_A = new $this->_thingA();
if ($has_relationship) {
$thing_B = new $this->_thingB();
}
$this->array_of_thing_A[] = $thing_A;
}
}
$model = new model_to_be_tested('foo', 'ThingA', 'ThingB');
$model->add_new_thing_A(true);
There's a live version here: https://repl.it/#rmoskal/InconsequentialAnotherGermanshorthairedpointer
Or provide a static constructor for the class.
I have a class with a private variable used to store an object.
I have a function that checks first if that variable already contains an object or not; if not, it instantiates the needed object and sets it to that variable, otherwise it just returns the content of that variable.
I was wondering if the getSessionCustomer() here is an overkill/unnecessary or if it has real benefits. I simply based this on the Album tutorial by Zend, but I haven't been able to fully test it out yet to really see the advantages (or disadvantages). As far as I know it wasn't explained in the docs why this additional function was included.
class JobController extends AbstractActionController
{
private $SessionCustomer;
public function saveJobAction()
{
$SessionCustomer = $this->getSessionCustomer();
if(empty($SessionCustomer->offsetGet('customer_id'))) {
return $this->redirect()->toRoute('login');
} else {
$JobService = $this->getServiceLocator()->get('Job\Factory\JobServiceFactory');
$job_id = $JobService->saveJob();
return $this->redirect()->toUrl('/job/' . $job_id);
}
}
public function viewJobAction()
{
$sm = $this->getServiceLocator();
$SessionCustomer = $this->getSessionCustomer();
if(empty($SessionCustomer->offsetGet('customer_id'))) {
return $this->redirect()->toRoute('login');
} else {
$JobTable = $sm->get('Job\Model\JobTable');
$JobItemTable = $sm->get('Job\Model\JobItemTable');
$jobId = $this->params()->fromRoute('id');
$Job = $JobTable->getJobById($jobId);
$JobItems = $JobItemTable->getJobItemsByJobId($jobId);
$this->layout()->setVariable('title', 'Order #' . $jobId);
$viewModel = new ViewModel();
$viewModel->setVariables(array(
'Job' => $Job,
'JobItems' => $JobItems
));
return $viewModel;
}
}
private function getSessionCustomer()
{
if(!$this->SessionCustomer) {
$this->SessionCustomer = $this->getServiceLocator()->get('Session\Customer');
}
return $this->SessionCustomer;
}
}
I don't think its an overkill, but I usually avoid calling getServiceLocator() in Controllers.
What you are asking about is basically making sure that the controller's dependency requirement is met. You can use a Factory for the same purpose and do this more sophisticated way. You can create a factory and inject the dependencies directly into the controller. This you will never make a call to the non-object variables.
For that you will be required to create a class that implements a FactoryInterface which will have a method createService which will provide you with ServiceLocator. You can use that serviceLocator to get all the dependencies and inject them directly into your Class.
I want to create an object of a class from a returned string but I am getting error Class **test_report** not found. My code:
public function display_report_builder($report_name = null)
{
$column_listing = new $report_name;// gets the test_report
return view('column_list')->with(['column_list_names' => $column_listing->columns]);
}
This isn't the better approach here. What you should do is to use a Factory design pattern:
class ReportFactory
{
public static function create($report_name)
{
switch($report_name) {
case 'test_report': return new TestReport();
default: throw new Exception('report not found');
}
}
}
Then you call with $column_listing = ReportFactory::create($report_name);
Why? Because you avoid "magic variables" with unknown data; you can trace errors properly; you can use namespace; you can extend functionalities easily, and easily activate or deactivate objects (or reports in this case); you have a cleaner code, and so on...
test if the class name (string) really is a valid class :
public function display_report_builder($report_name = null)
{
$column_list_names = null;
if (class_exists($report_name) && is_a($report_name, App\reports\test_report::class, true)) {
$column_listing = new $report_name;
$column_list_names = $column_listing->columns;
}
return view('column_list', compact('column_list_names'));
}
is_a() : Checks if the given object is of this class or has this class
as one of its parents.
I have the following code:
<?php
class X
{
public function do($url)
{
$httpRequest = new \HttpRequest\Curl($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
In order to be able to unit test this class, I'd like to inject a mocked HttpRequest class. One way to do this would be as follows:
<?php
class X
{
private $httpRequestClass;
public function __construct($httpRequestClass = '\HttpRequest\Curl')
{
$this->httpRequestClass = $httpRequestClass;
}
public function do($url)
{
$httpRequest = new $this->httpRequestClass($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
But this doesn't seem right. Any other ideas?
public function __construct($url, $httpRequestClass = null)
{
$this->url = $url;
if ($httpRequestClass == null) //> Default
$this->httpRequestClass = new HttpRequest\Curl($this->url);
else
$this->httpRequestClass = $httpRequestClass;
}
so when you are using this class normally just call it with one param
yourClass('your url');
Otherwise pass the istance in the second argument
yourClass('url', new MockedObj);
Of course you should always Inject your dependencies without providing a default object
The class needs to generate objects of type HttpRequest, but we don't necessarily want it to initialize an object: we may want it to use the prototype pattern, for example. Therefore, the class calls for the factory pattern. I chose a factory callback, as opposed to a factory class, for brevity.
<?php
class X
{
private $factoryCallback;
public function __construct($factoryCallback = null)
{
$this->factoryCallback = $factoryCallback;
}
public function do($url)
{
$httpRequest = $this->createHttpRequest($url);
$httpRequest->fire();
// etc.
}
private function createHttpRequest($url)
{
$callback = $this->factoryCallback;
if (is_callable($callback)) {
return $callback($url, $this->getOptions());
}
return new \HttpRequest\Curl($url, $this->getOptions());
}
// ...
}
The helper method, createHttpRequest(), is a bit redundant in this example, but would be used for error handling in production code.
Currently I have a class called user that I want to create with different variables, but I think I'm doing it wrong.
Currently I have a class "Unit" with these two functions
public function __construct($table, $id) {
require_once('database.php');
require_once('app.php');
require_once("postmark.php");
$this->table = $table;
$this->valid = true;
if(!$id) {
$this->valid = false;
}
$this->populate($id);
}
public function populate($id) {
$db = new DB();
$q = $db->where('id', $id)->get($this->table);
$resp = $q->fetchAll();
foreach ($resp as $row) {
foreach ($row as $key=>$value) {
if(!is_int($key))
$this->$key = html_entity_decode($value, ENT_QUOTES);
if(is_null($value)) {
$this->$key = null;
}
}
}
if(count($resp) <= 0) $this->valid = false;
$verdict = !$db->error;
$db = null;
unset($db);
return $verdict;
}
And then my "User" class extends it like so
public function __construct($id, $hash = null, $verify = null, $api = null) {
if($api)
$value = $this->apiToId($api);
else if($verify)
$value = $this->verifyToId($verify);
else if($hash)
$value = $this->hashToId($hash);
else
$value = $id;
parent::__construct("users", $value);
}
But I can't help but think this is poor in design. A few things I have seen in the past are the use of ampersands, possibly making it so I could do
$user = new User()->fromId($id);
Or
$user = new User()->withHash($hash);
Instead of passing it a long list of null params. That or I could improve the way inheritance works. While I like to think I know what I'm doing with PHP, I'd really like some help looking in the right direction. PHP's docs are so cumbersome, that I never no where to look, but always find cool useful tools. I'm wondering how I can improve this for more flexibility and structure.
Move includes to the very top of your php file. Anything that needs to be conditionally included is probably poorly designed.
Your unit class should be declared as abstract. This prevents anyone from instantiating a unit. You can only declare subclasses of it.
Any functions relating to your class should be declared as methods. Thus, the example given in an answer now-removed is a terrible choice. The function alloc really should be a static function defined in User. Code snippet at bottom.
Your init functions should be declared as static and return a new instance of the class. Defining an instance of the class to re-instantiate the class is just a bad idea.
Your database connection should use a Singleton pattern. Look it up if you need to.
Post your full code and comment on this answer if you'd like some help implementing all of this.
$user = User::initWithHash($hash);
//your create method:
/**
* Creates and returns a new instance of the class. Useful
* #return an instance of User.
*/
public static function create() {
return new User();
}