1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
20:
21: 22: 23: 24: 25: 26: 27:
28: class TranslateBehavior extends ModelBehavior {
29:
30: 31: 32: 33: 34:
35: var $runtime = array();
36:
37: 38: 39: 40: 41:
42: var $_joinTable;
43:
44: 45: 46: 47: 48:
49: var $_runtimeModel;
50:
51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68:
69: function setup(&$model, $config = array()) {
70: $db =& ConnectionManager::getDataSource($model->useDbConfig);
71: if (!$db->connected) {
72: trigger_error(
73: sprintf(__('Datasource %s for TranslateBehavior of model %s is not connected', true), $model->useDbConfig, $model->alias),
74: E_USER_ERROR
75: );
76: return false;
77: }
78:
79: $this->settings[$model->alias] = array();
80: $this->runtime[$model->alias] = array('fields' => array());
81: $this->translateModel($model);
82: return $this->bindTranslation($model, $config, false);
83: }
84:
85: 86: 87: 88: 89: 90: 91:
92: function cleanup(&$model) {
93: $this->unbindTranslation($model);
94: unset($this->settings[$model->alias]);
95: unset($this->runtime[$model->alias]);
96: }
97:
98: 99: 100: 101: 102: 103: 104: 105:
106: function beforeFind(&$model, $query) {
107: $locale = $this->_getLocale($model);
108: if (empty($locale)) {
109: return $query;
110: }
111: $db =& ConnectionManager::getDataSource($model->useDbConfig);
112: $RuntimeModel =& $this->translateModel($model);
113:
114: if (!empty($RuntimeModel->tablePrefix)) {
115: $tablePrefix = $RuntimeModel->tablePrefix;
116: } else {
117: $tablePrefix = $db->config['prefix'];
118: }
119: $joinTable = new StdClass();
120: $joinTable->tablePrefix = $tablePrefix;
121: $joinTable->table = $RuntimeModel->table;
122:
123: $this->_joinTable = $joinTable;
124: $this->_runtimeModel = $RuntimeModel;
125:
126: if (is_string($query['fields']) && 'COUNT(*) AS ' . $db->name('count') == $query['fields']) {
127: $query['fields'] = 'COUNT(DISTINCT('.$db->name($model->alias . '.' . $model->primaryKey) . ')) ' . $db->alias . 'count';
128:
129: $query['joins'][] = array(
130: 'type' => 'INNER',
131: 'alias' => $RuntimeModel->alias,
132: 'table' => $joinTable,
133: 'conditions' => array(
134: $model->alias . '.' . $model->primaryKey => $db->identifier($RuntimeModel->alias.'.foreign_key'),
135: $RuntimeModel->alias.'.model' => $model->name,
136: $RuntimeModel->alias.'.locale' => $locale
137: )
138: );
139: $conditionFields = $this->_checkConditions($model, $query);
140: foreach ($conditionFields as $field) {
141: $query = $this->_addJoin($model, $query, $field, $locale, false);
142: }
143: unset($this->_joinTable, $this->_runtimeModel);
144: return $query;
145: }
146: $autoFields = false;
147:
148: if (empty($query['fields'])) {
149: $query['fields'] = array($model->alias.'.*');
150:
151: $recursive = $model->recursive;
152: if (isset($query['recursive'])) {
153: $recursive = $query['recursive'];
154: }
155:
156: if ($recursive >= 0) {
157: foreach (array('hasOne', 'belongsTo') as $type) {
158: foreach ($model->{$type} as $key => $value) {
159:
160: if (empty($value['fields'])) {
161: $query['fields'][] = $key.'.*';
162: } else {
163: foreach ($value['fields'] as $field) {
164: $query['fields'][] = $key.'.'.$field;
165: }
166: }
167: }
168: }
169: }
170: $autoFields = true;
171: }
172: $fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']);
173: $addFields = array();
174: if (is_array($query['fields'])) {
175: foreach ($fields as $key => $value) {
176: $field = (is_numeric($key)) ? $value : $key;
177: if (in_array($model->alias.'.*', $query['fields']) || $autoFields || in_array($model->alias.'.'.$field, $query['fields']) || in_array($field, $query['fields'])) {
178: $addFields[] = $field;
179: }
180: }
181: }
182:
183: if ($addFields) {
184: foreach ($addFields as $field) {
185: foreach (array($field, $model->alias.'.'.$field) as $_field) {
186: $key = array_search($_field, $query['fields']);
187:
188: if ($key !== false) {
189: unset($query['fields'][$key]);
190: }
191: }
192: $query = $this->_addJoin($model, $query, $field, $locale, true);
193: }
194: }
195: $this->runtime[$model->alias]['beforeFind'] = $addFields;
196: unset($this->_joinTable, $this->_runtimeModel);
197: return $query;
198: }
199:
200: 201: 202: 203: 204: 205: 206: 207:
208: function _checkConditions(&$model, $query) {
209: $conditionFields = array();
210: if (empty($query['conditions']) || (!empty($query['conditions']) && !is_array($query['conditions'])) ) {
211: return $conditionFields;
212: }
213: foreach ($query['conditions'] as $col => $val) {
214: foreach ($this->settings[$model->alias] as $field => $assoc) {
215: if (is_numeric($field)) {
216: $field = $assoc;
217: }
218: if (strpos($col, $field) !== false) {
219: $conditionFields[] = $field;
220: }
221: }
222: }
223: return $conditionFields;
224: }
225:
226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236:
237: function _addJoin(&$model, $query, $field, $locale, $addField = false) {
238: $db =& ConnectionManager::getDataSource($model->useDbConfig);
239:
240: $RuntimeModel = $this->_runtimeModel;
241: $joinTable = $this->_joinTable;
242:
243: if (is_array($locale)) {
244: foreach ($locale as $_locale) {
245: if ($addField) {
246: $query['fields'][] = 'I18n__'.$field.'__'.$_locale.'.content';
247: }
248: $query['joins'][] = array(
249: 'type' => 'LEFT',
250: 'alias' => 'I18n__'.$field.'__'.$_locale,
251: 'table' => $joinTable,
252: 'conditions' => array(
253: $model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}__{$_locale}.foreign_key"),
254: 'I18n__'.$field.'__'.$_locale.'.model' => $model->name,
255: 'I18n__'.$field.'__'.$_locale.'.'.$RuntimeModel->displayField => $field,
256: 'I18n__'.$field.'__'.$_locale.'.locale' => $_locale
257: )
258: );
259: }
260: } else {
261: if ($addField) {
262: $query['fields'][] = 'I18n__'.$field.'.content';
263: }
264: $query['joins'][] = array(
265: 'type' => 'INNER',
266: 'alias' => 'I18n__'.$field,
267: 'table' => $joinTable,
268: 'conditions' => array(
269: $model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}.foreign_key"),
270: 'I18n__'.$field.'.model' => $model->name,
271: 'I18n__'.$field.'.'.$RuntimeModel->displayField => $field,
272: 'I18n__'.$field.'.locale' => $locale
273: )
274: );
275: }
276: return $query;
277: }
278:
279: 280: 281: 282: 283: 284: 285: 286: 287:
288: function afterFind(&$model, $results, $primary) {
289: $this->runtime[$model->alias]['fields'] = array();
290: $locale = $this->_getLocale($model);
291:
292: if (empty($locale) || empty($results) || empty($this->runtime[$model->alias]['beforeFind'])) {
293: return $results;
294: }
295: $beforeFind = $this->runtime[$model->alias]['beforeFind'];
296:
297: foreach ($results as $key => $row) {
298: $results[$key][$model->alias]['locale'] = (is_array($locale)) ? @$locale[0] : $locale;
299:
300: foreach ($beforeFind as $field) {
301: if (is_array($locale)) {
302: foreach ($locale as $_locale) {
303: if (!isset($results[$key][$model->alias][$field]) && !empty($results[$key]['I18n__'.$field.'__'.$_locale]['content'])) {
304: $results[$key][$model->alias][$field] = $results[$key]['I18n__'.$field.'__'.$_locale]['content'];
305: }
306: unset($results[$key]['I18n__'.$field.'__'.$_locale]);
307: }
308:
309: if (!isset($results[$key][$model->alias][$field])) {
310: $results[$key][$model->alias][$field] = '';
311: }
312: } else {
313: $value = '';
314: if (!empty($results[$key]['I18n__'.$field]['content'])) {
315: $value = $results[$key]['I18n__'.$field]['content'];
316: }
317: $results[$key][$model->alias][$field] = $value;
318: unset($results[$key]['I18n__'.$field]);
319: }
320: }
321: }
322: return $results;
323: }
324:
325: 326: 327: 328: 329: 330: 331:
332: function beforeValidate(&$model) {
333: unset($this->runtime[$model->alias]['beforeSave']);
334: $this->_setRuntimeData($model);
335: return true;
336: }
337:
338: 339: 340: 341: 342: 343: 344: 345: 346:
347: function beforeSave($model, $options = array()) {
348: if (isset($options['validate']) && $options['validate'] == false) {
349: unset($this->runtime[$model->alias]['beforeSave']);
350: }
351: if (isset($this->runtime[$model->alias]['beforeSave'])) {
352: return true;
353: }
354: $this->_setRuntimeData($model);
355: return true;
356: }
357:
358: 359: 360: 361: 362: 363: 364: 365: 366: 367:
368: function _setRuntimeData(Model $model) {
369: $locale = $this->_getLocale($model);
370: if (empty($locale)) {
371: return true;
372: }
373: $fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']);
374: $tempData = array();
375:
376: foreach ($fields as $key => $value) {
377: $field = (is_numeric($key)) ? $value : $key;
378:
379: if (isset($model->data[$model->alias][$field])) {
380: $tempData[$field] = $model->data[$model->alias][$field];
381: if (is_array($model->data[$model->alias][$field])) {
382: if (is_string($locale) && !empty($model->data[$model->alias][$field][$locale])) {
383: $model->data[$model->alias][$field] = $model->data[$model->alias][$field][$locale];
384: } else {
385: $values = array_values($model->data[$model->alias][$field]);
386: $model->data[$model->alias][$field] = $values[0];
387: }
388: }
389: }
390: }
391: $this->runtime[$model->alias]['beforeSave'] = $tempData;
392: }
393:
394: 395: 396: 397: 398: 399: 400: 401:
402: function afterSave(&$model, $created) {
403: if (!isset($this->runtime[$model->alias]['beforeValidate']) && !isset($this->runtime[$model->alias]['beforeSave'])) {
404: return true;
405: }
406: $locale = $this->_getLocale($model);
407: if (isset($this->runtime[$model->alias]['beforeValidate'])) {
408: $tempData = $this->runtime[$model->alias]['beforeValidate'];
409: } else {
410: $tempData = $this->runtime[$model->alias]['beforeSave'];
411: }
412:
413: unset($this->runtime[$model->alias]['beforeValidate'], $this->runtime[$model->alias]['beforeSave']);
414: $conditions = array('model' => $model->alias, 'foreign_key' => $model->id);
415: $RuntimeModel =& $this->translateModel($model);
416:
417: foreach ($tempData as $field => $value) {
418: unset($conditions['content']);
419: $conditions['field'] = $field;
420: if (is_array($value)) {
421: $conditions['locale'] = array_keys($value);
422: } else {
423: $conditions['locale'] = $locale;
424: if (is_array($locale)) {
425: $value = array($locale[0] => $value);
426: } else {
427: $value = array($locale => $value);
428: }
429: }
430: $translations = $RuntimeModel->find('list', array('conditions' => $conditions, 'fields' => array($RuntimeModel->alias . '.locale', $RuntimeModel->alias . '.id')));
431: foreach ($value as $_locale => $_value) {
432: $RuntimeModel->create();
433: $conditions['locale'] = $_locale;
434: $conditions['content'] = $_value;
435: if (array_key_exists($_locale, $translations)) {
436: $RuntimeModel->save(array($RuntimeModel->alias => array_merge($conditions, array('id' => $translations[$_locale]))));
437: } else {
438: $RuntimeModel->save(array($RuntimeModel->alias => $conditions));
439: }
440: }
441: }
442: }
443:
444: 445: 446: 447: 448: 449: 450:
451: function afterDelete(&$model) {
452: $RuntimeModel =& $this->translateModel($model);
453: $conditions = array('model' => $model->alias, 'foreign_key' => $model->id);
454: $RuntimeModel->deleteAll($conditions);
455: }
456:
457: 458: 459: 460: 461: 462: 463:
464: function _getLocale(&$model) {
465: if (!isset($model->locale) || is_null($model->locale)) {
466: if (!class_exists('I18n')) {
467: App::import('Core', 'i18n');
468: }
469: $I18n =& I18n::getInstance();
470: $I18n->l10n->get(Configure::read('Config.language'));
471: $model->locale = $I18n->l10n->locale;
472: }
473:
474: return $model->locale;
475: }
476:
477: 478: 479: 480: 481: 482: 483: 484: 485: 486:
487: function &translateModel(&$model) {
488: if (!isset($this->runtime[$model->alias]['model'])) {
489: if (!isset($model->translateModel) || empty($model->translateModel)) {
490: $className = 'I18nModel';
491: } else {
492: $className = $model->translateModel;
493: }
494:
495: if (PHP5) {
496: $this->runtime[$model->alias]['model'] = ClassRegistry::init($className, 'Model');
497: } else {
498: $this->runtime[$model->alias]['model'] =& ClassRegistry::init($className, 'Model');
499: }
500: }
501: if (!empty($model->translateTable) && $model->translateTable !== $this->runtime[$model->alias]['model']->useTable) {
502: $this->runtime[$model->alias]['model']->setSource($model->translateTable);
503: } elseif (empty($model->translateTable) && empty($model->translateModel)) {
504: $this->runtime[$model->alias]['model']->setSource('i18n');
505: }
506: $model =& $this->runtime[$model->alias]['model'];
507: return $model;
508: }
509:
510: 511: 512: 513: 514: 515: 516: 517: 518:
519: function bindTranslation(&$model, $fields, $reset = true) {
520: if (is_string($fields)) {
521: $fields = array($fields);
522: }
523: $associations = array();
524: $RuntimeModel =& $this->translateModel($model);
525: $default = array('className' => $RuntimeModel->alias, 'foreignKey' => 'foreign_key');
526:
527: foreach ($fields as $key => $value) {
528: if (is_numeric($key)) {
529: $field = $value;
530: $association = null;
531: } else {
532: $field = $key;
533: $association = $value;
534: }
535:
536: if (array_key_exists($field, $this->settings[$model->alias])) {
537: unset($this->settings[$model->alias][$field]);
538: } elseif (in_array($field, $this->settings[$model->alias])) {
539: $this->settings[$model->alias] = array_merge(array_diff_assoc($this->settings[$model->alias], array($field)));
540: }
541:
542: if (array_key_exists($field, $this->runtime[$model->alias]['fields'])) {
543: unset($this->runtime[$model->alias]['fields'][$field]);
544: } elseif (in_array($field, $this->runtime[$model->alias]['fields'])) {
545: $this->runtime[$model->alias]['fields'] = array_merge(array_diff_assoc($this->runtime[$model->alias]['fields'], array($field)));
546: }
547:
548: if (is_null($association)) {
549: if ($reset) {
550: $this->runtime[$model->alias]['fields'][] = $field;
551: } else {
552: $this->settings[$model->alias][] = $field;
553: }
554: } else {
555: if ($reset) {
556: $this->runtime[$model->alias]['fields'][$field] = $association;
557: } else {
558: $this->settings[$model->alias][$field] = $association;
559: }
560:
561: foreach (array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany') as $type) {
562: if (isset($model->{$type}[$association]) || isset($model->__backAssociation[$type][$association])) {
563: trigger_error(
564: sprintf(__('Association %s is already binded to model %s', true), $association, $model->alias),
565: E_USER_ERROR
566: );
567: return false;
568: }
569: }
570: $associations[$association] = array_merge($default, array('conditions' => array(
571: 'model' => $model->alias,
572: $RuntimeModel->displayField => $field
573: )));
574: }
575: }
576:
577: if (!empty($associations)) {
578: $model->bindModel(array('hasMany' => $associations), $reset);
579: }
580: return true;
581: }
582:
583: 584: 585: 586: 587: 588: 589: 590: 591:
592: function unbindTranslation(&$model, $fields = null) {
593: if (empty($fields) && empty($this->settings[$model->alias])) {
594: return false;
595: }
596: if (empty($fields)) {
597: return $this->unbindTranslation($model, $this->settings[$model->alias]);
598: }
599:
600: if (is_string($fields)) {
601: $fields = array($fields);
602: }
603: $RuntimeModel =& $this->translateModel($model);
604: $associations = array();
605:
606: foreach ($fields as $key => $value) {
607: if (is_numeric($key)) {
608: $field = $value;
609: $association = null;
610: } else {
611: $field = $key;
612: $association = $value;
613: }
614:
615: if (array_key_exists($field, $this->settings[$model->alias])) {
616: unset($this->settings[$model->alias][$field]);
617: } elseif (in_array($field, $this->settings[$model->alias])) {
618: $this->settings[$model->alias] = array_merge(array_diff_assoc($this->settings[$model->alias], array($field)));
619: }
620:
621: if (array_key_exists($field, $this->runtime[$model->alias]['fields'])) {
622: unset($this->runtime[$model->alias]['fields'][$field]);
623: } elseif (in_array($field, $this->runtime[$model->alias]['fields'])) {
624: $this->runtime[$model->alias]['fields'] = array_merge(array_diff_assoc($this->runtime[$model->alias]['fields'], array($field)));
625: }
626:
627: if (!is_null($association) && (isset($model->hasMany[$association]) || isset($model->__backAssociation['hasMany'][$association]))) {
628: $associations[] = $association;
629: }
630: }
631:
632: if (!empty($associations)) {
633: $model->unbindModel(array('hasMany' => $associations), false);
634: }
635: return true;
636: }
637: }
638: if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
639:
640: 641: 642: 643:
644: class I18nModel extends AppModel {
645: var $name = 'I18nModel';
646: var $useTable = 'i18n';
647: var $displayField = 'field';
648: }
649: }
650: