1 | <?php |
||
23 | class ModelObjNode extends BaseNode |
||
24 | { |
||
25 | /** |
||
26 | * @var int|string |
||
27 | */ |
||
28 | protected $id; |
||
29 | |||
30 | /** |
||
31 | * @var EEM_Base |
||
32 | */ |
||
33 | protected $model; |
||
34 | |||
35 | /** |
||
36 | * @var RelationNode[] |
||
37 | */ |
||
38 | protected $nodes; |
||
39 | |||
40 | /** |
||
41 | * We don't pass the model objects because this needs to serialize to something tiny for effiency. |
||
42 | * @param $model_obj_id |
||
43 | * @param EEM_Base $model |
||
44 | */ |
||
45 | public function __construct($model_obj_id, EEM_Base $model, array $dont_traverse_models = []]) |
||
|
|||
46 | { |
||
47 | $this->id = $model_obj_id; |
||
48 | $this->model = $model; |
||
49 | $this->dont_traverse_models = $dont_traverse_models; |
||
50 | } |
||
51 | |||
52 | /** |
||
53 | * Creates a relation node for each relation of this model's relations. |
||
54 | * Does NOT call `discover` on them yet though. |
||
55 | * @since $VID:$ |
||
56 | * @throws \EE_Error |
||
57 | * @throws InvalidDataTypeException |
||
58 | * @throws InvalidInterfaceException |
||
59 | * @throws InvalidArgumentException |
||
60 | * @throws ReflectionException |
||
61 | */ |
||
62 | protected function discover() |
||
63 | { |
||
64 | $this->nodes = []; |
||
65 | foreach ($this->model->relation_settings() as $relationName => $relation) { |
||
66 | // Make sure this isn't one of the models we were told to not traverse into. |
||
67 | if (in_array($relationName, $this->dont_traverse_models)) { |
||
68 | continue; |
||
69 | } |
||
70 | if ($relation instanceof EE_Has_Many_Relation) { |
||
71 | $this->nodes[ $relationName ] = new RelationNode( |
||
72 | $this->id, |
||
73 | $this->model, |
||
74 | $relation->get_other_model(), |
||
75 | $this->dont_traverse_models |
||
76 | ); |
||
77 | } elseif ($relation instanceof EE_HABTM_Relation && |
||
78 | ! in_array( |
||
79 | $relation->get_join_model()->get_this_model_name(), |
||
80 | $this->dont_traverse_models |
||
81 | )) { |
||
82 | $this->nodes[ $relation->get_join_model()->get_this_model_name() ] = new RelationNode( |
||
83 | $this->id, |
||
84 | $this->model, |
||
85 | $relation->get_join_model(), |
||
86 | $this->dont_traverse_models |
||
87 | ); |
||
88 | } |
||
89 | } |
||
90 | ksort($this->nodes); |
||
91 | } |
||
92 | |||
93 | |||
94 | /** |
||
95 | * Whether this item has already been initialized |
||
96 | */ |
||
97 | protected function isDiscovered() |
||
98 | { |
||
99 | return $this->nodes !== null && is_array($this->nodes); |
||
100 | } |
||
101 | |||
102 | /** |
||
103 | * @since $VID:$ |
||
104 | * @return boolean |
||
105 | */ |
||
106 | public function isComplete() |
||
107 | { |
||
108 | if ($this->complete === null) { |
||
109 | $this->complete = false; |
||
110 | } |
||
111 | return $this->complete; |
||
112 | } |
||
113 | |||
114 | /** |
||
115 | * Triggers working on each child relation node that has work to do. |
||
116 | * @since $VID:$ |
||
117 | * @param $model_objects_to_identify |
||
118 | * @return int units of work done |
||
119 | */ |
||
120 | protected function work($model_objects_to_identify) |
||
121 | { |
||
122 | $num_identified = 0; |
||
123 | // Begin assuming we'll finish all the work on this node and its children... |
||
124 | $this->complete = true; |
||
125 | foreach ($this->nodes as $model_name => $relation_node) { |
||
126 | $num_identified += $relation_node->visit($model_objects_to_identify - $num_identified); |
||
127 | // To save on space when serializing, only bother keeping a record of relation nodes that actually found |
||
128 | // related model objects. |
||
129 | if ($relation_node->isComplete() && $relation_node->countSubNodes() === 0) { |
||
130 | unset($this->nodes[ $model_name ]); |
||
131 | } |
||
132 | if ($num_identified >= $model_objects_to_identify) { |
||
133 | // ...but admit we're wrong if the work exceeded the budget. |
||
134 | $this->complete = false; |
||
135 | break; |
||
136 | } |
||
137 | } |
||
138 | return $num_identified; |
||
139 | } |
||
140 | |||
141 | /** |
||
142 | * @since $VID:$ |
||
143 | * @return array |
||
144 | * @throws \EE_Error |
||
145 | * @throws InvalidDataTypeException |
||
146 | * @throws InvalidInterfaceException |
||
147 | * @throws InvalidArgumentException |
||
148 | * @throws ReflectionException |
||
149 | */ |
||
150 | public function toArray() |
||
151 | { |
||
152 | $tree = [ |
||
153 | 'id' => $this->id, |
||
154 | 'complete' => $this->isComplete(), |
||
155 | 'rels' => [] |
||
156 | ]; |
||
157 | if ($this->nodes === null) { |
||
158 | $tree['rels'] = null; |
||
159 | } else { |
||
160 | foreach ($this->nodes as $relation_name => $relation_node) { |
||
161 | $tree['rels'][ $relation_name ] = $relation_node->toArray(); |
||
162 | } |
||
163 | } |
||
164 | return $tree; |
||
165 | } |
||
166 | |||
167 | /** |
||
168 | * @since $VID:$ |
||
169 | * @return array|mixed |
||
170 | * @throws InvalidArgumentException |
||
171 | * @throws InvalidDataTypeException |
||
172 | * @throws InvalidInterfaceException |
||
173 | * @throws ReflectionException |
||
174 | * @throws \EE_Error |
||
175 | */ |
||
176 | public function getIds() |
||
177 | { |
||
178 | $ids = [ |
||
179 | $this->model->get_this_model_name() => [ |
||
180 | $this->id => $this->id |
||
181 | ] |
||
182 | ]; |
||
183 | if ($this->nodes && is_array($this->nodes)) { |
||
184 | foreach ($this->nodes as $relation_node) { |
||
185 | $ids = array_replace_recursive($ids, $relation_node->getIds()); |
||
186 | } |
||
187 | } |
||
188 | return $ids; |
||
189 | } |
||
190 | |||
191 | /** |
||
192 | * Don't serialize the models. Just record their names on some dynamic properties. |
||
193 | * @since $VID:$ |
||
194 | */ |
||
195 | public function __sleep() |
||
196 | { |
||
197 | $this->m = $this->model->get_this_model_name(); |
||
198 | return array_merge( |
||
199 | [ |
||
200 | 'm', |
||
201 | 'id', |
||
202 | 'nodes', |
||
203 | ], |
||
204 | parent::__sleep() |
||
205 | ); |
||
206 | } |
||
207 | |||
208 | /** |
||
209 | * Use the dynamic properties to instantiate the models we use. |
||
210 | * @since $VID:$ |
||
211 | * @throws EE_Error |
||
212 | * @throws InvalidArgumentException |
||
213 | * @throws InvalidDataTypeException |
||
214 | * @throws InvalidInterfaceException |
||
215 | * @throws ReflectionException |
||
216 | */ |
||
217 | public function __wakeup() |
||
218 | { |
||
219 | $this->model = EE_Registry::instance()->load_model($this->m); |
||
220 | parent::__wakeup(); |
||
221 | } |
||
222 | } |
||
223 | // End of file Visitor.php |
||
225 |