1 : <?php
2 :
3 :
4 :
5 :
6 :
7 : class RelationsActiveRecord extends CActiveRecord {
8 :
9 : protected $_magic_attributes=array();
10 :
11 :
12 :
13 :
14 : protected $_eligible_for_delete=array();
15 :
16 :
17 :
18 :
19 : protected $_relation_updated=array();
20 :
21 :
22 :
23 :
24 :
25 :
26 :
27 :
28 :
29 :
30 : protected $_delete_commands=array ();
31 :
32 :
33 :
34 :
35 :
36 :
37 :
38 :
39 : protected $_insert_commands=array ();
40 :
41 :
42 : protected $cascade_on_delete=false;
43 :
44 :
45 :
46 :
47 :
48 :
49 :
50 :
51 :
52 :
53 :
54 :
55 :
56 :
57 :
58 :
59 : public function many_many_map () {
60 0 : return array();
61 : }
62 :
63 :
64 :
65 :
66 :
67 :
68 :
69 : public function idPresent ($check_for_id) {
70 4 : if (empty ($check_for_id))
71 4 : return false;
72 4 : $primary_key=$this->tableSchema->primaryKey;
73 4 : if (Yii::app()->db->createCommand()
74 4 : ->select($primary_key)
75 4 : ->from($this->tableName())
76 4 : ->where($primary_key.'=:id', array (':id'=>$check_for_id))
77 4 : ->queryRow() )
78 4 : return true;
79 1 : return false;
80 : }
81 :
82 :
83 :
84 :
85 :
86 : public function refresh () {
87 2 : $this->_magic_attributes=array();
88 2 : return parent::refresh();
89 : }
90 :
91 : public function expandCallables($array) {
92 2 : foreach ($array as $key => $entry) {
93 2 : if (is_callable ($entry))
94 2 : $array[$key]=call_user_func ($entry);
95 2 : if (is_array ($entry))
96 2 : $array[$key]=$this->expandCallables ($entry);
97 2 : }
98 2 : return $array;
99 : }
100 :
101 :
102 :
103 :
104 :
105 :
106 :
107 : public function magicRelationEligibleForDelete ($relation_name, $relation_object) {
108 1 : $relations=$this->relations();
109 1 : $relation_primary_key=$relation_object->tableSchema->primaryKey;
110 :
111 1 : $pk=$relation_object->$relation_primary_key;
112 1 : if (isset ($this->_relation_updated[$relation_name][$pk]))
113 1 : unset ($this->_relation_updated[$relation_name][$pk]);
114 1 : $this->_eligible_for_delete[]=$relation_object;
115 1 : }
116 :
117 :
118 :
119 :
120 :
121 :
122 : public function magicRelationUpdated ($relation_name, $relation_object) {
123 :
124 2 : $relations=$this->relations();
125 2 : $relation_settings=$relations[$relation_name];
126 2 : $relation_primary_key=$relation_object->tableSchema->primaryKey;
127 2 : $attributes=$relation_object->attributes;
128 :
129 2 : unset ($attributes[$relation_primary_key]);
130 :
131 2 : unset ($attributes[$relation_settings[2]]);
132 2 : if (empty ($attributes)) {
133 :
134 0 : $this->magicRelationEligibleForDelete ($relation_name, $relation_object);
135 0 : return;
136 : }
137 :
138 2 : if (!isset ($this->_relation_updated[$relation_name]))
139 2 : $this->_relation_updated[$relation_name]=array ('new' => array());
140 2 : if ($relation_object->isNewRecord) {
141 2 : $this->_relation_updated[$relation_name]['new'][]=$relation_object;
142 2 : } else {
143 1 : $pk=$relation_object->$relation_primary_key;
144 1 : $this->_relation_updated[$relation_name][$pk]=$relation_object;
145 : }
146 2 : }
147 :
148 :
149 :
150 :
151 :
152 : public function setPost_Attributes($POST) {
153 2 : if (!isset ($POST[get_class ($this)]))
154 2 : return;
155 2 : $this->attributes=$POST[get_class ($this)];
156 2 : foreach ($this->relations() as $relation_name => $relation_settings) {
157 2 : $relation_type=$relation_settings[0];
158 2 : $relation_class=$relation_settings[1];
159 2 : $relation_fk=$relation_settings[2];
160 : switch ($relation_type) {
161 :
162 2 : case 'CHasOneRelation':
163 2 : if ($this->isNewRecord || empty ($this->$relation_name)) {
164 1 : if (!isset ($POST[$relation_class]))
165 1 : continue;
166 :
167 1 : $this->$relation_name=new $relation_class;
168 2 : } elseif (!isset ($POST[$relation_class])) {
169 0 : $this->$relation_name->unsetAttributes();
170 0 : $this->magicRelationUpdated ($relation_name, $this->$relation_name);
171 0 : $this->$relation_name=null;
172 0 : continue;
173 : }
174 2 : $this->$relation_name->attributes=$POST[$relation_class];
175 2 : $this->magicRelationUpdated ($relation_name, $this->$relation_name);
176 2 : break;
177 :
178 2 : case 'CHasManyRelation':
179 2 : if ($this->isNewRecord || empty ($this->$relation_name)) {
180 1 : if (!isset ($POST[$relation_class]))
181 1 : continue;
182 :
183 1 : $model_list=array ();
184 1 : foreach ($POST[$relation_class] as $post_attributes) {
185 1 : $model=new $relation_class;
186 1 : $model->attributes=$post_attributes;
187 1 : $this->magicRelationUpdated ($relation_name, $model);
188 1 : $model_list[]=$model;
189 1 : }
190 1 : $this->$relation_name=$model_list;
191 1 : } else {
192 1 : $model_list=array ();
193 1 : $delete_models=$this->$relation_name;
194 :
195 1 : $relation_pk=$relation_class::model()->tableSchema->primaryKey;
196 1 : if (!empty ($POST[$relation_class])) {
197 1 : $post_attribute_list=$POST[$relation_class];
198 1 : foreach ($delete_models as $delete_models_key => $model) {
199 1 : foreach ($post_attribute_list as $post_attributes_key => $post_attributes) {
200 :
201 1 : if (isset ($post_attributes[$relation_pk]) &&
202 1 : $post_attributes[$relation_pk] === $model->$relation_pk) {
203 :
204 1 : $model->attributes=$post_attributes;
205 1 : $this->magicRelationUpdated ($relation_name, $model);
206 1 : $model_list[]=$model;
207 :
208 1 : unset ($delete_models[$delete_models_key]);
209 1 : unset ($post_attribute_list[$post_attributes_key]);
210 1 : continue 2;
211 : }
212 1 : }
213 1 : }
214 :
215 :
216 1 : if (!empty ($post_attribute_list)) {
217 1 : foreach ($post_attribute_list as $post_attributes) {
218 1 : $model=new $relation_class;
219 1 : $model->attributes=$post_attributes;
220 1 : $this->magicRelationUpdated ($relation_name, $model);
221 1 : $model_list[]=$model;
222 1 : }
223 1 : }
224 1 : $this->$relation_name=$model_list;
225 :
226 :
227 :
228 :
229 1 : }
230 :
231 1 : if (!empty ($delete_models)) {
232 1 : foreach ($delete_models as $model) {
233 1 : $this->magicRelationEligibleForDelete ($relation_name, $model);
234 1 : }
235 1 : }
236 : }
237 2 : break;
238 :
239 2 : case 'CManyManyRelation':
240 2 : $many_many_map=$this->many_many_map();
241 :
242 :
243 2 : if (empty ($many_many_map[$relation_name]) ||
244 2 : empty ($many_many_map[$relation_name]['attribute']))
245 2 : continue;
246 2 : $delete_ids=array ();
247 2 : $insert_ids=array ();
248 2 : $relation_pk=$relation_class::model()->tableSchema->primaryKey;
249 2 : $list_attribute=$many_many_map[$relation_name]['attribute'];
250 :
251 2 : $selection=$this->$list_attribute;
252 :
253 2 : if (!empty ($this->$relation_name)) {
254 1 : foreach ($this->$relation_name as $related_model) {
255 1 : if (in_array ($related_model->$relation_pk, $selection))
256 1 : unset ($selection[array_search ($related_model->$relation_pk, $selection)]);
257 : else
258 1 : $delete_ids[]=$related_model->$relation_pk;
259 1 : }
260 1 : $insert_ids=$selection;
261 1 : } else {
262 1 : $insert_ids=$selection;
263 : }
264 2 : $pivot_table=$this->metaData->relations[$relation_name]->getJunctionTableName();
265 2 : $foreign_keys=$this->metaData->relations[$relation_name]->getJunctionForeignKeys();
266 2 : $relation_key_column=array_shift ($foreign_keys);
267 2 : $remote_key_column=array_shift ($foreign_keys);
268 2 : $primary_key=$this->tableSchema->primaryKey;
269 :
270 2 : if (!empty ($insert_ids)) {
271 2 : $this_var=$this;
272 2 : foreach ($insert_ids as $id) {
273 2 : $this->_insert_commands[]=array (
274 2 : 'table' => $this->metaData->relations[$relation_name]->getJunctionTableName(),
275 : 'columns' => array (
276 2 : $relation_key_column => function () use ($this_var, $primary_key) { return $this->$primary_key; },
277 2 : $remote_key_column => $id,
278 2 : ),
279 : );
280 2 : }
281 2 : }
282 :
283 2 : if (!empty ($delete_ids)) {
284 1 : foreach ($delete_ids as $id) {
285 1 : $this->_delete_commands[]=array (
286 1 : 'table' => $this->metaData->relations[$relation_name]->getJunctionTableName(),
287 : 'conditions' => array (
288 1 : 'and',
289 1 : $relation_key_column.' = :this_id',
290 1 : $remote_key_column.' = :remote_id',
291 1 : ),
292 : 'params' => array (
293 1 : ':this_id' => $this->$primary_key,
294 1 : ':remote_id' => $id,
295 1 : ),
296 : );
297 1 : }
298 1 : }
299 2 : break;
300 : }
301 2 : }
302 2 : }
303 :
304 :
305 :
306 : public function magicRelationManyManySelectedRead ($relation_name) {
307 2 : if (isset ($this->_magic_attributes[$relation_name.'_selected']))
308 2 : return $this->_magic_attributes[$relation_name.'_selected'];
309 0 : if (empty ($this->$relation_name))
310 0 : return null;
311 0 : $relations=$this->relations();
312 0 : $relation_settings=$relations[$relation_name];
313 0 : $primary_key=$relation_settings[1]::model()->tableSchema->primaryKey;
314 0 : $selected_list=array ();
315 0 : foreach ($this->$relation_name as $model)
316 0 : $selected_list[]=$model->$primary_key;
317 0 : return $this->_magic_attributes[$relation_name.'_selected']=$selected_list;
318 : }
319 :
320 : public function magicRelationManyManySelectedWrite ($relation_name, $selected_list) {
321 2 : $this->_magic_attributes[$relation_name.'_selected']=$selected_list;
322 2 : }
323 :
324 :
325 :
326 : public function getAllErrors () {
327 0 : $errors=$this->getErrors();
328 0 : $relations=$this->relations();
329 0 : if (empty ($relations))
330 0 : return $errors;
331 0 : foreach ($relations as $relation_name => $relation_settings) {
332 0 : if (empty ($this->$relation_name))
333 0 : continue;
334 0 : if (is_object ($this->$relation_name)) {
335 0 : if (!empty ($this->$relation_name->errors))
336 0 : $errors[$relation_name]=$this->$relation_name->getErrors ();
337 0 : } elseif (is_array ($this->$relation_name)) {
338 0 : $errors[$relation_name]=array ();
339 0 : foreach ($this->$relation_name as $related_key => $related_model) {
340 0 : if (!empty ($related_model->errors))
341 0 : $errors[$relation_name][$related_key]=$related_model->getErrors ();
342 0 : }
343 0 : if (empty ($errors[$relation_name]))
344 0 : unset ($errors[$relation_name]);
345 0 : }
346 0 : }
347 0 : return $errors;
348 : }
349 :
350 :
351 :
352 :
353 :
354 :
355 :
356 : public function beforeValidate() {
357 6 : if (empty ($this->_relation_updated))
358 6 : return parent::beforeValidate();
359 2 : $primary_key=$this->tableSchema->primaryKey;
360 2 : $relations=$this->relations();
361 2 : $isValid=true;
362 2 : foreach ($this->_relation_updated as $relation_name => $related_models) {
363 2 : if (empty ($related_models))
364 2 : continue;
365 2 : $relation_settings=$relations[$relation_name];
366 2 : if (!empty ($related_models['new'])) {
367 2 : foreach ($related_models['new'] as $new_models)
368 2 : $related_models[]=$new_models;
369 2 : }
370 2 : unset ($related_models['new']);
371 2 : foreach ($related_models as $key => $model) {
372 2 : $isValid=$isValid & $model->validate();
373 2 : if ($model->hasErrors()) {
374 :
375 0 : foreach ($model->errors as $attribute => $error_messages)
376 :
377 0 : foreach ($error_messages as $message)
378 0 : $this->addError ($relation_name, $message);
379 0 : }
380 2 : }
381 2 : }
382 2 : return $isValid & parent::beforeValidate();
383 : }
384 :
385 :
386 :
387 :
388 :
389 : public function beforeSave () {
390 :
391 :
392 3 : if (!empty ($this->_eligible_for_delete)) {
393 1 : foreach ($this->_eligible_for_delete as $related_object) {
394 1 : $related_object->delete();
395 1 : }
396 1 : $this->_eligible_for_delete=array ();
397 1 : }
398 3 : return parent::beforeSave();
399 : }
400 :
401 :
402 :
403 :
404 :
405 : protected function afterSave () {
406 :
407 3 : if (!empty ($this->_delete_commands)) {
408 1 : foreach ($this->_delete_commands as $delete_command) {
409 1 : $delete_command=$this->expandCallables ($delete_command);
410 1 : Yii::app()->db->createCommand()->delete (
411 1 : $delete_command['table'],
412 1 : $delete_command['conditions'],
413 1 : $delete_command['params']
414 1 : );
415 1 : }
416 1 : }
417 :
418 3 : if (!empty ($this->_insert_commands)) {
419 2 : foreach ($this->_insert_commands as $insert_command) {
420 2 : $insert_command=$this->expandCallables ($insert_command);
421 2 : Yii::app()->db->createCommand()->insert (
422 2 : $insert_command['table'],
423 2 : $insert_command['columns']
424 2 : );
425 2 : }
426 2 : }
427 :
428 3 : if (!empty ($this->_relation_updated)) {
429 2 : $primary_key=$this->tableSchema->primaryKey;
430 2 : $relations=$this->relations();
431 2 : foreach ($this->_relation_updated as $relation_name => $related_models) {
432 2 : if (empty ($related_models))
433 2 : continue;
434 2 : $relation_settings=$relations[$relation_name];
435 :
436 :
437 2 : foreach ($related_models as $pk_or_new => $r_model) {
438 2 : if ($pk_or_new === 'new') {
439 2 : foreach ($r_model as $new_model) {
440 2 : if (empty($new_model->$relation_settings[2]))
441 2 : $new_model->$relation_settings[2]=$this->$primary_key;
442 2 : $new_model->save();
443 2 : }
444 2 : continue;
445 : }
446 1 : $r_model->save();
447 2 : }
448 2 : }
449 2 : $this->_relation_updated=array ();
450 2 : }
451 :
452 3 : return parent::afterSave();
453 : }
454 :
455 : public function cascade ($cascade_setting) {
456 1 : $this->cascade_on_delete=$cascade_setting;
457 1 : }
458 :
459 :
460 :
461 :
462 : protected function beforeDelete () {
463 2 : if (!$this->cascade_on_delete)
464 2 : return parent::beforeDelete();
465 :
466 :
467 :
468 1 : foreach ($this->relations () as $relation_name => $relation_settings) {
469 1 : if (empty ($this->$relation_name))
470 1 : continue;
471 1 : switch ($relation_settings[0]) {
472 1 : case 'CHasOneRelation':
473 1 : $this->$relation_name->delete();
474 1 : break;
475 1 : case 'CHasManyRelation':
476 1 : foreach ($this->$relation_name as $relation_entry)
477 1 : $relation_entry->delete();
478 1 : break;
479 1 : case 'CManyManyRelation':
480 1 : $primary_key=$this->tableSchema->primaryKey;
481 1 : $pivot_table=$this->metaData->relations[$relation_name]->getJunctionTableName();
482 1 : $foreign_keys=$this->metaData->relations[$relation_name]->getJunctionForeignKeys();
483 1 : $primary_key_column=array_shift ($foreign_keys);
484 1 : $conditions="$primary_key_column = :id";
485 1 : $params=array (':id' => $this->$primary_key);
486 1 : $rows_affected=Yii::app()->db->createCommand()->delete ($pivot_table, $conditions, $params);
487 1 : break;
488 1 : default:
489 1 : continue;
490 1 : }
491 1 : }
492 1 : return parent::beforeDelete();
493 : }
494 :
495 : public function afterDelete () {
496 :
497 2 : return parent::afterDelete();
498 : }
499 :
500 : }
|