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 $e ) { |
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 | } |