translate.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: translate_8php-source.html 580 2008-07-01 14:45:49Z gwoo $ */
00003 /**
00004  * Short description for file.
00005  *
00006  * Long description for file
00007  *
00008  * PHP versions 4 and 5
00009  *
00010  * CakePHP(tm) :  Rapid Development Framework <http://www.cakephp.org/>
00011  * Copyright 2005-2008, Cake Software Foundation, Inc.
00012  *                              1785 E. Sahara Avenue, Suite 490-204
00013  *                              Las Vegas, Nevada 89104
00014  *
00015  * Licensed under The MIT License
00016  * Redistributions of files must retain the above copyright notice.
00017  *
00018  * @filesource
00019  * @copyright       Copyright 2005-2008, Cake Software Foundation, Inc.
00020  * @link                http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00021  * @package         cake
00022  * @subpackage      cake.cake.libs.model.behaviors
00023  * @since           CakePHP(tm) v 1.2.0.4525
00024  * @version         $Revision: 580 $
00025  * @modifiedby      $LastChangedBy: gwoo $
00026  * @lastmodified    $Date: 2008-07-01 09:45:49 -0500 (Tue, 01 Jul 2008) $
00027  * @license         http://www.opensource.org/licenses/mit-license.php The MIT License
00028  */
00029 /**
00030  * Short description for file.
00031  *
00032  * Long description for file
00033  *
00034  * @package     cake
00035  * @subpackage  cake.cake.libs.model.behaviors
00036  */
00037 class TranslateBehavior extends ModelBehavior {
00038 /**
00039  * Used for runtime configuration of model
00040  */
00041     var $runtime = array();
00042 /**
00043  * Callback
00044  *
00045  * $config for TranslateBehavior should be
00046  * array( 'fields' => array('field_one',
00047  * 'field_two' => 'FieldAssoc', 'field_three'))
00048  *
00049  * With above example only one permanent hasMany will be joined (for field_two
00050  * as FieldAssoc)
00051  *
00052  * $config could be empty - and translations configured dynamically by
00053  * bindTranslation() method
00054  */
00055     function setup(&$model, $config = array()) {
00056         $db =& ConnectionManager::getDataSource($model->useDbConfig);
00057         if (!$db->connected) {
00058             trigger_error('Datasource '.$model->useDbConfig.' for TranslateBehavior of model '.$model->alias.' is not connected', E_USER_ERROR);
00059             return false;
00060         }
00061 
00062         $this->settings[$model->alias] = array();
00063         $this->runtime[$model->alias] = array('fields' => array());
00064         $this->translateModel($model);
00065         return $this->bindTranslation($model, $config, false);
00066     }
00067 /**
00068  * Callback
00069  */
00070     function cleanup(&$model) {
00071         $this->unbindTranslation($model);
00072         unset($this->settings[$model->alias]);
00073         unset($this->runtime[$model->alias]);
00074     }
00075 /**
00076  * Callback
00077  */
00078     function beforeFind(&$model, $query) {
00079         $locale = $this->_getLocale($model);
00080         if (empty($locale)) {
00081             return $query;
00082         }
00083         $db =& ConnectionManager::getDataSource($model->useDbConfig);
00084         $tablePrefix = $db->config['prefix'];
00085         $RuntimeModel =& $this->translateModel($model);
00086 
00087         if (is_string($query['fields']) && 'COUNT(*) AS '.$db->name('count') == $query['fields']) {
00088             $query['fields'] = 'COUNT(DISTINCT('.$db->name($model->alias . '.' . $model->primaryKey) . ')) ' . $db->alias . 'count';
00089             $query['joins'][] = array(
00090                 'type' => 'INNER',
00091                 'alias' => $RuntimeModel->alias,
00092                 'table' => $db->name($tablePrefix . $RuntimeModel->useTable),
00093                 'conditions' => array(
00094                     $model->alias.'.id' => $db->identifier($RuntimeModel->alias.'.foreign_key'),
00095                     $RuntimeModel->alias.'.model' => $model->name,
00096                     $RuntimeModel->alias.'.locale' => $locale
00097                 )
00098             );
00099             return $query;
00100         }
00101         $autoFields = false;
00102 
00103         if (empty($query['fields'])) {
00104             $query['fields'] = array($model->alias.'.*');
00105 
00106             foreach (array('hasOne', 'belongsTo') as $type) {
00107                 foreach ($model->{$type} as $key => $value) {
00108 
00109                     if (empty($value['fields'])) {
00110                         $query['fields'][] = $key.'.*';
00111                     } else {
00112                         foreach ($value['fields'] as $field) {
00113                             $query['fields'][] = $key.'.'.$field;
00114                         }
00115                     }
00116                 }
00117             }
00118             $autoFields = true;
00119         }
00120         $fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']);
00121         $addFields = array();
00122         if (is_array($query['fields'])) {
00123             foreach ($fields as $key => $value) {
00124                 $field = ife(is_numeric($key), $value, $key);
00125 
00126                 if (in_array($model->alias.'.*', $query['fields']) || $autoFields || in_array($model->alias.'.'.$field, $query['fields']) || in_array($field, $query['fields'])) {
00127                     $addFields[] = $field;
00128                 }
00129             }
00130         }
00131 
00132         if ($addFields) {
00133             foreach ($addFields as $field) {
00134                 foreach (array($field, $model->alias.'.'.$field) as $_field) {
00135                     $key = array_search($_field, $query['fields']);
00136 
00137                     if ($key !== false) {
00138                         unset($query['fields'][$key]);
00139                     }
00140                 }
00141 
00142                 if (is_array($locale)) {
00143                     foreach ($locale as $_locale) {
00144                         $query['fields'][] = 'I18n__'.$field.'__'.$_locale.'.content';
00145                         $query['joins'][] = array(
00146                             'type' => 'LEFT',
00147                             'alias' => 'I18n__'.$field.'__'.$_locale,
00148                             'table' => $db->name($tablePrefix . $RuntimeModel->useTable),
00149                             'conditions' => array(
00150                                 $model->alias.'.id' => $db->identifier("I18n__{$field}__{$_locale}.foreign_key"),
00151                                 'I18n__'.$field.'__'.$_locale.'.model' => $model->name,
00152                                 'I18n__'.$field.'__'.$_locale.'.'.$RuntimeModel->displayField => $field,
00153                                 'I18n__'.$field.'__'.$_locale.'.locale' => $_locale
00154                             )
00155                         );
00156                     }
00157                 } else {
00158                     $query['fields'][] = 'I18n__'.$field.'.content';
00159                     $query['joins'][] = array(
00160                         'type' => 'LEFT',
00161                         'alias' => 'I18n__'.$field,
00162                         'table' => $db->name($tablePrefix . $RuntimeModel->useTable),
00163                         'conditions' => array(
00164                             $model->alias.'.id' => $db->identifier("I18n__{$field}.foreign_key"),
00165                             'I18n__'.$field.'.model' => $model->name,
00166                             'I18n__'.$field.'.'.$RuntimeModel->displayField => $field
00167                         )
00168                     );
00169 
00170                     if (is_string($query['conditions'])) {
00171                         $query['conditions'] = $db->conditions($query['conditions'], true, false, $model) . ' AND '.$db->name('I18n__'.$field.'.locale').' = \''.$locale.'\'';
00172                     } else {
00173                         $query['conditions'][$db->name("I18n__{$field}.locale")] = $locale;
00174                     }
00175                 }
00176             }
00177         }
00178         if (is_array($query['fields'])) {
00179             $query['fields'] = array_merge($query['fields']);
00180         }
00181         $this->runtime[$model->alias]['beforeFind'] = $addFields;
00182         return $query;
00183     }
00184 /**
00185  * Callback
00186  */
00187     function afterFind(&$model, $results, $primary) {
00188         $this->runtime[$model->alias]['fields'] = array();
00189         $locale = $this->_getLocale($model);
00190 
00191         if (empty($locale) || empty($results) || empty($this->runtime[$model->alias]['beforeFind'])) {
00192             return $results;
00193         }
00194         $beforeFind = $this->runtime[$model->alias]['beforeFind'];
00195 
00196         foreach ($results as $key => $row) {
00197             $results[$key][$model->alias]['locale'] = ife(is_array($locale), @$locale[0], $locale);
00198 
00199             foreach ($beforeFind as $field) {
00200                 if (is_array($locale)) {
00201                     foreach ($locale as $_locale) {
00202                         if (!isset($results[$key][$model->alias][$field]) && !empty($results[$key]['I18n__'.$field.'__'.$_locale]['content'])) {
00203                             $results[$key][$model->alias][$field] = $results[$key]['I18n__'.$field.'__'.$_locale]['content'];
00204                         }
00205                         unset($results[$key]['I18n__'.$field.'__'.$_locale]);
00206                     }
00207 
00208                     if (!isset($results[$key][$model->alias][$field])) {
00209                         $results[$key][$model->alias][$field] = '';
00210                     }
00211                 } else {
00212                     $value = ife(empty($results[$key]['I18n__'.$field]['content']), '', $results[$key]['I18n__'.$field]['content']);
00213                     $results[$key][$model->alias][$field] = $value;
00214                     unset($results[$key]['I18n__'.$field]);
00215                 }
00216             }
00217         }
00218         return $results;
00219     }
00220 /**
00221  * Callback
00222  */
00223     function beforeValidate(&$model) {
00224         $locale = $this->_getLocale($model);
00225         if (empty($locale)) {
00226             return true;
00227         }
00228         $fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']);
00229         $tempData = array();
00230 
00231         foreach ($fields as $key => $value) {
00232             $field = ife(is_numeric($key), $value, $key);
00233 
00234             if (isset($model->data[$model->alias][$field])) {
00235                 $tempData[$field] = $model->data[$model->alias][$field];
00236                 if (is_array($model->data[$model->alias][$field])) {
00237                     if (is_string($locale) && !empty($model->data[$model->alias][$field][$locale])) {
00238                         $model->data[$model->alias][$field] = $model->data[$model->alias][$field][$locale];
00239                     } else {
00240                         $values = array_values($model->data[$model->alias][$field]);
00241                         $model->data[$model->alias][$field] = $values[0];
00242                     }
00243                 }
00244             }
00245         }
00246         $this->runtime[$model->alias]['beforeSave'] = $tempData;
00247         return true;
00248     }
00249 /**
00250  * Callback
00251  */
00252     function afterSave(&$model, $created) {
00253         if (!isset($this->runtime[$model->alias]['beforeSave'])) {
00254             return true;
00255         }
00256         $locale = $this->_getLocale($model);
00257         $tempData = $this->runtime[$model->alias]['beforeSave'];
00258         unset($this->runtime[$model->alias]['beforeSave']);
00259         $conditions = array('model' => $model->alias, 'foreign_key' => $model->id);
00260         $RuntimeModel =& $this->translateModel($model);
00261 
00262         foreach ($tempData as $field => $value) {
00263             unset($conditions['content']);
00264             $conditions['field'] = $field;
00265             if (is_array($value)) {
00266                 $conditions['locale'] = array_keys($value);
00267             } else {
00268                 $conditions['locale'] = $locale;
00269                 if (is_array($locale)) {
00270                     $value = array($locale[0] => $value);
00271                 } else {
00272                     $value = array($locale => $value);
00273                 }
00274             }
00275             $translations = $RuntimeModel->find('list', array('conditions' => $conditions, 'fields' => array($RuntimeModel->alias . '.locale', $RuntimeModel->alias . '.id')));
00276             foreach ($value as $_locale => $_value) {
00277                 $RuntimeModel->create();
00278                 $conditions['locale'] = $_locale;
00279                 $conditions['content'] = $_value;
00280                 if (array_key_exists($_locale, $translations)) {
00281                     $RuntimeModel->save(array($RuntimeModel->alias => array_merge($conditions, array('id' => $translations[$_locale]))));
00282                 } else {
00283                     $RuntimeModel->save(array($RuntimeModel->alias => $conditions));
00284                 }
00285             }
00286         }
00287     }
00288 /**
00289  * Callback
00290  */
00291     function afterDelete(&$model) {
00292         $RuntimeModel =& $this->translateModel($model);
00293         $conditions = array('model' => $model->alias, 'foreign_key' => $model->id);
00294         $RuntimeModel->deleteAll($conditions);
00295     }
00296 /**
00297  * Get selected locale for model
00298  *
00299  * @return mixed string or false
00300  */
00301     function _getLocale(&$model) {
00302         if (!isset($model->locale) || is_null($model->locale)) {
00303             if (!class_exists('I18n')) {
00304                 App::import('Core', 'i18n');
00305             }
00306             $I18n =& I18n::getInstance();
00307             $model->locale = $I18n->l10n->locale;
00308         }
00309         return $model->locale;
00310     }
00311 /**
00312  * Get instance of model for translations
00313  *
00314  * @return object
00315  */
00316     function &translateModel(&$model) {
00317         if (!isset($this->runtime[$model->alias]['model'])) {
00318             if (!isset($model->translateModel) || empty($model->translateModel)) {
00319                 $className = 'I18nModel';
00320             } else {
00321                 $className = $model->translateModel;
00322             }
00323 
00324             if (PHP5) {
00325                 $this->runtime[$model->alias]['model'] = ClassRegistry::init($className, 'Model');
00326             } else {
00327                 $this->runtime[$model->alias]['model'] =& ClassRegistry::init($className, 'Model');
00328             }
00329         }
00330         $useTable = 'i18n';
00331 
00332         if (!empty($model->translateTable)) {
00333             $useTable = $model->translateTable;
00334         }
00335         if ($useTable !== $this->runtime[$model->alias]['model']->useTable) {
00336             $this->runtime[$model->alias]['model']->setSource($model->translateTable);
00337         }
00338         return $this->runtime[$model->alias]['model'];
00339     }
00340 /**
00341  * Bind translation for fields, optionally with hasMany association for
00342  * fake field
00343  *
00344  * @param object instance of model
00345  * @param mixed string with field or array(field1, field2=>AssocName, field3)
00346  * @param boolean $reset
00347  * @return bool
00348  */
00349     function bindTranslation(&$model, $fields, $reset = true) {
00350         if (is_string($fields)) {
00351             $fields = array($fields);
00352         }
00353         $associations = array();
00354         $RuntimeModel =& $this->translateModel($model);
00355         $default = array('className' => $RuntimeModel->alias, 'foreignKey' => 'foreign_key');
00356 
00357         foreach ($fields as $key => $value) {
00358             if (is_numeric($key)) {
00359                 $field = $value;
00360                 $association = null;
00361             } else {
00362                 $field = $key;
00363                 $association = $value;
00364             }
00365 
00366             if (array_key_exists($field, $this->settings[$model->alias])) {
00367                 unset($this->settings[$model->alias][$field]);
00368 
00369             } elseif (in_array($field, $this->settings[$model->alias])) {
00370                 $this->settings[$model->alias] = array_merge(array_diff_assoc($this->settings[$model->alias], array($field)));
00371             }
00372 
00373             if (array_key_exists($field, $this->runtime[$model->alias]['fields'])) {
00374                 unset($this->runtime[$model->alias]['fields'][$field]);
00375 
00376             } elseif (in_array($field, $this->runtime[$model->alias]['fields'])) {
00377                 $this->runtime[$model->alias]['fields'] = array_merge(array_diff_assoc($this->runtime[$model->alias]['fields'], array($field)));
00378             }
00379 
00380             if (is_null($association)) {
00381                 if ($reset) {
00382                     $this->runtime[$model->alias]['fields'][] = $field;
00383                 } else {
00384                     $this->settings[$model->alias][] = $field;
00385                 }
00386             } else {
00387 
00388                 if ($reset) {
00389                     $this->runtime[$model->alias]['fields'][$field] = $association;
00390                 } else {
00391                     $this->settings[$model->alias][$field] = $association;
00392                 }
00393 
00394                 foreach (array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany') as $type) {
00395                     if (isset($model->{$type}[$association]) || isset($model->__backAssociation[$type][$association])) {
00396                         trigger_error('Association '.$association.' is already binded to model '.$model->alias, E_USER_ERROR);
00397                         return false;
00398                     }
00399                 }
00400                 $associations[$association] = array_merge($default, array('conditions' => array(
00401                     'model' => $model->alias,
00402                     $RuntimeModel->displayField => $field
00403                 )));
00404             }
00405         }
00406 
00407         if (!empty($associations)) {
00408             $model->bindModel(array('hasMany' => $associations), $reset);
00409         }
00410         return true;
00411     }
00412 /**
00413  * Unbind translation for fields, optionally unbinds hasMany association for
00414  * fake field
00415  *
00416  * @param object instance of model
00417  * @param mixed string with field, or array(field1, field2=>AssocName, field3), or null for unbind all original translations
00418  * @return bool
00419  */
00420     function unbindTranslation(&$model, $fields = null) {
00421         if (empty($fields)) {
00422             return $this->unbindTranslation($model, $this->settings[$model->alias]);
00423         }
00424 
00425         if (is_string($fields)) {
00426             $fields = array($fields);
00427         }
00428         $RuntimeModel =& $this->translateModel($model);
00429         $default = array('className' => $RuntimeModel->alias, 'foreignKey' => 'foreign_key');
00430         $associations = array();
00431 
00432         foreach ($fields as $key => $value) {
00433             if (is_numeric($key)) {
00434                 $field = $value;
00435                 $association = null;
00436             } else {
00437                 $field = $key;
00438                 $association = $value;
00439             }
00440 
00441             if (array_key_exists($field, $this->settings[$model->alias])) {
00442                 unset($this->settings[$model->alias][$field]);
00443 
00444             } elseif (in_array($field, $this->settings[$model->alias])) {
00445                 $this->settings[$model->alias] = array_merge(array_diff_assoc($this->settings[$model->alias], array($field)));
00446             }
00447 
00448             if (array_key_exists($field, $this->runtime[$model->alias]['fields'])) {
00449                 unset($this->runtime[$model->alias]['fields'][$field]);
00450 
00451             } elseif (in_array($field, $this->runtime[$model->alias]['fields'])) {
00452                 $this->runtime[$model->alias]['fields'] = array_merge(array_diff_assoc($this->runtime[$model->alias]['fields'], array($field)));
00453             }
00454 
00455             if (!is_null($association) && (isset($model->hasMany[$association]) || isset($model->__backAssociation['hasMany'][$association]))) {
00456                 $associations[] = $association;
00457             }
00458         }
00459 
00460         if (!empty($associations)) {
00461             $model->unbindModel(array('hasMany' => $associations), false);
00462         }
00463         return true;
00464     }
00465 }
00466 if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
00467 /**
00468  * @package     cake
00469  * @subpackage  cake.cake.libs.model.behaviors
00470  */
00471     class I18nModel extends AppModel {
00472         var $name = 'I18nModel';
00473         var $useTable = 'i18n';
00474         var $displayField = 'field';
00475     }
00476 }
00477 ?>