Co do ActiveRecord, to ja chcę u siebie zaimplementować bardziej dynamiczne rozwiązanie, odwzorowujące w czasie rzeczywistym stan bazy danych, więc np. utworzenie obiektu Post, który będzie odwzorowywał tablicę posts w bazie danych wyglądałoby tak:
<?php
class Post extends ActiveRecord {}
?>
Podobnie wygląda to w Ruby on Rails.
A co do sprawdzonych źródeł wzorców, m.in. Registry: PoEAA (Patterns of Enterprise Application Architecture). W polskim wydaniu opis wzorca Registry znajduje się na s. 438.
A co do mojej implementacji tego wzorca - u mnie jest to po prostu kontener, nie zawiera on metod tworzenia obiektów, jeśli one nie istnieją, a jedynie udostępnia globalny intefejs dostępu do przechowywanych obiektów. Brakuje mi jeszcze w moim rozwiązaniu implementacji wzorca IdentityMap, lub czegoś podobnego, co zapewni unikalność obiektów - chwilowo są to tylko nazwy wpisów w rejestrze.
Wygląda to mniej więcej tak:
<?php
final class Registry extends Container {
private static function getInstance
(){ if( self::$Instance instanceof Registry ){
return self::$Instance;
}
else {
return self::$Instance = new Registry;
self::$Instance->DataType = new String( 'Entry' );
}
}
public static function getEntry
( String
$Name ){ $Registry = self::getInstance();
if( self::hasEntry( $Name )->isFalse() ){
throw new ContainerException( 'Element ' . $Name->toString() . ' does not
exist in ' . get_class( $Registry ) );
}
return $Registry->Elements[$Name->toString()];
}
public static function hasEntry
( String
$Name ){ $Registry = self::getInstance();
return new Boolean
( isset( $Registry->Elements[$Name->toString()] ) ); }
public static function addEntry
( String
$Name, $Element ){ $Registry = self::getInstance();
return $Registry->Elements[$Name->toString()] = $Element;
}
}
?>
A tak wygląda klasa kontenera, którą Registry rozszerza.
<?php
abstract class Container{
protected
$Elements = array(); protected $ClassName;
protected $DataType;
function __construct(){
$ClassName = new String( get_class( $this ) );
$ClassName->replace( new String( '/Container$/s' ), new String() );
$this->DataType = Inflector::singularize( $ClassName );
}
protected function getDataType(){
return $this->DataType;
}
protected function hasElement( String $Name ){
$DataType = $this->getDataType()->toString();
return new Boolean
( isset( $this->Elements[ $Name->toString() ] ) && $this->Elements[ $Name->toString() ] instanceof
$DataType );
}
protected function getElement( String $Name ){
if( $this->hasElement($Name)->isFalse() ){
throw new Exception( 'Element ' . $Name->toString() . ' does not
exist in ' . get_class( $this ) );
}
return $this->Elements[$Name->toString()];
}
protected function addElement( String $Name, $Element ){
$DataType = $this->getDataType()->toString();
if( !$Element instanceof $DataType ){
throw new Exception( 'Argument 1 passed to ' . get_class( $this
) . '::add' . $this->getDataType()->toString() . '() must be an object of class ' .
$DataType );
}
return $this->Elements[$Name->toString()] = $Element;
}
protected function removeElement( String $Name ){
if( $this->hasElement( $Name )->isTrue() ){
unset( $this->Elements[ $Name->toString() ] ); }
}
public function getAll(){
return $this->Elements;
}
public function removeAll(){
$this->Elements = array(); }
function __call( $MethodName, $MethodParams ){
$MethodName = new String( $MethodName );
if( $MethodName->startsWith( new String('get') ) ||
$MethodName->startsWith( new String('has') ) ||
$MethodName->startsWith( new String('add') ) ||
$MethodName->startsWith( new String('remove') ) ){
$DataType = clone $MethodName;
$DataType->replace( new String( '/^(get|has|add|remove)/s' ), new
String() );
$MethodName->replace( new String( '/' . $DataType->toString() .
'$/' ), new String() );
$MethodName->concat( new String( 'Element' ) );
if( $DataType->equals( $this->getDataType() ) === true ){
return call_user_func_array
( array( &$this, $MethodName->toString() ), $MethodParams );
}
else {
throw new Exception( 'Container type invalid' );
}
}
}
}
?>