Completed
Branch FET/recalculate-line-items (4e6c10)
by
unknown
62:03 queued 53:01
created

EE_HABTM_Relation::hasNonKeyFields()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Class EE_HABTM_Relation
5
 *
6
 * @package       Event Espresso
7
 * @subpackage    core
8
 * @author        Michael Nelson
9
 */
10
class EE_HABTM_Relation extends EE_Model_Relation_Base
11
{
12
    /**
13
     * Model which defines the relation between two other models. Eg, the EE_Event_Question_Group model,
14
     * which joins EE_Event and EE_Question_Group
15
     *
16
     * @var EEM_Base
17
     */
18
    protected $_joining_model_name;
19
20
    protected $_model_relation_chain_to_join_model;
21
22
23
    /**
24
     * Object representing the relationship between two models. HasAndBelongsToMany relations always use a join-table
25
     * (and an ee joining-model.) This knows how to join the models,
26
     * get related models across the relation, and add-and-remove the relationships.
27
     *
28
     * @param bool    $joining_model_name
29
     * @param boolean $block_deletes                 for this type of relation, we block by default for now. if there
30
     *                                               are related models across this relation, block (prevent and add an
31
     *                                               error) the deletion of this model
32
     * @param string  $blocking_delete_error_message a customized error message on blocking deletes instead of the
33
     *                                               default
34
     */
35
    public function __construct($joining_model_name, $block_deletes = true, $blocking_delete_error_message = '')
36
    {
37
        $this->_joining_model_name = $joining_model_name;
38
        parent::__construct($block_deletes, $blocking_delete_error_message);
39
    }
40
41
    /**
42
     * Gets the joining model's object
43
     *
44
     * @return EEM_Base
45
     */
46
    public function get_join_model()
47
    {
48
        return $this->_get_model($this->_joining_model_name);
49
    }
50
51
52
    /**
53
     * Gets the SQL string for joining the main model's table containing the pk to the join table. Eg "LEFT JOIN
54
     * real_join_table AS join_table_alias ON this_table_alias.pk = join_table_alias.fk_to_this_table"
55
     *
56
     * @param string $model_relation_chain like 'Event.Event_Venue.Venue'
57
     * @return string of SQL
58
     * @throws \EE_Error
59
     */
60
    public function get_join_to_intermediate_model_statement($model_relation_chain)
61
    {
62
        // create sql like
63
        // LEFT JOIN join_table AS join_table_alias ON this_table_alias.this_table_pk = join_table_alias.join_table_fk_to_this
64
        // LEFT JOIN other_table AS other_table_alias ON join_table_alias.join_table_fk_to_other = other_table_alias.other_table_pk
65
        // remember the model relation chain to the JOIN model, because we'll
66
        // need it for get_join_statement()
67
        $this->_model_relation_chain_to_join_model = $model_relation_chain;
68
        $this_table_pk_field                       = $this->get_this_model()->get_primary_key_field();// get_foreign_key_to($this->get_other_model()->get_this_model_name());
69
        $join_table_fk_field_to_this_table         = $this->get_join_model()->get_foreign_key_to($this->get_this_model()->get_this_model_name());
70
        $this_table_alias                          = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
71
            $model_relation_chain,
72
            $this->get_this_model()->get_this_model_name()
73
        ) . $this_table_pk_field->get_table_alias();
74
75
        $join_table_alias = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
76
            $model_relation_chain,
77
            $this->get_join_model()->get_this_model_name()
78
        ) . $join_table_fk_field_to_this_table->get_table_alias();
79
        $join_table       = $this->get_join_model()->get_table_for_alias($join_table_alias);
80
        // phew! ok, we have all the info we need, now we can create the SQL join string
81
        $SQL = $this->_left_join(
82
            $join_table,
83
            $join_table_alias,
84
            $join_table_fk_field_to_this_table->get_table_column(),
85
            $this_table_alias,
86
            $this_table_pk_field->get_table_column()
87
        ) . $this->get_join_model()->_construct_internal_join_to_table_with_alias($join_table_alias);
88
89
        return $SQL;
90
    }
