Ну, я однажды нашел следующий умный шаблон, любезно предоставленный Беном Шольценом , разработчиком для Zend Framework. Это выглядит примерно так:
class ModelRelation
implements IteratorAggregate
{
protected $_iterator;
protected $_mapper;
protected $_method;
protected $_arguments;
public function __construct( MapperAbstract $mapper, $method, array $arguments = array() )
{
$this->_mapper = $mapper;
$this->_method = $method;
$this->_arguments = $arguments;
}
public function getIterator()
{
if( $this->_iterator === null )
{
$this->_iterator = call_user_func_array( array( $this->_mapper, $this->_method ), $this->_arguments );
}
return $this->_iterator;
}
public function __call( $name, array $arguments )
{
return call_user_func_array( array( $this->getIterator(), $name ), $arguments );
}
}
Реальная реализация Бена Шольцена здесь .
То, как вы бы это использовали, выглядит примерно так:
class UserMapper
extends MapperAbstract
{
protected $_addressMapper;
public function __construct( AddressMapper $addressMapper )
{
$this->_addressMapper = $addressMapper;
}
public function getUserById( $id )
{
$userData = $this->getUserDataSomehow();
$user = new User( $userData );
$user->addresses = new ModelRelation(
$this->_addressesMapper,
'getAddressesByUserId',
array( $id )
);
return $user;
}
}
class AddressMapper
extends MapperAbstract
{
public function getAddressesByUserId( $id )
{
$addressData = $this->getAddressDataSomehow();
$addresses = new SomeAddressIterator( $addressData );
return $addresses;
}
}
$user = $userMapper->getUserById( 3 );
foreach( $user->addresses as $address ) // calls getIterator() of ModelRelation
{
// whatever
}
Дело в том, хотя; это может стать очень медленным, если графы объектов становятся очень сложными и глубоко вложенными в какой-то момент, потому что все преобразователи должны запрашивать свои собственные данные (предполагая, что вы используете базу данных для сохранения). Я испытал это, когда использовал этот шаблон для CMS, чтобы получить вложенные Pages
объекты (произвольно глубокие дочерние страницы).
Вероятно, его можно настроить с помощью некоторого механизма кэширования, чтобы значительно ускорить процесс.