Completed
Branch CASC/base (3cd0b5)
by
unknown
24:25 queued 16:20
created

RelationNode::getIds()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 0
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
1
<?php
2
3
namespace EventEspresso\core\services\orm\tree_traversal;
4
5
use EE_Base_Class;
6
use EE_Error;
7
use EE_Has_Many_Any_Relation;
8
use EE_Model_Relation_Base;
9
use EEM_Base;
10
use EventEspresso\core\exceptions\InvalidDataTypeException;
11
use EventEspresso\core\exceptions\InvalidInterfaceException;
12
use EventEspresso\core\services\payment_methods\forms\PayPalSettingsForm;
13
use InvalidArgumentException;
14
use ReflectionException;
15
16
/**
17
 * Class RelationNode
18
 *
19
 * Wraps a model object and one of its model's relations; stores how many related model objects exist across that
20
 * relation, and eventually createsa a ModelObjNode for each of its related model objects.
21
 *
22
 * @package     Event Espresso
23
 * @author         Mike Nelson
24
 * @since         $VID:$
25
 *
26
 */
27
class RelationNode extends BaseNode
28
{
29
    /**
30
     * @var EE_Base_Class
31
     */
32
    protected $main_model_obj;
33
34
    /**
35
     * @var int
36
     */
37
    protected $count;
38
39
    /**
40
     * @var EEM_Base
41
     */
42
    protected $related_model;
43
44
    /**
45
     * @var ModelObjNode[]
46
     */
47
    protected $model_obj_nodes;
48
49
    public function __construct($main_model_obj, $related_model)
50
    {
51
        $this->main_model_obj = $main_model_obj;
52
        $this->related_model = $related_model;
53
        $this->model_obj_nodes = [];
54
    }
55
56
57
    /**
58
     * Here is where most of the work happens. We've counted how many related model objects exist, here we identify
59
     * them (ie, learn their IDs). But its recursive, so we'll also find their related dependent model objects etc.
60
     * @since $VID:$
61
     * @param int $model_objects_to_identify
62
     * @return int
63
     * @throws EE_Error
64
     * @throws InvalidArgumentException
65
     * @throws InvalidDataTypeException
66
     * @throws InvalidInterfaceException
67
     * @throws ReflectionException
68
     */
69
    protected function work($model_objects_to_identify)
70
    {
71
        $num_identified = $this->visitAlreadyDiscoveredNodes($this->model_obj_nodes, $model_objects_to_identify);
72
        if ($num_identified < $model_objects_to_identify) {
73
            $related_model_objs = $this->related_model->get_all(
74
                [
75
                    $this->whereQueryParams(),
76
                    'limit' => [
77
                        count($this->model_obj_nodes),
78
                        $model_objects_to_identify
79
                    ]
80
                ]
81
            );
82
            $new_item_nodes = [];
83
84
            // Add entity nodes for each of the model objects we fetched.
85
            foreach ($related_model_objs as $related_model_obj) {
86
                $entity_node = new ModelObjNode($related_model_obj);
87
                $this->model_obj_nodes[ $related_model_obj->ID() ] = $entity_node;
88
                $new_item_nodes[ $related_model_obj->ID() ] = $entity_node;
89
            }
90
            $num_identified += count($new_item_nodes);
91
            if ($num_identified < $model_objects_to_identify) {
92
                // And lastly do the work.
93
                $num_identified += $this->visitAlreadyDiscoveredNodes(
94
                    $new_item_nodes,
95
                    $model_objects_to_identify - $num_identified
96
                );
97
            }
98
        }
99
100 View Code Duplication
        if (count($this->model_obj_nodes) >= $this->count && $this->allChildrenComplete()) {
101
            $this->complete = true;
102
        }
103
        return $num_identified;
104
    }
105
106
    /**
107
     * Checks if all the identified child nodes are complete or not.
108
     * @since $VID:$
109
     * @return bool
110
     */
111
    protected function allChildrenComplete()
112
    {
113
        foreach ($this->model_obj_nodes as $model_obj_node) {
114
            if (! $model_obj_node->isComplete()) {
115
                return false;
116
            }
117
        }
118
        return true;
119
    }
120
121
    /**
122
     * Visits the provided nodes and keeps track of how much work was done, making sure to not go over budget.
123
     * @since $VID:$
124
     * @param ModelObjNode[] $model_obj_nodes
125
     * @param $work_budget
126
     * @return int
127
     */
128
    protected function visitAlreadyDiscoveredNodes($model_obj_nodes, $work_budget)
129
    {
130
        $work_done = 0;
131
        if (! $model_obj_nodes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $model_obj_nodes of type EventEspresso\core\servi...raversal\ModelObjNode[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
132
            return 0;
133
        }
134
        foreach ($model_obj_nodes as $model_obj_node) {
135
            if ($work_done >= $work_budget) {
136
                break;
137
            }
138
            $work_done += $model_obj_node->visit($work_budget - $work_done);
139
        }
140
        return $work_done;
141
    }
142
143
    /**
144
     * Whether this item has already been initialized
145
     */
146
    protected function isDiscovered()
147
    {
148
        return $this->count !== null;
149
    }
150
151
    /**
152
     * @since $VID:$
153
     * @return boolean
154
     */
155
    public function isComplete()
156
    {
157
        if ($this->complete === null) {
158 View Code Duplication
            if (count($this->model_obj_nodes) === $this->count) {
159
                $this->complete = true;
160
            } else {
161
                $this->complete = false;
162
            }
163
        }
164
        return $this->complete;
165
    }
166
167
    /**
168
     * Discovers how many related model objects exist.
169
     * @since $VID:$
170
     * @return mixed|void
171
     * @throws EE_Error
172
     * @throws InvalidArgumentException
173
     * @throws InvalidDataTypeException
174
     * @throws InvalidInterfaceException
175
     * @throws ReflectionException
176
     */
177
    protected function discover()
178
    {
179
        $this->count = $this->related_model->count([$this->whereQueryParams()]);
180
    }
181
182
    /**
183
     * @since $VID:$
184
     * @return array
185
     * @throws EE_Error
186
     * @throws InvalidDataTypeException
187
     * @throws InvalidInterfaceException
188
     * @throws InvalidArgumentException
189
     * @throws ReflectionException
190
     */
191
    protected function whereQueryParams()
192
    {
193
        $where_params =  [
194
            $this->related_model->get_foreign_key_to(
195
                $this->main_model_obj->get_model()->get_this_model_name()
196
            )->get_name() => $this->main_model_obj->ID()
197
        ];
198
        try {
199
            $relation_settings = $this->main_model_obj->get_model()->related_settings_for($this->related_model->get_this_model_name());
200
        } catch (EE_Error $e) {
201
            // This will happen for has-and-belongs-to-many relations, when this node's related model is that join table
202
            // which hasn't been explicitly declared in the main model object's model's relations.
203
            $relation_settings = null;
204
        }
205
        if ($relation_settings instanceof EE_Has_Many_Any_Relation) {
206
            $where_params[ $this->related_model->get_field_containing_related_model_name()->get_name() ] = $this->main_model_obj->get_model()->get_this_model_name();
207
        }
208
        return $where_params;
209
    }
210
    /**
211
     * @since $VID:$
212
     * @return array
213
     */
214
    public function toArray()
215
    {
216
        $tree = [
217
            'count' => $this->count,
218
            'complete' => $this->isComplete(),
219
            'objs' => []
220
        ];
221
        foreach ($this->model_obj_nodes as $id => $model_obj_node) {
222
            $tree['objs'][ $id ] = $model_obj_node->toArray();
223
        }
224
        return $tree;
225
    }
226
227
    /**
228
     * Gets the IDs of all the model objects to delete; indexed first by model object name.
229
     * @since $VID:$
230
     * @return array
231
     */
232
    public function getIds()
233
    {
234
        if (empty($this->model_obj_nodes)) {
235
            return [];
236
        }
237
        $ids = [
238
            $this->related_model->get_this_model_name() => array_combine(
239
                array_keys($this->model_obj_nodes),
240
                array_keys($this->model_obj_nodes)
241
            )
242
        ];
243
        foreach ($this->model_obj_nodes as $model_obj_node) {
244
            $ids = array_replace_recursive($ids, $model_obj_node->getIds());
245
        }
246
        return $ids;
247
    }
248
}
249
// End of file RelationNode.php
250
// Location: EventEspresso\core\services\orm\tree_traversal/RelationNode.php
251