1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Joins any two models together via a has-and-belongs-to-many relation, using |
4
|
|
|
* the esp_extra_join table. |
5
|
|
|
*/ |
6
|
|
|
class EE_HABTM_Any_Relation extends EE_HABTM_Relation{ |
7
|
|
|
/** |
8
|
|
|
* |
9
|
|
|
* @var string |
10
|
|
|
*/ |
11
|
|
|
protected $_alphabetically_first_model_name = null; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* Object representing the relationship between two models. HasAndBelongsToMany relations always use a join-table |
15
|
|
|
* (and an ee joining-model.) This knows how to join the models, |
16
|
|
|
* get related models across the relation, and add-and-remove the relationships. |
17
|
|
|
* @param boolean $block_deletes for this type of relation, we block by default for now. if there are related models across this relation, block (prevent and add an error) the deletion of this model |
18
|
|
|
* @param type $blocking_delete_error_message a customized error message on blocking deletes instead of the default |
19
|
|
|
*/ |
20
|
|
|
function __construct( $block_deletes = true, $blocking_delete_error_message =''){ |
21
|
|
|
parent::__construct( 'Extra_Join', $block_deletes, $blocking_delete_error_message); |
22
|
|
|
} |
23
|
|
|
function _construct_finalize_set_models($this_model_name, $other_model_name){ |
24
|
|
|
if( $this_model_name < $other_model_name ) { |
25
|
|
|
$this->_alphabetically_first_model_name = $this_model_name; |
26
|
|
|
} else{ |
27
|
|
|
$this->_alphabetically_first_model_name = $other_model_name; |
28
|
|
|
} |
29
|
|
|
return parent::_construct_finalize_set_models( $this_model_name, $other_model_name ); |
30
|
|
|
} |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* |
34
|
|
|
* @param string $model_name |
35
|
|
|
* @param string $comparison_model_name |
|
|
|
|
36
|
|
|
* @param string $id_or_name_field should be the string 'ID' or 'name' only |
37
|
|
|
* @return EE_Model_Field_Base |
38
|
|
|
*/ |
39
|
|
|
function get_join_table_fk_field_to( $model_name, $id_or_name_field ) { |
40
|
|
|
$order = null; |
|
|
|
|
41
|
|
|
if( $model_name === $this->_alphabetically_first_model_name ) { |
42
|
|
|
$order = 'first'; |
43
|
|
|
} else { |
44
|
|
|
$order = 'second'; |
45
|
|
|
} |
46
|
|
|
return $this->get_join_model()->field_settings_for( 'EXJ_' . $order . '_model_' . $id_or_name_field ); |
|
|
|
|
47
|
|
|
} |
48
|
|
|
/** |
49
|
|
|
* Gets the SQL string for joining the main model's table containing the pk to the join table. Eg "LEFT JOIN real_join_table AS join_table_alias ON this_table_alias.pk = join_table_alias.fk_to_this_table" |
50
|
|
|
* @param string $model_relation_chain like 'Event.Event_Venue.Venue' |
51
|
|
|
* @return string of SQL |
52
|
|
|
*/ |
53
|
|
|
function get_join_to_intermediate_model_statement($model_relation_chain){ |
54
|
|
|
//create sql like |
55
|
|
|
//LEFT JOIN join_table AS join_table_alias ON this_table_alias.this_table_pk = join_table_alias.join_table_fk_to_this |
56
|
|
|
//LEFT JOIN other_table AS other_table_alias ON join_table_alias.join_table_fk_to_other = other_table_alias.other_table_pk |
57
|
|
|
//remember the model relation chain to the JOIN model, because we'll |
58
|
|
|
//need it for get_join_statement() |
59
|
|
|
$this->_model_relation_chain_to_join_model = $model_relation_chain; |
60
|
|
|
$this_table_pk_field = $this->get_this_model()->get_primary_key_field(); |
|
|
|
|
61
|
|
|
$join_table_fk_field_to_this_table = $this->get_join_table_fk_field_to( |
62
|
|
|
$this->get_this_model()->get_this_model_name(), |
|
|
|
|
63
|
|
|
'ID' ); |
64
|
|
|
$field_with_model_name = $this->get_join_table_fk_field_to( |
65
|
|
|
$this->get_this_model()->get_this_model_name(), |
|
|
|
|
66
|
|
|
'name' ); |
67
|
|
|
$this_table_alias = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix($model_relation_chain, $this->get_this_model()->get_this_model_name()) . $this_table_pk_field->get_table_alias(); |
|
|
|
|
68
|
|
|
$join_table_alias = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix($model_relation_chain, $this->get_join_model()->get_this_model_name()) . $join_table_fk_field_to_this_table->get_table_alias(); |
|
|
|
|
69
|
|
|
$join_table = $this->get_join_model()->get_table_for_alias($join_table_alias); |
|
|
|
|
70
|
|
|
//phew! ok, we have all the info we need, now we can create the SQL join string |
71
|
|
|
$SQL = $this->_left_join( |
72
|
|
|
$join_table, |
73
|
|
|
$join_table_alias, |
74
|
|
|
$join_table_fk_field_to_this_table->get_table_column(), |
75
|
|
|
$this_table_alias, |
76
|
|
|
$this_table_pk_field->get_table_column(), |
77
|
|
|
$field_with_model_name->get_qualified_column()."='".$this->get_this_model()->get_this_model_name()."'" ) . |
|
|
|
|
78
|
|
|
$this->get_join_model()->_construct_internal_join_to_table_with_alias($join_table_alias); |
|
|
|
|
79
|
|
|
|
80
|
|
|
return $SQL; |
81
|
|
|
} |
82
|
|
|
/** |
83
|
|
|
* Gets the SQL string for joining the join table to the other model's pk's table. Eg "LEFT JOIN real_other_table AS other_table_alias ON join_table_alias.fk_to_other_table = other_table_alias.pk" |
84
|
|
|
* If you want to join between modelA -> joinModelAB -> modelB (eg, Event -> Event_Question_Group -> Question_Group), |
85
|
|
|
* you shoudl prepend the result of this function with results from get_join_to_intermediate_model_statement(), |
86
|
|
|
* so that you join first to the intermediate join table, and then to the other model's pk's table |
87
|
|
|
* @param string $model_relation_chain like 'Event.Event_Venue.Venue' |
88
|
|
|
* @return string of SQL |
89
|
|
|
*/ |
90
|
|
|
function get_join_statement($model_relation_chain){ |
91
|
|
|
if( $this->_model_relation_chain_to_join_model === NULL ){ |
92
|
|
|
throw new EE_Error( sprintf( __( 'When using EE_HABTM_Relation to create a join, you must call get_join_to_intermediate_model_statement BEFORE get_join_statement', 'event_espresso' ))); |
93
|
|
|
} |
94
|
|
|
$join_table_fk_field_to_this_table = $this->get_join_table_fk_field_to( |
95
|
|
|
$this->get_this_model()->get_this_model_name(), |
|
|
|
|
96
|
|
|
'ID' ); |
97
|
|
|
$join_table_fk_field_to_other_table = $this->get_join_table_fk_field_to( |
98
|
|
|
$this->get_other_model()->get_this_model_name(), |
|
|
|
|
99
|
|
|
'ID' ); |
100
|
|
|
$field_with_other_model_name = $this->get_join_table_fk_field_to( |
101
|
|
|
$this->get_other_model()->get_this_model_name(), |
|
|
|
|
102
|
|
|
'name' ); |
103
|
|
|
|
104
|
|
|
$join_table_alias = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix($this->_model_relation_chain_to_join_model, $this->get_join_model()->get_this_model_name()) . $join_table_fk_field_to_this_table->get_table_alias(); |
|
|
|
|
105
|
|
|
|
106
|
|
|
$other_table_pk_field = $this->get_other_model()->get_primary_key_field(); |
|
|
|
|
107
|
|
|
$other_table_alias = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix($model_relation_chain, $this->get_other_model()->get_this_model_name()) . $other_table_pk_field->get_table_alias(); |
|
|
|
|
108
|
|
|
$other_table = $this->get_other_model()->get_table_for_alias($other_table_alias); |
|
|
|
|
109
|
|
|
|
110
|
|
|
$SQL = $this->_left_join( |
111
|
|
|
$other_table, |
112
|
|
|
$other_table_alias, |
113
|
|
|
$other_table_pk_field->get_table_column(), |
114
|
|
|
$join_table_alias, |
115
|
|
|
$join_table_fk_field_to_other_table->get_table_column(), |
116
|
|
|
$field_with_other_model_name->get_qualified_column()."='".$this->get_other_model()->get_this_model_name()."'" |
|
|
|
|
117
|
|
|
) . |
118
|
|
|
$this->get_other_model()->_construct_internal_join_to_table_with_alias($other_table_alias); |
|
|
|
|
119
|
|
|
return $SQL; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Ensures there is an entry in the join table between these two models. Feel free to do this manually if you like. |
124
|
|
|
* @param EE_Base_Class/int $this_obj_or_id |
|
|
|
|
125
|
|
|
* @param EE_Base_Class/int $other_obj_or_id |
|
|
|
|
126
|
|
|
* @param array $extra_join_model_fields_n_values col=>val pairs that are used as extra conditions for checking existing values and for setting new rows if no exact matches. |
127
|
|
|
* @return EE_Base_Class |
128
|
|
|
*/ |
129
|
|
|
function add_relation_to($this_obj_or_id, $other_obj_or_id, $extra_join_model_fields_n_values = array() ){ |
130
|
|
|
$this_model_obj = $this->get_this_model()->ensure_is_obj($this_obj_or_id, true); |
|
|
|
|
131
|
|
|
$other_model_obj = $this->get_other_model()->ensure_is_obj($other_obj_or_id, true); |
|
|
|
|
132
|
|
|
//check if such a relationship already exists |
133
|
|
|
$join_model_fk_to_this_model = $this->get_join_table_fk_field_to( |
134
|
|
|
$this->get_this_model()->get_this_model_name(), |
|
|
|
|
135
|
|
|
'ID' ); |
136
|
|
|
$join_model_name_field_to_this_model = $this->get_join_table_fk_field_to( |
137
|
|
|
$this->get_this_model()->get_this_model_name(), |
|
|
|
|
138
|
|
|
'name' ); |
139
|
|
|
$join_model_fk_to_other_model = $this->get_join_table_fk_field_to( |
140
|
|
|
$this->get_other_model()->get_this_model_name(), |
|
|
|
|
141
|
|
|
'ID' ); |
142
|
|
|
$join_model_name_field_to_other_model = $this->get_join_table_fk_field_to( |
143
|
|
|
$this->get_other_model()->get_this_model_name(), |
|
|
|
|
144
|
|
|
'name' ); |
145
|
|
|
|
146
|
|
|
$cols_n_values = array( |
147
|
|
|
$join_model_fk_to_this_model->get_name() => $this_model_obj->ID(), |
148
|
|
|
$join_model_name_field_to_this_model->get_name() => $this_model_obj->get_model()->get_this_model_name(), |
149
|
|
|
$join_model_fk_to_other_model->get_name() => $other_model_obj->ID(), |
150
|
|
|
$join_model_name_field_to_other_model->get_name() => $other_model_obj->get_model()->get_this_model_name() ); |
151
|
|
|
|
152
|
|
|
//if $where_query exists lets add them to the query_params. |
153
|
|
View Code Duplication |
if ( !empty( $extra_join_model_fields_n_values ) ) { |
|
|
|
|
154
|
|
|
//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) |
155
|
|
|
//make sure we strip THIS models name from the query param |
156
|
|
|
foreach ( $extra_join_model_fields_n_values as $query_param => $val ) { |
157
|
|
|
$query_param = str_replace($this->get_join_model()->get_this_model_name().".","", $query_param); |
|
|
|
|
158
|
|
|
$parsed_query[$query_param] = $val; |
|
|
|
|
159
|
|
|
} |
160
|
|
|
$cols_n_values = array_merge( $cols_n_values, $parsed_query ); |
|
|
|
|
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
$query_params = array( $cols_n_values ); |
164
|
|
|
|
165
|
|
|
|
166
|
|
|
$existing_entry_in_join_table = $this->get_join_model()->get_one($query_params); |
|
|
|
|
167
|
|
|
//if there is already an entry in the join table, indicating a relationship, we're done |
168
|
|
|
//again, if you want more sophisticated logic or insertions (handling more columns than just 2 foreign keys to |
169
|
|
|
//the other tables, use the joining model directly! |
170
|
|
|
if( ! $existing_entry_in_join_table ){ |
171
|
|
|
$this->get_join_model()->insert($cols_n_values); |
|
|
|
|
172
|
|
|
} |
173
|
|
|
return $other_model_obj; |
174
|
|
|
} |
175
|
|
|
/** |
176
|
|
|
* Deletes any rows in the join table that have foreign keys matching the other model objects specified |
177
|
|
|
* @param EE_Base_Class/int $this_obj_or_id |
|
|
|
|
178
|
|
|
* @param EE_Base_Class/int $other_obj_or_id |
|
|
|
|
179
|
|
|
* * @param array $where_query col=>val pairs that are used as extra conditions for checking existing values and for removing existing rows if exact matches exist. |
180
|
|
|
* @return EE_Base_Class |
181
|
|
|
*/ |
182
|
|
|
function remove_relation_to($this_obj_or_id, $other_obj_or_id, $where_query = array() ){ |
183
|
|
|
$this_model_obj = $this->get_this_model()->ensure_is_obj($this_obj_or_id, true); |
|
|
|
|
184
|
|
|
$other_model_obj = $this->get_other_model()->ensure_is_obj($other_obj_or_id, true); |
|
|
|
|
185
|
|
|
//check if such a relationship already exists |
186
|
|
|
$join_model_fk_to_this_model = $this->get_join_table_fk_field_to( |
187
|
|
|
$this->get_this_model()->get_this_model_name(), |
|
|
|
|
188
|
|
|
'ID' ); |
189
|
|
|
$join_model_name_field_to_this_model = $this->get_join_table_fk_field_to( |
190
|
|
|
$this->get_this_model()->get_this_model_name(), |
|
|
|
|
191
|
|
|
'name' ); |
192
|
|
|
$join_model_fk_to_other_model = $this->get_join_table_fk_field_to( |
193
|
|
|
$this->get_other_model()->get_this_model_name(), |
|
|
|
|
194
|
|
|
'ID' ); |
195
|
|
|
$join_model_name_field_to_other_model = $this->get_join_table_fk_field_to( |
196
|
|
|
$this->get_other_model()->get_this_model_name(), |
|
|
|
|
197
|
|
|
'name' ); |
198
|
|
|
|
199
|
|
|
$cols_n_values = array( |
200
|
|
|
$join_model_fk_to_this_model->get_name() => $this_model_obj->ID(), |
201
|
|
|
$join_model_name_field_to_this_model->get_name() => $this_model_obj->get_model()->get_this_model_name(), |
202
|
|
|
$join_model_fk_to_other_model->get_name() => $other_model_obj->ID(), |
203
|
|
|
$join_model_name_field_to_other_model->get_name() => $other_model_obj->get_model()->get_this_model_name() ); |
204
|
|
|
|
205
|
|
|
//if $where_query exists lets add them to the query_params. |
206
|
|
View Code Duplication |
if ( !empty( $where_query ) ) { |
|
|
|
|
207
|
|
|
//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) |
208
|
|
|
//make sure we strip THIS models name from the query param |
209
|
|
|
foreach ( $where_query as $query_param => $val ) { |
210
|
|
|
$query_param = str_replace($this->get_join_model()->get_this_model_name().".","", $query_param); |
|
|
|
|
211
|
|
|
$parsed_query[$query_param] = $val; |
|
|
|
|
212
|
|
|
} |
213
|
|
|
$cols_n_values = array_merge( $cols_n_values, $parsed_query ); |
|
|
|
|
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
$existing_entry_in_join_table = $this->get_join_model()->delete( array($cols_n_values) ); |
|
|
|
|
217
|
|
|
return $other_model_obj; |
218
|
|
|
} |
219
|
|
|
} |
220
|
|
|
|
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.
Consider the following example. The parameter
$ireland
is not defined by the methodfinale(...)
.The most likely cause is that the parameter was changed, but the annotation was not.