Skip to content
This repository
Browse code

Fix issue with find(count) and translated conditions.

Because count queries did not have joins created for the translated
fields pagination would generate invalid queries. Checking the conditions
for translated fields and adding in the correct joins solves that.
Extract what would have been duplicated code into methods.

Add a few protected properties to keep method signatures sane. The code
could be even simpler if the existing find(count) join was removed.

Fixes #2349
  • Loading branch information...
commit f9bcc950a18b6e033ef4f30c1751162e23b4c000 1 parent 049b04d
Mark Story authored January 08, 2012
136  cake/libs/model/behaviors/translate.php
@@ -35,6 +35,20 @@ class TranslateBehavior extends ModelBehavior {
35 35
 	var $runtime = array();
36 36
 
37 37
 /**
  38
+ * Stores the joinTable object for generating joins.
  39
+ *
  40
+ * @var object
  41
+ */
  42
+	var $_joinTable;
  43
+
  44
+/**
  45
+ * Stores the runtime model for generating joins.
  46
+ *
  47
+ * @var Model
  48
+ */
  49
+	var $_runtimeModel;
  50
+
  51
+/**
38 52
  * Callback
39 53
  *
40 54
  * $config for TranslateBehavior should be
@@ -96,6 +110,7 @@ function beforeFind(&$model, $query) {
96 110
 		}
97 111
 		$db =& ConnectionManager::getDataSource($model->useDbConfig);
98 112
 		$RuntimeModel =& $this->translateModel($model);
  113
+	
99 114
 		if (!empty($RuntimeModel->tablePrefix)) {
100 115
 			$tablePrefix = $RuntimeModel->tablePrefix;
101 116
 		} else {
@@ -105,8 +120,12 @@ function beforeFind(&$model, $query) {
105 120
 		$joinTable->tablePrefix = $tablePrefix;
106 121
 		$joinTable->table = $RuntimeModel->table;
107 122
 
108  
-		if (is_string($query['fields']) && 'COUNT(*) AS '.$db->name('count') == $query['fields']) {
  123
+		$this->_joinTable = $joinTable;
  124
+		$this->_runtimeModel = $RuntimeModel;
  125
+
  126
+		if (is_string($query['fields']) && 'COUNT(*) AS ' . $db->name('count') == $query['fields']) {
109 127
 			$query['fields'] = 'COUNT(DISTINCT('.$db->name($model->alias . '.' . $model->primaryKey) . ')) ' . $db->alias . 'count';
  128
+
110 129
 			$query['joins'][] = array(
111 130
 				'type' => 'INNER',
112 131
 				'alias' => $RuntimeModel->alias,
@@ -117,6 +136,11 @@ function beforeFind(&$model, $query) {
117 136
 					$RuntimeModel->alias.'.locale' => $locale
118 137
 				)
119 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);
120 144
 			return $query;
121 145
 		}
122 146
 		$autoFields = false;
@@ -150,7 +174,6 @@ function beforeFind(&$model, $query) {
150 174
 		if (is_array($query['fields'])) {
151 175
 			foreach ($fields as $key => $value) {
152 176
 				$field = (is_numeric($key)) ? $value : $key;
153  
-
154 177
 				if (in_array($model->alias.'.*', $query['fields']) || $autoFields || in_array($model->alias.'.'.$field, $query['fields']) || in_array($field, $query['fields'])) {
155 178
 					$addFields[] = $field;
156 179
 				}
@@ -166,39 +189,90 @@ function beforeFind(&$model, $query) {
166 189
 						unset($query['fields'][$key]);
167 190
 					}
168 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
+	}
169 199
 
170  
-				if (is_array($locale)) {
171  
-					foreach ($locale as $_locale) {
172  
-						$query['fields'][] = 'I18n__'.$field.'__'.$_locale.'.content';
173  
-						$query['joins'][] = array(
174  
-							'type' => 'LEFT',
175  
-							'alias' => 'I18n__'.$field.'__'.$_locale,
176  
-							'table' => $joinTable,
177  
-							'conditions' => array(
178  
-								$model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}__{$_locale}.foreign_key"),
179  
-								'I18n__'.$field.'__'.$_locale.'.model' => $model->name,
180  
-								'I18n__'.$field.'__'.$_locale.'.'.$RuntimeModel->displayField => $field,
181  
-								'I18n__'.$field.'__'.$_locale.'.locale' => $_locale
182  
-							)
183  
-						);
184  
-					}
185  
-				} else {
186  
-					$query['fields'][] = 'I18n__'.$field.'.content';
187  
-					$query['joins'][] = array(
188  
-						'type' => 'INNER',
189  
-						'alias' => 'I18n__'.$field,
190  
-						'table' => $joinTable,
191  
-						'conditions' => array(
192  
-							$model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}.foreign_key"),
193  
-							'I18n__'.$field.'.model' => $model->name,
194  
-							'I18n__'.$field.'.'.$RuntimeModel->displayField => $field,
195  
-							'I18n__'.$field.'.locale' => $locale
196  
-						)
197  
-					);
  200
+/**
  201
+ * Check a query's conditions for translated fields.
  202
+ * Return an array of translated fields found in the conditions.
  203
+ *
  204
+ * @param Model $model The model being read.
  205
+ * @param array $query The query array.
  206
+ * @return array The list of translated fields that are in the conditions.
  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;
198 220
 				}
199 221
 			}
200 222
 		}
201  
-		$this->runtime[$model->alias]['beforeFind'] = $addFields;
  223
+		return $conditionFields;
  224
+	}
  225
+
  226
+/**
  227
+ * Appends a join for translated fields and possibly a field.
  228
+ *
  229
+ * @param Model $model The model being worked on.
  230
+ * @param object $joinTable The jointable object.
  231
+ * @param array $query The query array to append a join to.
  232
+ * @param string $field The field name being joined.
  233
+ * @param mixed $locale The locale(s) having joins added.
  234
+ * @param boolean $addField Whether or not to add a field.
  235
+ * @return array The modfied query
  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
+		}
202 276
 		return $query;
203 277
 	}
204 278
 
19  cake/tests/cases/libs/model/behaviors/translate.test.php
@@ -63,6 +63,24 @@ function endTest() {
63 63
 	}
64 64
 
65 65
 /**
  66
+ * Test that count queries with conditions get the correct joins
  67
+ *
  68
+ * @return void
  69
+ */
  70
+	function testCountWithConditions() {
  71
+		$this->loadFixtures('Translate', 'TranslatedItem');
  72
+
  73
+		$Model =& new TranslatedItem();
  74
+		$Model->locale = 'eng';
  75
+		$result = $Model->find('count', array(
  76
+			'conditions' => array(
  77
+				'I18n__content.locale' => 'eng'
  78
+			)
  79
+		));
  80
+		$this->assertEqual(3, $result);
  81
+	}
  82
+
  83
+/**
66 84
  * testTranslateModel method
67 85
  *
68 86
  * @access public
@@ -895,4 +913,5 @@ function testUnbindTranslationInfinteLoop() {
895 913
 
896 914
 		$this->assertFalse($result);
897 915
 	}
  916
+
898 917
 }

0 notes on commit f9bcc95

Please sign in to comment.
Something went wrong with that request. Please try again.