1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
if (!defined('EVENT_ESPRESSO_VERSION')) |
4
|
|
|
exit('No direct script access allowed'); |
5
|
|
|
|
6
|
|
|
/** |
7
|
|
|
* |
8
|
|
|
* EEME_Base |
9
|
|
|
* For magically adding fields, relations, and functions onto existing models. |
10
|
|
|
* example child class: adds a class called EEME_Sample_Attendee which adds an extra table for |
11
|
|
|
* meta info that we want to use for frequent querying (otherwise we could just use the extra meta features), |
12
|
|
|
* and adds a field named 'ATT_foobar' on the Attendee model, |
13
|
|
|
* which is actually a foreign key to transactions, and |
14
|
|
|
* a relation to transactions, and a function called new_func() onto EEM_Attendee which |
15
|
|
|
* gets all attendees which have a direct relation to the specified transaction. |
16
|
|
|
* For example, |
17
|
|
|
* |
18
|
|
|
* class EEME_Sample_Attendee extends EEME_Base{ |
19
|
|
|
function __construct() { |
20
|
|
|
$this->_model_name_extended = 'Attendee'; |
21
|
|
|
$this->_extra_tables = array( |
22
|
|
|
'Mock_Attendee_Meta' => new EE_Secondary_Table('esp_mock_attendee_meta', 'MATTM_ID', 'ATT_ID' ) |
23
|
|
|
); |
24
|
|
|
$this->_extra_fields = array('Mock_Attendee_Meta'=>array( |
25
|
|
|
'MATTM_ID'=> new EE_DB_Only_Int_Field('MATTM_ID', __('Mock Attendee Meta Row ID','event_espresso'), false), |
26
|
|
|
'MATT_ID_fk'=>new EE_DB_Only_Int_Field('ATT_ID', __("Foreign Key to Attendee in Post Table", "event_espresso"), false), |
27
|
|
|
'ATT_foobar'=>new EE_Foreign_Key_Int_Field('ATT_foobar', __("Foobar", 'event_espresso'), true,0,'Transaction'))); |
28
|
|
|
$this->_extra_relations = array('Transaction'=>new EE_Belongs_To_Relation()); |
29
|
|
|
parent::__construct(); |
30
|
|
|
} |
31
|
|
|
function ext_new_func($arg1){ |
32
|
|
|
return $this->_->get_all(array(array('Transaction.TXN_ID'=>$arg1))); |
33
|
|
|
} |
34
|
|
|
} |
35
|
|
|
* |
36
|
|
|
* example usage: early you need to simply construct this extension, and it will automatically |
37
|
|
|
* add any of its needed hooks. Like so: new EEME_Sample_Attendee(); |
38
|
|
|
* then you can use that field, relation, and function on the EEM_Attendee singleton. Eg. |
39
|
|
|
* $attendees_directly_related_to_txn_1 = EEM_Attendee::instance()->new_func(1); |
40
|
|
|
* |
41
|
|
|
* @package Event Espresso |
42
|
|
|
* @subpackage |
43
|
|
|
* @author Mike Nelson |
44
|
|
|
* |
45
|
|
|
*/ |
46
|
|
|
abstract class EEME_Base { |
47
|
|
|
|
48
|
|
|
const extending_method_prefix = 'ext_'; |
49
|
|
|
const dynamic_callback_method_prefix = 'dynamic_callback_method_'; |
50
|
|
|
|
51
|
|
|
protected $_extra_tables = array(); |
52
|
|
|
protected $_extra_fields = array(); |
53
|
|
|
protected $_extra_relations = array(); |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* The model name that is extended (not classname) |
57
|
|
|
* @var string |
58
|
|
|
*/ |
59
|
|
|
protected $_model_name_extended = NULL; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* The model this extends |
63
|
|
|
* @var EEM_Base |
64
|
|
|
*/ |
65
|
|
|
protected $_ = NULL; |
66
|
|
|
|
67
|
|
|
|
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* @throws \EE_Error |
71
|
|
|
*/ |
72
|
|
|
public function __construct(){ |
73
|
|
|
if( ! $this->_model_name_extended){ |
74
|
|
|
throw new EE_Error( |
75
|
|
|
__( "When declaring a model extension, you must define its _model_name_extended property. It should be a model name like 'Attendee' or 'Event'", |
76
|
|
|
"event_espresso" ) |
77
|
|
|
); |
78
|
|
|
} |
79
|
|
|
$construct_end_action = 'AHEE__EEM_'.$this->_model_name_extended.'__construct__end'; |
80
|
|
|
if ( did_action( $construct_end_action )) { |
81
|
|
|
throw new EE_Error( |
82
|
|
|
sprintf( |
83
|
|
|
__( "Hooked in model extension '%s' too late! The model %s has already been used! We know because the action %s has been fired", "event_espresso"), |
84
|
|
|
get_class($this), |
85
|
|
|
$this->_model_name_extended, |
86
|
|
|
$construct_end_action |
87
|
|
|
) |
88
|
|
|
); |
89
|
|
|
} |
90
|
|
|
add_filter('FHEE__EEM_'.$this->_model_name_extended.'__construct__tables',array($this,'add_extra_tables_on_filter')); |
91
|
|
|
add_filter('FHEE__EEM_'.$this->_model_name_extended.'__construct__fields',array($this,'add_extra_fields_on_filter')); |
92
|
|
|
add_filter('FHEE__EEM_'.$this->_model_name_extended.'__construct__model_relations',array($this,'add_extra_relations_on_filter')); |
93
|
|
|
$this->_register_extending_methods(); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
|
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @param array $existing_tables |
100
|
|
|
* @return array |
101
|
|
|
*/ |
102
|
|
|
public function add_extra_tables_on_filter( $existing_tables ){ |
103
|
|
|
return array_merge( (array)$existing_tables, $this->_extra_tables ); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
|
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* @param array $existing_fields |
110
|
|
|
* @return array |
111
|
|
|
*/ |
112
|
|
|
public function add_extra_fields_on_filter( $existing_fields ){ |
113
|
|
|
if( $this->_extra_fields){ |
|
|
|
|
114
|
|
|
foreach($this->_extra_fields as $table_alias => $fields){ |
115
|
|
|
if( ! isset( $existing_fields[ $table_alias ] ) ){ |
116
|
|
|
$existing_fields[ $table_alias ] = array(); |
117
|
|
|
} |
118
|
|
|
$existing_fields[$table_alias] = array_merge( |
119
|
|
|
(array)$existing_fields[$table_alias], |
120
|
|
|
$this->_extra_fields[$table_alias] |
121
|
|
|
); |
122
|
|
|
|
123
|
|
|
} |
124
|
|
|
} |
125
|
|
|
return $existing_fields; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
|
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* @param array $existing_relations |
132
|
|
|
* @return array |
133
|
|
|
*/ |
134
|
|
|
public function add_extra_relations_on_filter( $existing_relations ){ |
135
|
|
|
return array_merge((array)$existing_relations,$this->_extra_relations); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
|
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* scans the child of EEME_Base for functions starting with ext_, and magically makes them functions on the |
142
|
|
|
* model extended. (Internally uses filters, and the __call magic method) |
143
|
|
|
*/ |
144
|
|
View Code Duplication |
protected function _register_extending_methods(){ |
|
|
|
|
145
|
|
|
$all_methods = get_class_methods(get_class($this)); |
146
|
|
|
foreach($all_methods as $method_name){ |
147
|
|
|
if(strpos($method_name, self::extending_method_prefix) === 0){ |
148
|
|
|
$method_name_on_model = str_replace(self::extending_method_prefix, '', $method_name); |
149
|
|
|
$callback_name = "FHEE__EEM_{$this->_model_name_extended}__$method_name_on_model"; |
150
|
|
|
add_filter($callback_name,array($this,self::dynamic_callback_method_prefix.$method_name_on_model),10,10); |
151
|
|
|
} |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* scans the child of EEME_Base for functions starting with ext_, and magically REMOVES them as functions on the |
157
|
|
|
* model extended. (Internally uses filters, and the __call magic method) |
158
|
|
|
*/ |
159
|
|
|
public function deregister(){ |
160
|
|
|
remove_filter('FHEE__EEM_'.$this->_model_name_extended.'__construct__tables',array($this,'add_extra_tables_on_filter')); |
161
|
|
|
remove_filter('FHEE__EEM_'.$this->_model_name_extended.'__construct__fields',array($this,'add_extra_fields_on_filter')); |
162
|
|
|
remove_filter('FHEE__EEM_'.$this->_model_name_extended.'__construct__model_relations',array($this,'add_extra_relations_on_filter')); |
163
|
|
|
$all_methods = get_class_methods(get_class($this)); |
164
|
|
|
foreach($all_methods as $method_name){ |
165
|
|
|
if(strpos($method_name, self::extending_method_prefix) === 0){ |
166
|
|
|
$method_name_on_model = str_replace(self::extending_method_prefix, '', $method_name); |
167
|
|
|
$callback_name = "FHEE__EEM_{$this->_model_name_extended}__$method_name_on_model"; |
168
|
|
|
remove_filter($callback_name,array($this,self::dynamic_callback_method_prefix.$method_name_on_model),10); |
169
|
|
|
} |
170
|
|
|
} |
171
|
|
|
/** @var EEM_Base $model_to_reset */ |
172
|
|
|
$model_to_reset = 'EEM_' . $this->_model_name_extended; |
173
|
|
|
if ( class_exists( $model_to_reset ) ) { |
174
|
|
|
$model_to_reset::reset(); |
175
|
|
|
} |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
|
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* @param string $callback_method_name |
182
|
|
|
* @param array $args |
183
|
|
|
* @return mixed |
184
|
|
|
* @throws EE_Error |
185
|
|
|
*/ |
186
|
|
|
public function __call( $callback_method_name, $args){ |
187
|
|
|
if(strpos($callback_method_name, self::dynamic_callback_method_prefix) === 0){ |
188
|
|
|
//it's a dynamic callback for a method name |
189
|
|
|
$method_called_on_model = str_replace(self::dynamic_callback_method_prefix, '', $callback_method_name); |
190
|
|
|
list( $original_return_val, $model_called, $args_provided_to_method_on_model ) = (array) $args; |
|
|
|
|
191
|
|
|
$this->_ = $model_called; |
192
|
|
|
$extending_method = self::extending_method_prefix.$method_called_on_model; |
193
|
|
|
if(method_exists($this, $extending_method)){ |
194
|
|
|
return call_user_func_array(array($this,$extending_method), $args_provided_to_method_on_model); |
195
|
|
|
}else{ |
196
|
|
|
throw new EE_Error( |
197
|
|
|
sprintf( |
198
|
|
|
__("An odd error occurred. Model '%s' had a method called on it that it didn't recognize. So it passed it onto the model extension '%s' (because it had a function named '%s' which should be able to handle it), but the function '%s' doesnt exist!)", "event_espresso"), |
199
|
|
|
$this->_model_name_extended, |
200
|
|
|
get_class($this), |
201
|
|
|
$extending_method,$extending_method |
202
|
|
|
) |
203
|
|
|
); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
}else{ |
207
|
|
|
throw new EE_Error( |
208
|
|
|
sprintf( |
209
|
|
|
__("There is no method named '%s' on '%s'", "event_espresso"), |
210
|
|
|
$callback_method_name, |
211
|
|
|
get_class($this) |
212
|
|
|
) |
213
|
|
|
); |
214
|
|
|
} |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
} |
218
|
|
|
// End of file EEME_Base.model.php |
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.