PHP OOP : Fluent interface and tree graphs - php

I'm trying to create a fluent interface for tree objects.
Here's a simplified example of what I currently do :
<?php
class node {
private $childs = array();
private $parent;
public function __construct($parent = null) {
$this->parent = $parent;
}
public function addChild($child) {
$this->childs[] = $child;
return $this;
}
public function createChild() {
return $this->addChild(new node($this));
}
public function setFoo() {
/* do something */
return $this;
}
}
$root = new node();
$root ->addChild((new node($root))
->setFoo()
)->addChild((new node($root))
->setFoo()
);
?>
I would like to reduce the part where I create the tree.
What I want to do is something like this :
$root->createChild()->setFoo();
$root->createChild()->setFoo();
in one line. And without having to explicitly create new nodes instances (like I did in the first code with new operators).
My goal is to be able to create any tree of any order, and its nodes of any degree without having to put a semi-colon in the code.

Rather than adding a createChild function I think you should change your constructor and addChild functions to consistently establish the parent / child relationship in the data. Once you've done that the addChild function and the constructor can be used to do what you described without a createChild function. Right now your constructor allow cross-linking between different trees and branches in the trees so it's something that will probably need to change anyway.
class node {
private $childs = array();
private $parent;
public function __construct(node $parent = null) {
if(!is_null($parent)) {
$parent->addChild($this);
}
}
public function addChild(node $child) {
$this->childs[] = $child;
$child->parent = $this;
return $this;
}
public function setFoo() {
/* do something */
return $this;
}
}
With this you can chain new objects into a tree:
$tree = (new node())->addChild(new node())
->addChild((new node())->setFoo())
->addChild((new node())->addChild(new node())
->addChild(new node())
->setFoo()
);
Trying to use a createChild function is a catch-22 situation where sometimes you need the parent and sometimes you need the child. You can solve it using a return object that contains both but I think it's a situation that is better avoided. If you don't like the "(new node())" syntax, a static function might be the way to go:
public static function create(node $parent = null) {
return new node($parent);
}
Which might be a little prettier depending on your tastes:
$tree = node::create()->addChild(node::create())
->addChild(node::create()->setFoo())
->addChild(node::create()->addChild(new node())
->addChild(new node())
->setFoo()
);

You can add this methods to create as child as you can.
public function createManyChild($nbrOfChild) {
for($i = 0; $i < $nbrOfChild; $i++){
$this->addChild(new node($this));
}
return $this;
}
And use the code like this.
$root = new node();
$root->createManyChild(3)->setFoo();

Related

Why is it bad if items knows about container? Composite pattern (2 objects has references to each other) php

