Subversion-Projekte lars-tiefland.cakephp

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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