1
+ <?php
2
+
3
+ namespace Simply \Database ;
4
+
5
+ use Simply \Database \Connection \Connection ;
6
+
7
+ /**
8
+ * ReferenceFiller.
9
+ * @author Riikka Kalliomäki <[email protected] >
10
+ * @copyright Copyright (c) 2018 Riikka Kalliomäki
11
+ * @license http://opensource.org/licenses/mit-license.php MIT License
12
+ */
13
+ class ReferenceFiller
14
+ {
15
+ private $ connection ;
16
+ private $ cache ;
17
+
18
+ public function __construct (Connection $ connection )
19
+ {
20
+ $ this ->connection = $ connection ;
21
+ }
22
+
23
+ /**
24
+ * @param Record[] $records
25
+ * @param string[] $references
26
+ */
27
+ public function fill (array $ records , array $ references ): void
28
+ {
29
+ if (empty ($ records )) {
30
+ return ;
31
+ }
32
+
33
+ $ this ->cache = [];
34
+ $ schema = reset ($ records )->getSchema ();
35
+ $ schemaId = $ this ->getSchemaId ($ schema );
36
+
37
+ foreach ($ records as $ record ) {
38
+ if ($ record ->getSchema () !== $ schema ) {
39
+ throw new \InvalidArgumentException ('The provided list of records did not share the same schema ' );
40
+ }
41
+
42
+ $ recordId = $ this ->getRecordId ($ schema , $ record ->getDatabaseValues ());
43
+ $ this ->cache [$ schemaId ][$ recordId ] = $ record ;
44
+ }
45
+
46
+ $ this ->fillReferences ($ records , $ references );
47
+ }
48
+
49
+ /**
50
+ * @param Record[] $records
51
+ * @param string[] $references
52
+ */
53
+ private function fillReferences (array $ records , array $ references ): void
54
+ {
55
+ $ schema = reset ($ records )->getSchema ();
56
+
57
+ foreach ($ this ->parseReferences ($ references ) as $ name => $ childReferences ) {
58
+ $ reference = $ schema ->getReference ($ name );
59
+ $ keys = $ reference ->getFields ();
60
+ $ fields = $ reference ->getReferencedFields ();
61
+ $ parent = $ reference ->getReferencedSchema ();
62
+ $ schemaId = $ this ->getSchemaId ($ parent );
63
+
64
+ if (\count ($ fields ) > 1 ) {
65
+ throw new \RuntimeException ('Filling references for composite foreign keys is not supported ' );
66
+ }
67
+
68
+ $ key = array_pop ($ keys );
69
+ $ field = array_pop ($ fields );
70
+ $ options = [];
71
+
72
+ foreach ($ records as $ record ) {
73
+ $ options [] = $ record [$ key ];
74
+ }
75
+
76
+ $ result = $ this ->connection ->select ($ parent ->getFields (), $ parent ->getTable (), [$ field => $ options ]);
77
+ $ result ->setFetchMode (\PDO ::FETCH_ASSOC );
78
+ $ sorted = [];
79
+
80
+ foreach ($ result as $ row ) {
81
+ $ record = $ this ->getCachedRecord ($ schemaId , $ parent , $ row );
82
+ $ sorted [$ record [$ field ]][] = $ record ;
83
+ }
84
+
85
+ foreach ($ records as $ record ) {
86
+ $ record ->fillReference ($ name , $ sorted [$ record [$ key ]] ?? []);
87
+ }
88
+
89
+ if ($ sorted && $ childReferences ) {
90
+ $ this ->fillReferences (array_merge (... $ sorted ), $ childReferences );
91
+ }
92
+ }
93
+ }
94
+
95
+ private function parseReferences (array $ references ): array
96
+ {
97
+ $ subReferences = [];
98
+
99
+ foreach ($ references as $ reference ) {
100
+ $ parts = explode ('. ' , $ reference , 2 );
101
+
102
+ if (!isset ($ subReferences [$ parts [0 ]])) {
103
+ $ subReferences [$ parts [0 ]] = [];
104
+ }
105
+
106
+ if (isset ($ parts [1 ])) {
107
+ $ subReferences [$ parts [0 ]][] = $ parts [1 ];
108
+ }
109
+ }
110
+
111
+ return $ subReferences ;
112
+ }
113
+
114
+ private function getCachedRecord (string $ schemaId , Schema $ schema , array $ row ): Record
115
+ {
116
+ $ recordId = $ this ->getRecordId ($ schema , $ row );
117
+
118
+ if (isset ($ this ->cache [$ schemaId ][$ recordId ])) {
119
+ return $ this ->cache [$ schemaId ][$ recordId ];
120
+ }
121
+
122
+ $ record = $ schema ->getRecord ($ row );
123
+ $ this ->cache [$ schemaId ][$ recordId ] = $ record ;
124
+ return $ record ;
125
+ }
126
+
127
+ private function getSchemaId (Schema $ schema ): string
128
+ {
129
+ return spl_object_hash ($ schema );
130
+ }
131
+
132
+ private function getRecordId (Schema $ schema , array $ row )
133
+ {
134
+ $ values = [];
135
+
136
+ foreach ($ schema ->getPrimaryKeys () as $ key ) {
137
+ if (!isset ($ row [$ key ])) {
138
+ throw new \RuntimeException ('Cannot determine cache id for record ' );
139
+ }
140
+
141
+ $ values [] = $ row [$ key ];
142
+ }
143
+
144
+ return implode ('- ' , $ values );
145
+ }
146
+ }
0 commit comments