1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace EventEspresso\core\services\orm\tree_traversal; |
4
|
|
|
|
5
|
|
|
use EE_Base_Class; |
6
|
|
|
use EE_Error; |
7
|
|
|
use EE_Model_Relation_Base; |
8
|
|
|
use EEM_Base; |
9
|
|
|
use EventEspresso\core\exceptions\InvalidDataTypeException; |
10
|
|
|
use EventEspresso\core\exceptions\InvalidInterfaceException; |
11
|
|
|
use EventEspresso\core\services\payment_methods\forms\PayPalSettingsForm; |
12
|
|
|
use InvalidArgumentException; |
13
|
|
|
use ReflectionException; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Class RelationNode |
17
|
|
|
* |
18
|
|
|
* Wraps a model object and one of its model's relations; stores how many related model objects exist across that |
19
|
|
|
* relation, and eventually createsa a ModelObjNode for each of its related model objects. |
20
|
|
|
* |
21
|
|
|
* @package Event Espresso |
22
|
|
|
* @author Mike Nelson |
23
|
|
|
* @since $VID:$ |
24
|
|
|
* |
25
|
|
|
*/ |
26
|
|
|
class RelationNode extends BaseNode |
27
|
|
|
{ |
28
|
|
|
/** |
29
|
|
|
* @var EE_Base_Class |
30
|
|
|
*/ |
31
|
|
|
protected $main_model_obj; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @var int |
35
|
|
|
*/ |
36
|
|
|
protected $count; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var EEM_Base |
40
|
|
|
*/ |
41
|
|
|
protected $related_model; |
42
|
|
|
|
43
|
|
|
|
44
|
|
|
protected $model_obj_nodes; |
45
|
|
|
|
46
|
|
|
public function __construct($main_model_obj, $related_model) |
47
|
|
|
{ |
48
|
|
|
$this->main_model_obj = $main_model_obj; |
49
|
|
|
$this->related_model = $related_model; |
50
|
|
|
$this->model_obj_nodes = []; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Here is where most of the work happens. We've counted how many related model objects exist, here we identify |
56
|
|
|
* them (ie, learn their IDs). But its recursive, so we'll also find their related dependent model objects etc. |
57
|
|
|
* @since $VID:$ |
58
|
|
|
* @param int $model_objects_to_identify |
59
|
|
|
* @return int |
60
|
|
|
* @throws EE_Error |
61
|
|
|
* @throws InvalidArgumentException |
62
|
|
|
* @throws InvalidDataTypeException |
63
|
|
|
* @throws InvalidInterfaceException |
64
|
|
|
* @throws ReflectionException |
65
|
|
|
*/ |
66
|
|
|
protected function work($model_objects_to_identify) |
67
|
|
|
{ |
68
|
|
|
$num_identified = $this->visitAlreadyDiscoveredNodes($this->model_obj_nodes, $model_objects_to_identify); |
69
|
|
|
if ($num_identified < $model_objects_to_identify) { |
70
|
|
|
$related_model_objs = $this->related_model->get_all( |
71
|
|
|
[ |
72
|
|
|
$this->whereQueryParams(), |
73
|
|
|
'limit' => [ |
74
|
|
|
count($this->model_obj_nodes), |
75
|
|
|
$model_objects_to_identify |
76
|
|
|
] |
77
|
|
|
] |
78
|
|
|
); |
79
|
|
|
$new_item_nodes = []; |
80
|
|
|
|
81
|
|
|
// Add entity nodes for each of the model objects we fetched. |
82
|
|
|
foreach ($related_model_objs as $related_model_obj) { |
83
|
|
|
$entity_node = new ModelObjNode($related_model_obj); |
84
|
|
|
$this->model_obj_nodes[ $related_model_obj->ID() ] = $entity_node; |
85
|
|
|
$new_item_nodes[ $related_model_obj->ID() ] = $entity_node; |
86
|
|
|
} |
87
|
|
|
$num_identified += count($new_item_nodes); |
88
|
|
|
if ($num_identified < $model_objects_to_identify) { |
89
|
|
|
// And lastly do the work. |
90
|
|
|
$num_identified += $this->visitAlreadyDiscoveredNodes( |
91
|
|
|
$new_item_nodes, |
92
|
|
|
$model_objects_to_identify - $num_identified |
93
|
|
|
); |
94
|
|
|
} |
95
|
|
|
} |
96
|
|
|
|
97
|
|
View Code Duplication |
if (count($this->model_obj_nodes) >= $this->count && $this->allChildrenComplete()) { |
98
|
|
|
$this->complete = true; |
99
|
|
|
} |
100
|
|
|
return $num_identified; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Checks if all the identified child nodes are complete or not. |
105
|
|
|
* @since $VID:$ |
106
|
|
|
* @return bool |
107
|
|
|
*/ |
108
|
|
|
protected function allChildrenComplete() |
109
|
|
|
{ |
110
|
|
|
foreach ($this->model_obj_nodes as $model_obj_node) { |
111
|
|
|
if (! $model_obj_node->isComplete()) { |
112
|
|
|
return false; |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
return true; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Visits the provided nodes and keeps track of how much work was done, making sure to not go over budget. |
120
|
|
|
* @since $VID:$ |
121
|
|
|
* @param ModelObjNode[] $model_obj_nodes |
122
|
|
|
* @param $work_budget |
123
|
|
|
* @return int |
124
|
|
|
*/ |
125
|
|
|
protected function visitAlreadyDiscoveredNodes($model_obj_nodes, $work_budget) |
126
|
|
|
{ |
127
|
|
|
$work_done = 0; |
128
|
|
|
if (! $model_obj_nodes) { |
|
|
|
|
129
|
|
|
return 0; |
130
|
|
|
} |
131
|
|
|
foreach ($model_obj_nodes as $model_obj_node) { |
132
|
|
|
if ($work_done >= $work_budget) { |
133
|
|
|
break; |
134
|
|
|
} |
135
|
|
|
$work_done += $model_obj_node->visit($work_budget - $work_done); |
136
|
|
|
} |
137
|
|
|
return $work_done; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Whether this item has already been initialized |
142
|
|
|
*/ |
143
|
|
|
protected function isDiscovered() |
144
|
|
|
{ |
145
|
|
|
return $this->count !== null; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* @since $VID:$ |
150
|
|
|
* @return boolean |
151
|
|
|
*/ |
152
|
|
|
public function isComplete() |
153
|
|
|
{ |
154
|
|
|
if ($this->complete === null) { |
155
|
|
View Code Duplication |
if (count($this->model_obj_nodes) === $this->count) { |
156
|
|
|
$this->complete = true; |
157
|
|
|
} else { |
158
|
|
|
$this->complete = false; |
159
|
|
|
} |
160
|
|
|
} |
161
|
|
|
return $this->complete; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Discovers how many related model objects exist. |
166
|
|
|
* @since $VID:$ |
167
|
|
|
* @return mixed|void |
168
|
|
|
* @throws EE_Error |
169
|
|
|
* @throws InvalidArgumentException |
170
|
|
|
* @throws InvalidDataTypeException |
171
|
|
|
* @throws InvalidInterfaceException |
172
|
|
|
* @throws ReflectionException |
173
|
|
|
*/ |
174
|
|
|
protected function discover() |
175
|
|
|
{ |
176
|
|
|
$this->count = $this->related_model->count([$this->whereQueryParams()]); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* @since $VID:$ |
181
|
|
|
* @return array |
182
|
|
|
* @throws EE_Error |
183
|
|
|
* @throws InvalidDataTypeException |
184
|
|
|
* @throws InvalidInterfaceException |
185
|
|
|
* @throws InvalidArgumentException |
186
|
|
|
* @throws ReflectionException |
187
|
|
|
*/ |
188
|
|
|
protected function whereQueryParams() |
189
|
|
|
{ |
190
|
|
|
return [ |
191
|
|
|
$this->related_model->get_foreign_key_to( |
192
|
|
|
$this->main_model_obj->get_model()->get_this_model_name() |
193
|
|
|
)->get_name() => $this->main_model_obj->ID() |
194
|
|
|
]; |
195
|
|
|
} |
196
|
|
|
/** |
197
|
|
|
* @since $VID:$ |
198
|
|
|
* @return array |
199
|
|
|
*/ |
200
|
|
|
public function toArray() |
201
|
|
|
{ |
202
|
|
|
$tree = [ |
203
|
|
|
'count' => $this->count, |
204
|
|
|
'complete' => $this->isComplete(), |
205
|
|
|
'objs' => [] |
206
|
|
|
]; |
207
|
|
|
foreach ($this->model_obj_nodes as $id => $model_obj_node) { |
208
|
|
|
$tree['objs'][ $id ] = $model_obj_node->toArray(); |
209
|
|
|
} |
210
|
|
|
return $tree; |
211
|
|
|
} |
212
|
|
|
} |
213
|
|
|
// End of file RelationNode.php |
214
|
|
|
// Location: EventEspresso\core\services\orm\tree_traversal/RelationNode.php |
215
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.