Патерны программирования. DesignPatternsphp documentation
Скачать 1.98 Mb.
|
1 2 3 declare (strict_types = 1 ); (continues on next page) 1.4. Дополнительно 143 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 4 5 namespace DesignPatterns\More\Repository\Domain; 6 7 use InvalidArgumentException; 8 9 /** 10 * This is a perfect example of a value object that is identifiable by it's value alone ␣ ˓→ and 11 * is guaranteed to be valid each time an instance is created. Another important ␣ ˓→ property of value objects 12 * is immutability. 13 * 14 * Notice also the use of a named constructor (fromInt) which adds a little context when ␣ ˓→ creating an instance. 15 */ 16 class PostId 17 { 18 public static function fromInt (int $id ) : PostId 19 { 20 self :: ensureIsValid ( $id ); 21 22 return new self( $id ); 23 } 24 25 private function __construct ( private int $id ) 26 { 27 } 28 29 public function toInt () : int 30 { 31 return $this -> id ; 32 } 33 34 private static function ensureIsValid (int $id ) 35 { 36 if ( $id <= 0 ) { 37 throw new InvalidArgumentException( 'Invalid PostId given' ); 38 } 39 } 40 } PostStatus.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\More\Repository\Domain; 6 7 use InvalidArgumentException; 8 9 /** (continues on next page) 144 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 10 * Like PostId, this is a value object which holds the value of the current status of a ␣ ˓→ Post. It can be constructed 11 * either from a string or int and is able to validate itself. An instance can then be ␣ ˓→ converted back to int or string. 12 */ 13 class PostStatus 14 { 15 public const STATE_DRAFT_ID = 1 ; 16 public const STATE_PUBLISHED_ID = 2 ; 17 18 public const STATE_DRAFT = 'draft' ; 19 public const STATE_PUBLISHED = 'published' ; 20 21 private static array $validStates = [ 22 self :: STATE_DRAFT_ID => self :: STATE_DRAFT , 23 self :: STATE_PUBLISHED_ID => self :: STATE_PUBLISHED , 24 ]; 25 26 public static function fromInt (int $statusId ) 27 { 28 self :: ensureIsValidId ( $statusId ); 29 30 return new self( $statusId , self :: $validStates [ $statusId ]); 31 } 32 33 public static function fromString (string $status ) 34 { 35 self :: ensureIsValidName ( $status ); 36 $state = array_search ( $status , self :: $validStates ); 37 38 if ( $state === false ) { 39 throw new InvalidArgumentException( 'Invalid state given!' ); 40 } 41 42 return new self( $state , $status ); 43 } 44 45 private function __construct ( private int $id , private string $name ) 46 { 47 } 48 49 public function toInt () : int 50 { 51 return $this -> id ; 52 } 53 54 /** 55 * there is a reason that I avoid using __toString() as it operates outside of the ␣ ˓→ stack in PHP 56 * and is therefore not able to operate well with exceptions 57 */ 58 public function toString () : string (continues on next page) 1.4. Дополнительно 145 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 59 { 60 return $this -> name ; 61 } 62 63 private static function ensureIsValidId (int $status ) 64 { 65 if ( ! in_array ( $status , array_keys (self :: $validStates ), true )) { 66 throw new InvalidArgumentException( 'Invalid status id given' ); 67 } 68 } 69 70 71 private static function ensureIsValidName (string $status ) 72 { 73 if ( ! in_array ( $status , self :: $validStates , true )) { 74 throw new InvalidArgumentException( 'Invalid status name given' ); 75 } 76 } 77 } PostRepository.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\More\Repository; 6 7 use OutOfBoundsException; 8 use DesignPatterns\More\Repository\Domain\Post; 9 use DesignPatterns\More\Repository\Domain\PostId; 10 11 /** 12 * This class is situated between Entity layer (class Post) and access object layer ␣ ˓→ (Persistence). 13 * 14 * Repository encapsulates the set of objects persisted in a data store and the ␣ ˓→ operations performed over them 15 * providing a more object-oriented view of the persistence layer 16 * 17 * Repository also supports the objective of achieving a clean separation and one-way ␣ ˓→ dependency 18 * between the domain and data mapping layers 19 */ 20 class PostRepository 21 { 22 public function __construct ( private Persistence $persistence ) 23 { 24 } 25 26 public function generateId () : PostId 27 { (continues on next page) 146 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 28 return PostId :: fromInt ( $this -> persistence -> generateId ()); 29 } 30 31 public function findById (PostId $id ) : Post 32 { 33 try { 34 $arrayData = $this -> persistence -> retrieve ( $id -> toInt ()); 35 } catch (OutOfBoundsException $e ) { 36 throw new OutOfBoundsException( sprintf ( 'Post with id %d does not exist' , $id - ˓→ > toInt ()), 0 , $e ); 37 } 38 39 return Post :: fromState ( $arrayData ); 40 } 41 42 public function save (Post $post ) 43 { 44 $this -> persistence -> persist ([ 45 'id' => $post -> getId () -> toInt (), 46 'statusId' => $post -> getStatus () -> toInt (), 47 'text' => $post -> getText (), 48 'title' => $post -> getTitle (), 49 ]); 50 } 51 } Persistence.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\More\Repository; 6 7 interface Persistence 8 { 9 public function generateId () : int; 10 11 public function persist ( array $data ); 12 13 public function retrieve (int $id ) : array ; 14 15 public function delete (int $id ); 16 } InMemoryPersistence.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\More\Repository; (continues on next page) 1.4. Дополнительно 147 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 6 7 use OutOfBoundsException; 8 9 class InMemoryPersistence implements Persistence 10 { 11 private array $data = []; 12 private int $lastId = 0 ; 13 14 public function generateId () : int 15 { 16 $this -> lastId ++ ; 17 18 return $this -> lastId ; 19 } 20 21 public function persist ( array $data ) 22 { 23 $this -> data [ $this -> lastId ] = $data ; 24 } 25 26 public function retrieve (int $id ) : array 27 { 28 if ( ! isset ( $this -> data [ $id ])) { 29 throw new OutOfBoundsException( sprintf ( 'No data found for ID %d' , $id )); 30 } 31 32 return $this -> data [ $id ]; 33 } 34 35 public function delete (int $id ) 36 { 37 if ( ! isset ( $this -> data [ $id ])) { 38 throw new OutOfBoundsException( sprintf ( 'No data found for ID %d' , $id )); 39 } 40 41 unset ( $this -> data [ $id ]); 42 } 43 } Тест Tests/PostRepositoryTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\More\Repository\Tests; 6 7 use OutOfBoundsException; 8 use DesignPatterns\More\Repository\Domain\PostId; (continues on next page) 148 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 9 use DesignPatterns\More\Repository\Domain\PostStatus; 10 use DesignPatterns\More\Repository\InMemoryPersistence; 11 use DesignPatterns\More\Repository\Domain\Post; 12 use DesignPatterns\More\Repository\PostRepository; 13 use PHPUnit\Framework\TestCase; 14 15 class PostRepositoryTest extends TestCase 16 { 17 private PostRepository $repository ; 18 19 protected function setUp () : void 20 { 21 $this -> repository = new PostRepository( new InMemoryPersistence()); 22 } 23 24 public function testCanGenerateId () 25 { 26 $this -> assertEquals ( 1 , $this -> repository -> generateId () -> toInt ()); 27 } 28 29 public function testThrowsExceptionWhenTryingToFindPostWhichDoesNotExist () 30 { 31 $this -> expectException (OutOfBoundsException :: class ); 32 $this -> expectExceptionMessage ( 'Post with id 42 does not exist' ); 33 34 $this -> repository -> findById (PostId :: fromInt ( 42 )); 35 } 36 37 public function testCanPersistPostDraft () 38 { 39 $postId = $this -> repository -> generateId (); 40 $post = Post :: draft ( $postId , 'Repository Pattern' , 'Design Patterns PHP' ); 41 $this -> repository -> save ( $post ); 42 43 $this -> repository -> findById ( $postId ); 44 45 $this -> assertEquals ( $postId , $this -> repository -> findById ( $postId ) -> getId ()); 46 $this -> assertEquals (PostStatus :: STATE_DRAFT , $post -> getStatus () -> toString ()); 47 } 48 } 1.4. Дополнительно 149 DesignPatternsPHP Documentation, Выпуск 1.0 1.4.3 Сущность-Атрибут-Значение Шаблон Сущность-Атрибут-Значение используется для реализации модели EAV на PHP Назначение Модель Сущность-Атрибут-Значение (EAV) - это модель данных, предназначенная для описания сущ- ностей, в которых количество атрибутов (свойств, параметров), характеризующих их, потенциально огромно, но то количество, которое реально будет использоваться в конкретной сущности, относитель- но мало. Диаграмма UML 150 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Код Вы можете найти этот код на GitHub Entity.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\More\EAV; 6 7 use SplObjectStorage; 8 9 class Entity implements \Stringable 10 { 11 /** 12 * @var SplObjectStorage 13 */ 14 private $values ; 15 16 /** 17 * @param Value[] $values 18 */ 19 public function __construct ( private string $name , array $values ) 20 { 21 $this -> values = new SplObjectStorage(); 22 23 foreach ( $values as $value ) { 24 $this -> values -> attach ( $value ); 25 } 26 } 27 28 public function __toString () : string 29 { 30 $text = [ $this -> name ]; 31 32 foreach ( $this -> values as $value ) { 33 $text [] = (string) $value ; 34 } 35 36 return join ( ', ' , $text ); 37 } 38 } Attribute.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\More\EAV; 6 7 use SplObjectStorage; (continues on next page) 1.4. Дополнительно 151 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 8 9 class Attribute implements \Stringable 10 { 11 private SplObjectStorage $values ; 12 13 public function __construct ( private string $name ) 14 { 15 $this -> values = new SplObjectStorage(); 16 } 17 18 public function addValue (Value $value ) : void 19 { 20 $this -> values -> attach ( $value ); 21 } 22 23 public function getValues () : SplObjectStorage 24 { 25 return $this -> values ; 26 } 27 28 public function __toString () : string 29 { 30 return $this -> name ; 31 } 32 } Value.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\More\EAV; 6 7 class Value implements \Stringable 8 { 9 public function __construct ( private Attribute $attribute , private string $name ) 10 { 11 $attribute -> addValue ( $this ); 12 } 13 14 public function __toString () : string 15 { 16 return sprintf ( '%s: %s' , (string) $this -> attribute , $this -> name ); 17 } 18 } 152 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Тест Tests/EAVTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\More\EAV\Tests; 6 7 use DesignPatterns\More\EAV\Attribute; 8 use DesignPatterns\More\EAV\Entity; 9 use DesignPatterns\More\EAV\Value; 10 use PHPUnit\Framework\TestCase; 11 12 class EAVTest extends TestCase 13 { 14 public function testCanAddAttributeToEntity () : void 15 { 16 $colorAttribute = new Attribute( 'color' ); 17 $colorSilver = new Value( $colorAttribute , 'silver' ); 18 $colorBlack = new Value( $colorAttribute , 'black' ); 19 20 $memoryAttribute = new Attribute( 'memory' ); 21 $memory8Gb = new Value( $memoryAttribute , '8GB' ); 22 23 $entity = new Entity( 'MacBook Pro' , [ $colorSilver , $colorBlack , $memory8Gb ]); 24 25 $this -> assertEquals ( 'MacBook Pro, color: silver, color: black, memory: 8GB' , ␣ ˓→ (string) $entity ); 26 } 27 } 1.4. Дополнительно 153 |