They say its not a good idea when tho objects has circular references. Lets see a composite example:
class Book
{
private $title;
public function __construct($title)
{
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
}
class Shelf
{
private $books = [];
public function addBook (Book $book)
{
$this->books[] = $book;
}
}
$shelf = new Shelf();
$sehlf->add (new Book('a'));
$sehlf->add (new Book('b'));
$sehlf->add (new Book('c'));
so far so good. For some reason, Book must know about Shelf, so I rewrite:
class Book
{
private $title;
private $shelf;
public function __construct($title, $shelf)
{
$this->shelf = $shelf;
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
}
class Shelf
{
private $books = [];
public function addBook (Book $book)
{
$this->books[] = $book;
}
}
$shelf = new Shelf();
$sehlf->add (new Book('a', $shelf));
$sehlf->add (new Book('b', $shelf));
$sehlf->add (new Book('c', $shelf));
and they say its bad.
There are a couple of problems with circular references which can be found here but I must accept the above is not a very problematic example -- as it is clear that shelf must be created first before book.
One of the most common problems for example is that it may be 'impossible' to use print_r on the instance of Book above assuming the variables were public -- will lead to a memory exhaustion.
There a couple of ways to resolve circular reference issues, one of the most common which includes using Setter injection. e.g.
public function setShelf(Shelf $shelf)
{
$this->shelf = $shelf;
}
Another issue particular with PHP is that it can have issues resolving circular dependencies. See this for example.
class Node {
public $parentNode;
public $childNodes = array();
function Node() {
$this->nodeValue = str_repeat('0123456789', 128);
}
function destroy()
{
$this->parentNode = null;
$this->childNodes = array();
}
}
function createRelationship() {
$parent = new Node();
$child = new Node();
$parent->childNodes[] = $child;
$child->parentNode = $parent;
$parent->destroy();
}
PHP will end up allocating about 35MB of memory for no good reason. To resolve this, we may use a destructor so the garbage collector knows how to take care of the object once we're done with it.
function destroy()
{
$this->parentNode = null;
$this->childNodes = array();
}

Get all the instances of a particular class in PHP

I want to be able to do something like:
objects = getAllInstances(ClassName);
where ClassName has a unique field, so that two instances can not have the exact same value of that field.
class ClassName {
protected $unique_field;
public function __construct($value)
{
$objects = getAllInstances(self);
foreach($objects as $object)
{
if($object->getUniqueField() === $value)
{
return $object;
}
}
}
public function getUniqueField()
{
return $this->unique_field;
}
};
Is there a design pattern, a built-in function in PHP for this purpose, or must I use a static array that holds all the created instances and then just loop over it?
You could create a factory that keeps a reference to all instances created with it:
class ClassNameFactory
{
private $instances = [];
public function create($value)
{
return $this->instances[] = new ClassName($value);
}
public function getInstances()
{
return $this->instances;
}
}
$f = new ClassNameFactory();
$o1 = $f->create('foo');
$o2 = $f->create('bar');
print_r($f->getInstances());
You can hold a static array with all the existing instances. Something similar to this...
static $instances;
public function __construct($name) {
$this->unique_field = $name;
if (empty($instances)) {
self::$instances = array();
}
foreach (self::$instances as $instance) {
if ($instance->getUniqueField() === $name)
return $instance;
}
self::$instances[] = $this;
}
What you need is the registry pattern:
class ClassNameRegistry {
private $instances = array();
public function set($name, InterfaceName $instance) {
$this->instances[$name] = $instance;
}
public function get($name) {
if (!$this->has($name)) {
throw new \LogicException(sprintf(
'No instance "%s" found for class "ClassName".',
$name
);
}
return $this->instances[$name];
}
public function has($name) {
return isset($this->instances[$name]);
}
public function getAll() {
return $this->instances;
}
}
This is certainly the best OOP architecture option because you isolate the behaviour in a standalone class as a service. If you do not have a dependency injection mechanism with services, I would suggest you to define the registry class as a singleton!
In my example, I used a InterfaceName to have a low coupling between Registry and its handled instances.

How to truly separate concerns for hiearchical data using the Data Mapper pattern

I am quite new to the Data Mapper pattern. I guess I am missing something, because, for me, it fails for everything more complicated than the most basic examples.
Let's say I have a simple website composed of pages:
class Page {
private $id = null;
private $parent = null;
private $title = null;
private $body = null;
public function setId($id) { $this->id = (int) $id; }
public function setParent(Page $parent) { $this->parent = $parent; }
public function setTitle($title) { $this->title = $title; }
public function setBody($body) { $this->body = $body; }
public function getId() { return $this->id; }
public function getParent() { return $this->parent; }
public function getTitle() { return $this->title; }
public function getBody() { return $this->body; }
}
Now I want to instantiate Page 7 that is deep in the tree:
Page 1
Page 2
Page 3
Page 4
Page 5
Page 6
Page 7
Using the Data Mapper pattern I would use the following mapper class:
class PageMapper {
public function fetch($id) {
//...
$data = $db->fetchRow("SELECT * FROM `pages` WHERE `id` = ?", $id);
$page = new Page();
$page->setId($data['id']);
$page->setTitle($data['title']);
$page->setBody($data['body']);
if ($data['parent_id'] !== null) {
$page->setParent(
$this->fetch($data['parent_id']);
);
}
}
public function save(Page $page) {
//...
}
}
The problem is obvious: I must instantiate all parents all the way up to the root Page.
Now imagine a Page would need to know about its children. That would mean, if I would want to instantiate the root Page, I would have to load the entire tree.
A solution could be that the parent/children would be loaded only on demand, but that would mean that the domain objects would have to know about the mapper in order to pull data from it... and the separation would be gone.
The domain models (instances of the Page class) should know nothing about the Data Mapper layer (separation), but still be able to perform those tasks (retrieving parent/children).
Is separation of concerns achievable in these conditions? If yes, how?
Look at Doctrine, a orm framework which implements the data mapper pattern:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-many-self-referencing
The problem with your approach is not the instantiation of the entire tree (children), but
the amount of queries it would use.
Look at the doctrine approach, there you would have a children property, which is loaded at once with one query.
I would also never recommend to implement a self-made solution for an orm.
A possible solution would be also to use a Closure:
class Page {
//...
public function setParents(Closure $pages)
{
$this->parents = $pages;
}
}
class PagesMapper {
public function fetch() {
//fetch the page ...
$parents = function($parent) use($id, $db) {
parents = $db->query(/* select parent... */);
$pages = array();
foreach($parents as $parent) {
$page = new Page();
$page->id = $parent->id;
//...
$pages[] = $page;
}
return $pages;
};
$page->setParents($parents);
return $page;
}
}
So the page domain would have no knowledge of the persistance layer.

PHP Iterator like mysql iterator?

Already I extended and implemented from SPL iterator.
But if I want to use it, I should use it on a foreach.
I tried to use it in a while like this:
$news = new testClass();
while( $row = $news )
echo $row["name"];
It will create an infinite loop !
But with foreach, it works fine!
Here is top of my class:
class testClass implements \Iterator
Where is the mistake ?
Fist, bravo on using the SPL classes for this type of 'standard' problem. Too often have I seen inexperienced/sloppy developers (or even good ones that simply don't think ahead) reinvent the wheel in these types of situations.
You're missing some very important details about the implementation of the iterator interface.
see PHP:Iterator - Manual for more information, and the reference implementation from below.
First, you need to implement the, rewind, current, key, next, and valid functions. the reference implementation looks like this:
class myIterator implements Iterator {
private $position = 0;
private $array = array(
"firstelement",
"secondelement",
"lastelement",
);
public function __construct() {
$this->position = 0;
}
function rewind() {
var_dump(__METHOD__);
$this->position = 0;
}
function current() {
var_dump(__METHOD__);
return $this->array[$this->position];
}
function key() {
var_dump(__METHOD__);
return $this->position;
}
function next() {
var_dump(__METHOD__);
++$this->position;
}
function valid() {
var_dump(__METHOD__);
return isset($this->array[$this->position]);
}
}
)
And the code for traversing that implementation looks like this:
$it = new myIterator;
foreach($it as $key => $value) {
var_dump($key, $value);
echo "\n";
}
foreach is language construct that iterates through all elements. while executes block of code until given condition is true. To make it work you have to use your own function that checks for valid key and returns current element.
Finally I created a simple example of this:
<?php
/**
* #author Soroush Khosravi
* #copyright 2013
*/
class _Iterator
{
private $array;
public function setArray(array $data)
{
$this->array = $data;
}
public function reader()
{
if (is_null($this->array))
return false;
$elem = array_shift($this->array);
if (count ($this->array) > 0)
return $elem;
return false;
}
}
Class child extends _Iterator
{
function execute()
{
$this->setArray(array(1,2,3,4,5,6));
return $this;
}
}
$obj = new child;
$obj = $obj->execute();
while($row = $obj->reader())
echo $row;
?>

How to get the parent object in PHP, from OUTSIDE the object itself?

I am using Reflections to adjust various values in objects, and I have an object who's parent I need to adjust.
For example:
class Ford extends Car
{
private $model;
}
class Car
{
private $color;
}
I can easily use Reflection to change the model, but how can I separate the parent from the child, so that I can use Reflection on the parent?
Some psuedo code for what I'm hoping is possible:
$ford = new Ford();
$manipulator = new Manipulator($ford);
$manipulator->set('model','F-150');
$manipulator->setParentValue('color','red');
class Manipulator
{
public function __construct($class) {
$this->class = $class;
$this->reflection = new \ReflectionClass($class);
}
public function set($property,$value) {
$property = $this->reflection->getProperty($property);
$property->setAccessible(true);
$property->setValue($this->class,$value);
}
public function setParentValue() {
$parent = $this->reflection->getParent();
$property = $this->reflection->getProperty($property);
$property->setAccessible(true);
// HOW DO I DO THIS?
$property->setValue($this->class::parent,$value);
}
}
Gist of the question:
In this case, how can I change the $color from outside the object altogether?
Is there something like Ford::parent() or get_parent_object($ford) available?
Note
The objects used above are not the exact scenario, but just used to illustrate the concept. In the real world case, I have a parent/child relationship, and I need to be able to access/change values in each from the outside.
ANSWER
Please check my answer below...I figured it out.
After extensive review, I have found that I can't access the parent of an object AS AN OBJECT outside of the object itself.
However, using Reflections, I was able to solve the example posted above:
<?php
class Car
{
private $color;
public function __construct()
{
$this->color = 'red';
}
public function color()
{
return $this->color;
}
}
class Ford extends Car
{
}
$ford = new Ford();
echo $ford->color(); // OUTPUTS 'red'
$reflection = new ReflectionClass($ford);
$properties = $reflection->getProperties();
foreach($properties as $property) {
echo $property->getName()."\n>";
}
$parent = $reflection->getParentClass();
$color = $parent->getProperty('color');
$color->setAccessible(true);
$color->setValue($ford,'blue');
echo $ford->color(); // OUTPUTS 'blue'
See it in action here: http://codepad.viper-7.com/R45LN0
See get_parent_class(): http://php.net/manual/en/function.get-parent-class.php
function getPrivateProperty(\ReflectionClass $class, $property)
{
if ($class->hasProperty($property)) {
return $class->getProperty($property);
}
if ($parent = $class->getParentClass()) {
return getPrivateProperty($parent, $property);
}
return null;
}
Here is the static version of the function I answered your other question with:
function getProperties($object) {
$properties = array();
try {
$rc = new \ReflectionClass($object);
do {
$rp = array();
/* #var $p \ReflectionProperty */
foreach ($rc->getProperties() as $p) {
$p->setAccessible(true);
$rp[$p->getName()] = $p->getValue($object);
}
$properties = array_merge($rp, $properties);
} while ($rc = $rc->getParentClass());
} catch (\ReflectionException $e) { }
return $properties;
}

Categories