Mercurial > hg > Members > shoshi > webvirt
comparison cake/libs/model/behaviors/translate.php @ 0:261e66bd5a0c
hg init
author | Shoshi TAMAKI <shoshi@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Sun, 24 Jul 2011 21:08:31 +0900 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:261e66bd5a0c |
---|---|
1 <?php | |
2 /** | |
3 * Translate behavior | |
4 * | |
5 * PHP versions 4 and 5 | |
6 * | |
7 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | |
8 * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) | |
9 * | |
10 * Licensed under The MIT License | |
11 * Redistributions of files must retain the above copyright notice. | |
12 * | |
13 * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) | |
14 * @link http://cakephp.org CakePHP(tm) Project | |
15 * @package cake | |
16 * @subpackage cake.cake.libs.model.behaviors | |
17 * @since CakePHP(tm) v 1.2.0.4525 | |
18 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) | |
19 */ | |
20 | |
21 /** | |
22 * Translate behavior | |
23 * | |
24 * @package cake | |
25 * @subpackage cake.cake.libs.model.behaviors | |
26 * @link http://book.cakephp.org/view/1328/Translate | |
27 */ | |
28 class TranslateBehavior extends ModelBehavior { | |
29 | |
30 /** | |
31 * Used for runtime configuration of model | |
32 * | |
33 * @var array | |
34 */ | |
35 var $runtime = array(); | |
36 | |
37 /** | |
38 * Callback | |
39 * | |
40 * $config for TranslateBehavior should be | |
41 * array( 'fields' => array('field_one', | |
42 * 'field_two' => 'FieldAssoc', 'field_three')) | |
43 * | |
44 * With above example only one permanent hasMany will be joined (for field_two | |
45 * as FieldAssoc) | |
46 * | |
47 * $config could be empty - and translations configured dynamically by | |
48 * bindTranslation() method | |
49 * | |
50 * @param Model $model Model the behavior is being attached to. | |
51 * @param array $config Array of configuration information. | |
52 * @return mixed | |
53 * @access public | |
54 */ | |
55 function setup(&$model, $config = array()) { | |
56 $db =& ConnectionManager::getDataSource($model->useDbConfig); | |
57 if (!$db->connected) { | |
58 trigger_error( | |
59 sprintf(__('Datasource %s for TranslateBehavior of model %s is not connected', true), $model->useDbConfig, $model->alias), | |
60 E_USER_ERROR | |
61 ); | |
62 return false; | |
63 } | |
64 | |
65 $this->settings[$model->alias] = array(); | |
66 $this->runtime[$model->alias] = array('fields' => array()); | |
67 $this->translateModel($model); | |
68 return $this->bindTranslation($model, $config, false); | |
69 } | |
70 | |
71 /** | |
72 * Cleanup Callback unbinds bound translations and deletes setting information. | |
73 * | |
74 * @param Model $model Model being detached. | |
75 * @return void | |
76 * @access public | |
77 */ | |
78 function cleanup(&$model) { | |
79 $this->unbindTranslation($model); | |
80 unset($this->settings[$model->alias]); | |
81 unset($this->runtime[$model->alias]); | |
82 } | |
83 | |
84 /** | |
85 * beforeFind Callback | |
86 * | |
87 * @param Model $model Model find is being run on. | |
88 * @param array $query Array of Query parameters. | |
89 * @return array Modified query | |
90 * @access public | |
91 */ | |
92 function beforeFind(&$model, $query) { | |
93 $locale = $this->_getLocale($model); | |
94 if (empty($locale)) { | |
95 return $query; | |
96 } | |
97 $db =& ConnectionManager::getDataSource($model->useDbConfig); | |
98 $RuntimeModel =& $this->translateModel($model); | |
99 if (!empty($RuntimeModel->tablePrefix)) { | |
100 $tablePrefix = $RuntimeModel->tablePrefix; | |
101 } else { | |
102 $tablePrefix = $db->config['prefix']; | |
103 } | |
104 | |
105 if (is_string($query['fields']) && 'COUNT(*) AS '.$db->name('count') == $query['fields']) { | |
106 $query['fields'] = 'COUNT(DISTINCT('.$db->name($model->alias . '.' . $model->primaryKey) . ')) ' . $db->alias . 'count'; | |
107 $query['joins'][] = array( | |
108 'type' => 'INNER', | |
109 'alias' => $RuntimeModel->alias, | |
110 'table' => $db->name($tablePrefix . $RuntimeModel->useTable), | |
111 'conditions' => array( | |
112 $model->alias . '.' . $model->primaryKey => $db->identifier($RuntimeModel->alias.'.foreign_key'), | |
113 $RuntimeModel->alias.'.model' => $model->name, | |
114 $RuntimeModel->alias.'.locale' => $locale | |
115 ) | |
116 ); | |
117 return $query; | |
118 } | |
119 $autoFields = false; | |
120 | |
121 if (empty($query['fields'])) { | |
122 $query['fields'] = array($model->alias.'.*'); | |
123 | |
124 $recursive = $model->recursive; | |
125 if (isset($query['recursive'])) { | |
126 $recursive = $query['recursive']; | |
127 } | |
128 | |
129 if ($recursive >= 0) { | |
130 foreach (array('hasOne', 'belongsTo') as $type) { | |
131 foreach ($model->{$type} as $key => $value) { | |
132 | |
133 if (empty($value['fields'])) { | |
134 $query['fields'][] = $key.'.*'; | |
135 } else { | |
136 foreach ($value['fields'] as $field) { | |
137 $query['fields'][] = $key.'.'.$field; | |
138 } | |
139 } | |
140 } | |
141 } | |
142 } | |
143 $autoFields = true; | |
144 } | |
145 $fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']); | |
146 $addFields = array(); | |
147 if (is_array($query['fields'])) { | |
148 foreach ($fields as $key => $value) { | |
149 $field = (is_numeric($key)) ? $value : $key; | |
150 | |
151 if (in_array($model->alias.'.*', $query['fields']) || $autoFields || in_array($model->alias.'.'.$field, $query['fields']) || in_array($field, $query['fields'])) { | |
152 $addFields[] = $field; | |
153 } | |
154 } | |
155 } | |
156 | |
157 if ($addFields) { | |
158 foreach ($addFields as $field) { | |
159 foreach (array($field, $model->alias.'.'.$field) as $_field) { | |
160 $key = array_search($_field, $query['fields']); | |
161 | |
162 if ($key !== false) { | |
163 unset($query['fields'][$key]); | |
164 } | |
165 } | |
166 | |
167 if (is_array($locale)) { | |
168 foreach ($locale as $_locale) { | |
169 $query['fields'][] = 'I18n__'.$field.'__'.$_locale.'.content'; | |
170 $query['joins'][] = array( | |
171 'type' => 'LEFT', | |
172 'alias' => 'I18n__'.$field.'__'.$_locale, | |
173 'table' => $db->name($tablePrefix . $RuntimeModel->useTable), | |
174 'conditions' => array( | |
175 $model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}__{$_locale}.foreign_key"), | |
176 'I18n__'.$field.'__'.$_locale.'.model' => $model->name, | |
177 'I18n__'.$field.'__'.$_locale.'.'.$RuntimeModel->displayField => $field, | |
178 'I18n__'.$field.'__'.$_locale.'.locale' => $_locale | |
179 ) | |
180 ); | |
181 } | |
182 } else { | |
183 $query['fields'][] = 'I18n__'.$field.'.content'; | |
184 $query['joins'][] = array( | |
185 'type' => 'LEFT', | |
186 'alias' => 'I18n__'.$field, | |
187 'table' => $db->name($tablePrefix . $RuntimeModel->useTable), | |
188 'conditions' => array( | |
189 $model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}.foreign_key"), | |
190 'I18n__'.$field.'.model' => $model->name, | |
191 'I18n__'.$field.'.'.$RuntimeModel->displayField => $field | |
192 ) | |
193 ); | |
194 | |
195 if (is_string($query['conditions'])) { | |
196 $query['conditions'] = $db->conditions($query['conditions'], true, false, $model) . ' AND '.$db->name('I18n__'.$field.'.locale').' = \''.$locale.'\''; | |
197 } else { | |
198 $query['conditions'][$db->name("I18n__{$field}.locale")] = $locale; | |
199 } | |
200 } | |
201 } | |
202 } | |
203 if (is_array($query['fields'])) { | |
204 $query['fields'] = array_merge($query['fields']); | |
205 } | |
206 $this->runtime[$model->alias]['beforeFind'] = $addFields; | |
207 return $query; | |
208 } | |
209 | |
210 /** | |
211 * afterFind Callback | |
212 * | |
213 * @param Model $model Model find was run on | |
214 * @param array $results Array of model results. | |
215 * @param boolean $primary Did the find originate on $model. | |
216 * @return array Modified results | |
217 * @access public | |
218 */ | |
219 function afterFind(&$model, $results, $primary) { | |
220 $this->runtime[$model->alias]['fields'] = array(); | |
221 $locale = $this->_getLocale($model); | |
222 | |
223 if (empty($locale) || empty($results) || empty($this->runtime[$model->alias]['beforeFind'])) { | |
224 return $results; | |
225 } | |
226 $beforeFind = $this->runtime[$model->alias]['beforeFind']; | |
227 | |
228 foreach ($results as $key => $row) { | |
229 $results[$key][$model->alias]['locale'] = (is_array($locale)) ? @$locale[0] : $locale; | |
230 | |
231 foreach ($beforeFind as $field) { | |
232 if (is_array($locale)) { | |
233 foreach ($locale as $_locale) { | |
234 if (!isset($results[$key][$model->alias][$field]) && !empty($results[$key]['I18n__'.$field.'__'.$_locale]['content'])) { | |
235 $results[$key][$model->alias][$field] = $results[$key]['I18n__'.$field.'__'.$_locale]['content']; | |
236 } | |
237 unset($results[$key]['I18n__'.$field.'__'.$_locale]); | |
238 } | |
239 | |
240 if (!isset($results[$key][$model->alias][$field])) { | |
241 $results[$key][$model->alias][$field] = ''; | |
242 } | |
243 } else { | |
244 $value = ''; | |
245 if (!empty($results[$key]['I18n__'.$field]['content'])) { | |
246 $value = $results[$key]['I18n__'.$field]['content']; | |
247 } | |
248 $results[$key][$model->alias][$field] = $value; | |
249 unset($results[$key]['I18n__'.$field]); | |
250 } | |
251 } | |
252 } | |
253 return $results; | |
254 } | |
255 | |
256 /** | |
257 * beforeValidate Callback | |
258 * | |
259 * @param Model $model Model invalidFields was called on. | |
260 * @return boolean | |
261 * @access public | |
262 */ | |
263 function beforeValidate(&$model) { | |
264 $locale = $this->_getLocale($model); | |
265 if (empty($locale)) { | |
266 return true; | |
267 } | |
268 $fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']); | |
269 $tempData = array(); | |
270 | |
271 foreach ($fields as $key => $value) { | |
272 $field = (is_numeric($key)) ? $value : $key; | |
273 | |
274 if (isset($model->data[$model->alias][$field])) { | |
275 $tempData[$field] = $model->data[$model->alias][$field]; | |
276 if (is_array($model->data[$model->alias][$field])) { | |
277 if (is_string($locale) && !empty($model->data[$model->alias][$field][$locale])) { | |
278 $model->data[$model->alias][$field] = $model->data[$model->alias][$field][$locale]; | |
279 } else { | |
280 $values = array_values($model->data[$model->alias][$field]); | |
281 $model->data[$model->alias][$field] = $values[0]; | |
282 } | |
283 } | |
284 } | |
285 } | |
286 $this->runtime[$model->alias]['beforeSave'] = $tempData; | |
287 return true; | |
288 } | |
289 | |
290 /** | |
291 * afterSave Callback | |
292 * | |
293 * @param Model $model Model the callback is called on | |
294 * @param boolean $created Whether or not the save created a record. | |
295 * @return void | |
296 * @access public | |
297 */ | |
298 function afterSave(&$model, $created) { | |
299 if (!isset($this->runtime[$model->alias]['beforeSave'])) { | |
300 return true; | |
301 } | |
302 $locale = $this->_getLocale($model); | |
303 $tempData = $this->runtime[$model->alias]['beforeSave']; | |
304 unset($this->runtime[$model->alias]['beforeSave']); | |
305 $conditions = array('model' => $model->alias, 'foreign_key' => $model->id); | |
306 $RuntimeModel =& $this->translateModel($model); | |
307 | |
308 foreach ($tempData as $field => $value) { | |
309 unset($conditions['content']); | |
310 $conditions['field'] = $field; | |
311 if (is_array($value)) { | |
312 $conditions['locale'] = array_keys($value); | |
313 } else { | |
314 $conditions['locale'] = $locale; | |
315 if (is_array($locale)) { | |
316 $value = array($locale[0] => $value); | |
317 } else { | |
318 $value = array($locale => $value); | |
319 } | |
320 } | |
321 $translations = $RuntimeModel->find('list', array('conditions' => $conditions, 'fields' => array($RuntimeModel->alias . '.locale', $RuntimeModel->alias . '.id'))); | |
322 foreach ($value as $_locale => $_value) { | |
323 $RuntimeModel->create(); | |
324 $conditions['locale'] = $_locale; | |
325 $conditions['content'] = $_value; | |
326 if (array_key_exists($_locale, $translations)) { | |
327 $RuntimeModel->save(array($RuntimeModel->alias => array_merge($conditions, array('id' => $translations[$_locale])))); | |
328 } else { | |
329 $RuntimeModel->save(array($RuntimeModel->alias => $conditions)); | |
330 } | |
331 } | |
332 } | |
333 } | |
334 | |
335 /** | |
336 * afterDelete Callback | |
337 * | |
338 * @param Model $model Model the callback was run on. | |
339 * @return void | |
340 * @access public | |
341 */ | |
342 function afterDelete(&$model) { | |
343 $RuntimeModel =& $this->translateModel($model); | |
344 $conditions = array('model' => $model->alias, 'foreign_key' => $model->id); | |
345 $RuntimeModel->deleteAll($conditions); | |
346 } | |
347 | |
348 /** | |
349 * Get selected locale for model | |
350 * | |
351 * @param Model $model Model the locale needs to be set/get on. | |
352 * @return mixed string or false | |
353 * @access protected | |
354 */ | |
355 function _getLocale(&$model) { | |
356 if (!isset($model->locale) || is_null($model->locale)) { | |
357 if (!class_exists('I18n')) { | |
358 App::import('Core', 'i18n'); | |
359 } | |
360 $I18n =& I18n::getInstance(); | |
361 $I18n->l10n->get(Configure::read('Config.language')); | |
362 $model->locale = $I18n->l10n->locale; | |
363 } | |
364 | |
365 return $model->locale; | |
366 } | |
367 | |
368 /** | |
369 * Get instance of model for translations. | |
370 * | |
371 * If the model has a translateModel property set, this will be used as the class | |
372 * name to find/use. If no translateModel property is found 'I18nModel' will be used. | |
373 * | |
374 * @param Model $model Model to get a translatemodel for. | |
375 * @return object | |
376 * @access public | |
377 */ | |
378 function &translateModel(&$model) { | |
379 if (!isset($this->runtime[$model->alias]['model'])) { | |
380 if (!isset($model->translateModel) || empty($model->translateModel)) { | |
381 $className = 'I18nModel'; | |
382 } else { | |
383 $className = $model->translateModel; | |
384 } | |
385 | |
386 if (PHP5) { | |
387 $this->runtime[$model->alias]['model'] = ClassRegistry::init($className, 'Model'); | |
388 } else { | |
389 $this->runtime[$model->alias]['model'] =& ClassRegistry::init($className, 'Model'); | |
390 } | |
391 } | |
392 if (!empty($model->translateTable) && $model->translateTable !== $this->runtime[$model->alias]['model']->useTable) { | |
393 $this->runtime[$model->alias]['model']->setSource($model->translateTable); | |
394 } elseif (empty($model->translateTable) && empty($model->translateModel)) { | |
395 $this->runtime[$model->alias]['model']->setSource('i18n'); | |
396 } | |
397 $model =& $this->runtime[$model->alias]['model']; | |
398 return $model; | |
399 } | |
400 | |
401 /** | |
402 * Bind translation for fields, optionally with hasMany association for | |
403 * fake field | |
404 * | |
405 * @param object instance of model | |
406 * @param mixed string with field or array(field1, field2=>AssocName, field3) | |
407 * @param boolean $reset | |
408 * @return bool | |
409 */ | |
410 function bindTranslation(&$model, $fields, $reset = true) { | |
411 if (is_string($fields)) { | |
412 $fields = array($fields); | |
413 } | |
414 $associations = array(); | |
415 $RuntimeModel =& $this->translateModel($model); | |
416 $default = array('className' => $RuntimeModel->alias, 'foreignKey' => 'foreign_key'); | |
417 | |
418 foreach ($fields as $key => $value) { | |
419 if (is_numeric($key)) { | |
420 $field = $value; | |
421 $association = null; | |
422 } else { | |
423 $field = $key; | |
424 $association = $value; | |
425 } | |
426 | |
427 if (array_key_exists($field, $this->settings[$model->alias])) { | |
428 unset($this->settings[$model->alias][$field]); | |
429 } elseif (in_array($field, $this->settings[$model->alias])) { | |
430 $this->settings[$model->alias] = array_merge(array_diff_assoc($this->settings[$model->alias], array($field))); | |
431 } | |
432 | |
433 if (array_key_exists($field, $this->runtime[$model->alias]['fields'])) { | |
434 unset($this->runtime[$model->alias]['fields'][$field]); | |
435 } elseif (in_array($field, $this->runtime[$model->alias]['fields'])) { | |
436 $this->runtime[$model->alias]['fields'] = array_merge(array_diff_assoc($this->runtime[$model->alias]['fields'], array($field))); | |
437 } | |
438 | |
439 if (is_null($association)) { | |
440 if ($reset) { | |
441 $this->runtime[$model->alias]['fields'][] = $field; | |
442 } else { | |
443 $this->settings[$model->alias][] = $field; | |
444 } | |
445 } else { | |
446 if ($reset) { | |
447 $this->runtime[$model->alias]['fields'][$field] = $association; | |
448 } else { | |
449 $this->settings[$model->alias][$field] = $association; | |
450 } | |
451 | |
452 foreach (array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany') as $type) { | |
453 if (isset($model->{$type}[$association]) || isset($model->__backAssociation[$type][$association])) { | |
454 trigger_error( | |
455 sprintf(__('Association %s is already binded to model %s', true), $association, $model->alias), | |
456 E_USER_ERROR | |
457 ); | |
458 return false; | |
459 } | |
460 } | |
461 $associations[$association] = array_merge($default, array('conditions' => array( | |
462 'model' => $model->alias, | |
463 $RuntimeModel->displayField => $field | |
464 ))); | |
465 } | |
466 } | |
467 | |
468 if (!empty($associations)) { | |
469 $model->bindModel(array('hasMany' => $associations), $reset); | |
470 } | |
471 return true; | |
472 } | |
473 | |
474 /** | |
475 * Unbind translation for fields, optionally unbinds hasMany association for | |
476 * fake field | |
477 * | |
478 * @param object $model instance of model | |
479 * @param mixed $fields string with field, or array(field1, field2=>AssocName, field3), or null for | |
480 * unbind all original translations | |
481 * @return bool | |
482 */ | |
483 function unbindTranslation(&$model, $fields = null) { | |
484 if (empty($fields) && empty($this->settings[$model->alias])) { | |
485 return false; | |
486 } | |
487 if (empty($fields)) { | |
488 return $this->unbindTranslation($model, $this->settings[$model->alias]); | |
489 } | |
490 | |
491 if (is_string($fields)) { | |
492 $fields = array($fields); | |
493 } | |
494 $RuntimeModel =& $this->translateModel($model); | |
495 $associations = array(); | |
496 | |
497 foreach ($fields as $key => $value) { | |
498 if (is_numeric($key)) { | |
499 $field = $value; | |
500 $association = null; | |
501 } else { | |
502 $field = $key; | |
503 $association = $value; | |
504 } | |
505 | |
506 if (array_key_exists($field, $this->settings[$model->alias])) { | |
507 unset($this->settings[$model->alias][$field]); | |
508 } elseif (in_array($field, $this->settings[$model->alias])) { | |
509 $this->settings[$model->alias] = array_merge(array_diff_assoc($this->settings[$model->alias], array($field))); | |
510 } | |
511 | |
512 if (array_key_exists($field, $this->runtime[$model->alias]['fields'])) { | |
513 unset($this->runtime[$model->alias]['fields'][$field]); | |
514 } elseif (in_array($field, $this->runtime[$model->alias]['fields'])) { | |
515 $this->runtime[$model->alias]['fields'] = array_merge(array_diff_assoc($this->runtime[$model->alias]['fields'], array($field))); | |
516 } | |
517 | |
518 if (!is_null($association) && (isset($model->hasMany[$association]) || isset($model->__backAssociation['hasMany'][$association]))) { | |
519 $associations[] = $association; | |
520 } | |
521 } | |
522 | |
523 if (!empty($associations)) { | |
524 $model->unbindModel(array('hasMany' => $associations), false); | |
525 } | |
526 return true; | |
527 } | |
528 } | |
529 if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) { | |
530 | |
531 /** | |
532 * @package cake | |
533 * @subpackage cake.cake.libs.model.behaviors | |
534 */ | |
535 class I18nModel extends AppModel { | |
536 var $name = 'I18nModel'; | |
537 var $useTable = 'i18n'; | |
538 var $displayField = 'field'; | |
539 } | |
540 } |