Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
39.53% |
17 / 43 |
|
28.57% |
2 / 7 |
CRAP | |
0.00% |
0 / 1 |
| CachingObjectMapper | |
39.53% |
17 / 43 |
|
28.57% |
2 / 7 |
72.59 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
| model | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
| toStorageRow | |
40.00% |
4 / 10 |
|
0.00% |
0 / 1 |
7.46 | |||
| fromStorageRow | |
53.85% |
7 / 13 |
|
0.00% |
0 / 1 |
7.46 | |||
| get | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
| normalizeRow | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| clear | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace Flow\Data\Mapper; |
| 4 | |
| 5 | use Flow\Data\ObjectManager; |
| 6 | use Flow\Data\ObjectMapper; |
| 7 | use Flow\Data\Utils\MultiDimArray; |
| 8 | use Flow\Model\UUID; |
| 9 | use InvalidArgumentException; |
| 10 | use OutOfBoundsException; |
| 11 | |
| 12 | /** |
| 13 | * Rows with the same primary key always return the same object |
| 14 | * from self::fromStorageRow. This means that if two parts of the |
| 15 | * code both load revision 123 they will receive the same object. |
| 16 | */ |
| 17 | class CachingObjectMapper implements ObjectMapper { |
| 18 | /** |
| 19 | * @var callable |
| 20 | */ |
| 21 | protected $toStorageRow; |
| 22 | |
| 23 | /** |
| 24 | * @var callable |
| 25 | */ |
| 26 | protected $fromStorageRow; |
| 27 | |
| 28 | /** |
| 29 | * @var string[] |
| 30 | */ |
| 31 | protected $primaryKey; |
| 32 | |
| 33 | /** |
| 34 | * @var MultiDimArray |
| 35 | */ |
| 36 | protected $loaded; |
| 37 | |
| 38 | /** |
| 39 | * @param callable $toStorageRow |
| 40 | * @param callable $fromStorageRow |
| 41 | * @param string[] $primaryKey |
| 42 | */ |
| 43 | public function __construct( $toStorageRow, $fromStorageRow, array $primaryKey ) { |
| 44 | $this->toStorageRow = $toStorageRow; |
| 45 | $this->fromStorageRow = $fromStorageRow; |
| 46 | ksort( $primaryKey ); |
| 47 | $this->primaryKey = $primaryKey; |
| 48 | $this->clear(); |
| 49 | } |
| 50 | |
| 51 | /** |
| 52 | * @param string $className Fully qualified class name |
| 53 | * @param string[] $primaryKey |
| 54 | * @return CachingObjectMapper |
| 55 | */ |
| 56 | public static function model( $className, array $primaryKey ) { |
| 57 | return new self( |
| 58 | [ $className, 'toStorageRow' ], |
| 59 | [ $className, 'fromStorageRow' ], |
| 60 | $primaryKey |
| 61 | ); |
| 62 | } |
| 63 | |
| 64 | public function toStorageRow( $object ) { |
| 65 | $row = ( $this->toStorageRow )( $object ); |
| 66 | $pk = ObjectManager::splitFromRow( $row, $this->primaryKey ); |
| 67 | if ( $pk === null ) { |
| 68 | // new object may not have pk yet, calling code |
| 69 | // should call self::fromStorageRow with $object to load |
| 70 | // db assigned pk and store obj in $this->loaded |
| 71 | } elseif ( !isset( $this->loaded[$pk] ) ) { |
| 72 | // first time this id has been seen |
| 73 | $this->loaded[$pk] = $object; |
| 74 | } elseif ( $this->loaded[$pk] !== $object ) { |
| 75 | // loaded object of this id is not same object |
| 76 | $class = get_class( $object ); |
| 77 | $id = json_encode( $pk ); |
| 78 | throw new \InvalidArgumentException( "Duplicate '$class' objects for id $id" ); |
| 79 | } |
| 80 | return $row; |
| 81 | } |
| 82 | |
| 83 | public function fromStorageRow( array $row, $object = null ) { |
| 84 | $pk = ObjectManager::splitFromRow( $row, $this->primaryKey ); |
| 85 | if ( $pk === null ) { |
| 86 | throw new \InvalidArgumentException( 'Storage row has no pk' ); |
| 87 | } elseif ( !isset( $this->loaded[$pk] ) ) { |
| 88 | // unserialize the object |
| 89 | $this->loaded[$pk] = ( $this->fromStorageRow )( $row, $object ); |
| 90 | return $this->loaded[$pk]; |
| 91 | } elseif ( $object === null ) { |
| 92 | // provide previously loaded object |
| 93 | return $this->loaded[$pk]; |
| 94 | } elseif ( $object !== $this->loaded[$pk] ) { |
| 95 | // loaded object of this id is not same object |
| 96 | $class = get_class( $object ); |
| 97 | $id = json_encode( $pk ); |
| 98 | throw new \InvalidArgumentException( "Duplicate '$class' objects for id $id" ); |
| 99 | } else { |
| 100 | // object was provided, load $row into $object |
| 101 | // we already know $this->loaded[$pk] === $object |
| 102 | return ( $this->fromStorageRow )( $row, $object ); |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | /** |
| 107 | * @param array $primaryKey |
| 108 | * @return object|null |
| 109 | * @throws InvalidArgumentException |
| 110 | */ |
| 111 | public function get( array $primaryKey ) { |
| 112 | $primaryKey = UUID::convertUUIDs( $primaryKey, 'alphadecimal' ); |
| 113 | ksort( $primaryKey ); |
| 114 | if ( array_keys( $primaryKey ) !== $this->primaryKey ) { |
| 115 | throw new InvalidArgumentException; |
| 116 | } |
| 117 | try { |
| 118 | return $this->loaded[$primaryKey]; |
| 119 | } catch ( OutOfBoundsException ) { |
| 120 | return null; |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | /** |
| 125 | * @inheritDoc |
| 126 | */ |
| 127 | public function normalizeRow( array $row ) { |
| 128 | $object = ( $this->fromStorageRow )( $row ); |
| 129 | return ( $this->toStorageRow )( $object ); |
| 130 | } |
| 131 | |
| 132 | public function clear() { |
| 133 | $this->loaded = new MultiDimArray; |
| 134 | } |
| 135 | } |