91
92
93
    /**
94
     * Gets the SQL string for joining the join table to the other model's pk's table. Eg "LEFT JOIN real_other_table
95
     * AS other_table_alias ON join_table_alias.fk_to_other_table = other_table_alias.pk" If you want to join between
96
     * modelA -> joinModelAB -> modelB (eg, Event -> Event_Question_Group -> Question_Group), you should prepend the
97
     * result of this function with results from get_join_to_intermediate_model_statement(), so that you join first to
98
     * the intermediate join table, and then to the other model's pk's table
99
     *
100
     * @param string $model_relation_chain like 'Event.Event_Venue.Venue'
101
     * @return string of SQL
102
     * @throws \EE_Error
103
     */
104
    public function get_join_statement($model_relation_chain)
105
    {
106
        if ($this->_model_relation_chain_to_join_model === null) {
107
            throw new EE_Error(sprintf(__(
108
                'When using EE_HABTM_Relation to create a join, you must call get_join_to_intermediate_model_statement BEFORE get_join_statement',
109
                'event_espresso'
110
            )));
111
        }
112
        $join_table_fk_field_to_this_table  = $this->get_join_model()->get_foreign_key_to($this->get_this_model()->get_this_model_name());
113
        $join_table_alias                   = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
114
            $this->_model_relation_chain_to_join_model,
115
            $this->get_join_model()->get_this_model_name()
116
        ) . $join_table_fk_field_to_this_table->get_table_alias();
117
        $other_table_pk_field               = $this->get_other_model()->get_primary_key_field();
118
        $join_table_fk_field_to_other_table = $this->get_join_model()->get_foreign_key_to($this->get_other_model()->get_this_model_name());
119
        $other_table_alias                  = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
120
            $model_relation_chain,
121
            $this->get_other_model()->get_this_model_name()
122
        ) . $other_table_pk_field->get_table_alias();
123
        $other_table                        = $this->get_other_model()->get_table_for_alias($other_table_alias);
124
125
        $SQL = $this->_left_join(
126
            $other_table,
127
            $other_table_alias,
128
            $other_table_pk_field->get_table_column(),
129
            $join_table_alias,
130
            $join_table_fk_field_to_other_table->get_table_column()
131
        ) . $this->get_other_model()->_construct_internal_join_to_table_with_alias($other_table_alias);
132
        return $SQL;
133
    }
134
135
136
    /**
137
     * Ensures there is an entry in the join table between these two models. Feel free to do this manually if you like.
138
     * If the join table has additional columns (eg, the Event_Question_Group table has a is_primary column), then
139
     * you'll want to directly use the EEM_Event_Question_Group model to add the entry to the table and set those extra
140
     * columns' values
141
     *
142
     * @param EE_Base_Class|int $this_obj_or_id
143
     * @param EE_Base_Class|int $other_obj_or_id
144
     * @param array             $extra_join_model_fields_n_values col=>val pairs that are used as extra conditions for
145
     *                                                            checking existing values and for setting new rows if
146
     *                                                            no exact matches.
147
     * @return EE_Base_Class
148
     * @throws \EE_Error
149
     */
150
    public function add_relation_to($this_obj_or_id, $other_obj_or_id, $extra_join_model_fields_n_values = array())
151
    {
152
        $this_model_obj  = $this->get_this_model()->ensure_is_obj($this_obj_or_id, true);
153
        $other_model_obj = $this->get_other_model()->ensure_is_obj($other_obj_or_id, true);
154
        // check if such a relationship already exists
155
        $join_model_fk_to_this_model  = $this->get_join_model()->get_foreign_key_to($this->get_this_model()->get_this_model_name());
156
        $join_model_fk_to_other_model = $this->get_join_model()->get_foreign_key_to($this->get_other_model()->get_this_model_name());
157
158
        $foreign_keys = $all_fields = array(
159
            $join_model_fk_to_this_model->get_name()  => $this_model_obj->ID(),
160
            $join_model_fk_to_other_model->get_name() => $other_model_obj->ID(),
161
        );
162
163
        // if $where_query exists lets add them to the query_params.
164 View Code Duplication
        if (! empty($extra_join_model_fields_n_values)) {
165
            // make sure we strip any of the join model names from the $where_query cause we don't need that in here (why? because client code may have used the same conditionals for get_all_related which DOES need the join model name)
166
            // make sure we strip THIS models name from the query param
167
            $parsed_query = array();
168
            foreach ($extra_join_model_fields_n_values as $query_param => $val) {
169
                $query_param                = str_replace(
170
                    $this->get_join_model()->get_this_model_name() . ".",
171
                    "",
172
                    $query_param
173
                );
174
                $parsed_query[ $query_param ] = $val;
175
            }
176
            $all_fields = array_merge($foreign_keys, $parsed_query);
177
        }
178
179
        $existing_entry_in_join_table = $this->get_join_model()->get_one(array($foreign_keys));
180
        // If there is already an entry in the join table, indicating a relationship, update it instead of adding a
181
        // new row.
182
        // Again, if you want more sophisticated logic or insertions (handling more columns than just 2 foreign keys to
183
        // the other tables) use the joining model directly!
184
        if (! $existing_entry_in_join_table) {
185
            $this->get_join_model()->insert($all_fields);
186
        } else {
187
            $this->get_join_model()->update(
188
                $all_fields,
189
                array($foreign_keys)
190
            );
191
        }
192
        return $other_model_obj;
193
    }
