I have a page
Object,
which has Paragraph
and Image
object Collections
And each paragraph
has only image_id(s)
that are assigned to a paragraph. All other information about image is stored in Page->Image
Now from the paragraph
object I want to access the image
information where paragraph has only image_id
and all other information about the image is in Page->Image
object.
What will be the better way to access this info?
Should I pass the page
object in every paragraph
constructor or something else?
I also can’t change the class structure, as it is written by someone else.
1
KISS is always the best approach:
foreach($page->paragraphs as $paragraph)
foreach($paragraph->image_ids as $image_id)
$image = $page->images[$image_id]
But if you wanted something a little more special a delegate can assist in providing the “missing” functionality you require and for which php 5.3 has made things super easy by means of a little magic.
<?php
class PageDelegate
{
private $page;
public function construct($page)
{
$this->page = $page;
}
/* The magic delegating methods __call, __set and __get */
public function __call($name, $args)
{
if (method_exists($this->page, $name))
return call_user_func_array(array($this->page, $name), $args);
// you may require some conversion to valid method name
// like converting - to _ etc. this should be kept in mind.
if (array_has_key($this->images, $name))
return $this->images[$name]; // note how this becomes $this->page->images[$name]
throw BadMethodCallException("Method $name does not exist");
}
public function __get($name)
{
if (property_exists($this->page, $name))
return $this->page->{$name};
throw InvalidArgumentException("Property $name does not exist");
}
public function __set($name, $value)
{
if (property_exists($this->page, $name))
$this->page->{$name} = $value;
else
throw InvalidArgumentException("Property $name does not exist");
}
// And perhaps something to help out with common tasks
public function images_in_paragraph($paragraph)
{
$images = array();
foreach($paragraph->image_ids as $image_id)
// Note how this becomes $this-page->images[$image_id];
$images[] = $this->{$image_id}();
// return the collection of image objects in the paragraph
return $images;
}
}
Using just the 3 magic methods __call, __set and __get we are effectively delegating to all instance members of Page and you can now call for images using their image id as a method. If there are any Page functionality you want to overwrite, simply adding the function would see it being called instead of our magic __call method to do your bidding. Now it doesn’t matter that you can’t change their source, you can still do what you please. =)
The implementation would then look something like this perhaps
$page = new PageDelegate($page);
foreach($page->paragraphs as $paragraph)
foreach($page->images_in_paragraph($paragraph) as $image)
echo "<img title="$image->title" src="$image->src">";
nJoy!
3
The paragraph object need to have some mechanism to know where it’s image data comes from.
Assuming that a paragraph can’t exist in isolation but only as part of a page, there’s no harm in adding the owning page as a constructor parameter for each paragraph in order to supply this.
1
I think rvcoutinho’s comment is in line with the correct answer. Either the page class or a 3rd class (decorator/mediator) should be responsible for providing the image to the paragraph. Any images that the paragraph requires should be passed to the paragraph.
If you don’t like either of those options, I would think that passing the Image Collection to the paragraph is far preferable than passing the page class. After all, if you just think about the problem conceptually, pages contain paragraphs. Paragraphs don’t contain or know about pages.
3