194
195
196
    /**
197
     * Deletes any rows in the join table that have foreign keys matching the other model objects specified
198
     *
199
     * @param EE_Base_Class|int $this_obj_or_id
200
     * @param EE_Base_Class|int $other_obj_or_id
201
     * @param array             $where_query col=>val pairs that are used as extra conditions for checking existing
202
     *                                       values and for removing existing rows if exact matches exist.
203
     * @return EE_Base_Class
204
     * @throws \EE_Error
205
     */
206
    public function remove_relation_to($this_obj_or_id, $other_obj_or_id, $where_query = array())
207
    {
208
        $this_model_obj  = $this->get_this_model()->ensure_is_obj($this_obj_or_id, true);
209
        $other_model_obj = $this->get_other_model()->ensure_is_obj($other_obj_or_id, true);
210
        // check if such a relationship already exists
211
        $join_model_fk_to_this_model  = $this->get_join_model()->get_foreign_key_to($this->get_this_model()->get_this_model_name());
212
        $join_model_fk_to_other_model = $this->get_join_model()->get_foreign_key_to($this->get_other_model()->get_this_model_name());
213
214
        $cols_n_values = array(
215
            $join_model_fk_to_this_model->get_name()  => $this_model_obj->ID(),
216
            $join_model_fk_to_other_model->get_name() => $other_model_obj->ID(),
217
        );
218
219
        // if $where_query exists lets add them to the query_params.
220 View Code Duplication
        if (! empty($where_query)) {
221
            // make sure we strip any of the join model names from the $where_query cause we don't need that in here (why? because client code may have used the same conditionals for get_all_related which DOES need the join model name)
222
            // make sure we strip THIS models name from the query param
223
            $parsed_query = array();
224
            foreach ($where_query as $query_param => $val) {
225
                $query_param                = str_replace(
226
                    $this->get_join_model()->get_this_model_name() . ".",
227
                    "",
228
                    $query_param
229
                );
230
                $parsed_query[ $query_param ] = $val;
231
            }
232
            $cols_n_values = array_merge($cols_n_values, $parsed_query);
233
        }
234
235
        $this->get_join_model()->delete(array($cols_n_values));
236
        return $other_model_obj;
237
    }
238
239
    /**
240
     * Gets all the non-key fields (ie, not the primary key and not foreign keys) on the join model.
241
     * @since $VID:$
242
     * @return EE_Model_Field_Base[]
243
     * @throws EE_Error
244
     */
245
    public function getNonKeyFields()
246
    {
247
        // all fields besides the primary key and two foreign keys should be parameters
248
        $join_model = $this->get_join_model();
249
        $standard_fields = array();
250
        if ($join_model->has_primary_key_field()) {
251
            $standard_fields[] = $join_model->primary_key_name();
252
        }
253
        if ($this->get_this_model()->has_primary_key_field()) {
254
            $standard_fields[] = $this->get_this_model()->primary_key_name();
255
        }
256
        if ($this->get_other_model()->has_primary_key_field()) {
257
            $standard_fields[] = $this->get_other_model()->primary_key_name();
258
        }
259
        return array_diff_key(
260
            $join_model->field_settings(),
261
            array_flip($standard_fields)
262
        );
263
    }
264
265
    /**
266
     * Returns true if the join model has non-key fields (ie, fields that aren't the primary key or foreign keys.)
267
     * @since $VID:$
268
     * @return boolean
269
     */
270
    public function hasNonKeyFields()
271
    {
272
        return count($this->get_join_model()->field_settings()) > 3;
273
    }
274
}
275