00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 App::import('Core', 'Set');
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 class DboSource extends DataSource {
00040
00041
00042
00043
00044
00045 var $description = "Database Data Source";
00046
00047
00048
00049
00050
00051 var $index = array('PRI' => 'primary', 'MUL' => 'index', 'UNI' => 'unique');
00052
00053
00054
00055
00056
00057 var $startQuote = null;
00058
00059
00060
00061
00062
00063 var $endQuote = null;
00064
00065
00066
00067
00068
00069 var $alias = 'AS ';
00070
00071
00072
00073
00074
00075 var $fieldCache = array();
00076
00077
00078
00079
00080
00081 var $__bypass = false;
00082
00083
00084
00085
00086
00087 var $__sqlOps = array('like', 'ilike', 'or', 'not', 'in', 'between', 'regexp', 'similar to');
00088
00089
00090
00091
00092
00093
00094 var $_commands = array(
00095 'begin' => 'BEGIN',
00096 'commit' => 'COMMIT',
00097 'rollback' => 'ROLLBACK'
00098 );
00099
00100
00101
00102 function __construct($config = null, $autoConnect = true) {
00103 if (!isset($config['prefix'])) {
00104 $config['prefix'] = '';
00105 }
00106 parent::__construct($config);
00107 $this->fullDebug = Configure::read() > 1;
00108
00109 if ($autoConnect) {
00110 return $this->connect();
00111 } else {
00112 return true;
00113 }
00114 }
00115
00116
00117
00118
00119
00120
00121 function reconnect($config = null) {
00122 $this->disconnect();
00123 if ($config != null) {
00124 $this->config = array_merge($this->_baseConfig, $config);
00125 }
00126 return $this->connect();
00127 }
00128
00129
00130
00131
00132
00133
00134 function value($data, $column = null) {
00135 if (is_array($data) && !empty($data)) {
00136 return array_map(array(&$this, 'value'), $data, array_fill(0, count($data), $column));
00137 } elseif (is_object($data)) {
00138 if (isset($data->type) && $data->type == 'identifier') {
00139 return $this->name($data->value);
00140 }
00141 } elseif (in_array($data, array('{$__cakeID__$}', '{$__cakeForeignKey__$}'), true)) {
00142 return $data;
00143 } else {
00144 return null;
00145 }
00146 }
00147
00148
00149
00150
00151
00152
00153 function identifier($identifier) {
00154 $obj = new stdClass();
00155 $obj->type = 'identifier';
00156 $obj->value = $identifier;
00157 return $obj;
00158 }
00159
00160
00161
00162
00163
00164
00165 function expression($expression) {
00166 $obj = new stdClass();
00167 $obj->type = 'expression';
00168 $obj->value = $expression;
00169 return $obj;
00170 }
00171
00172
00173
00174
00175
00176
00177 function rawQuery($sql) {
00178 $this->took = $this->error = $this->numRows = false;
00179 return $this->execute($sql);
00180 }
00181
00182
00183
00184
00185
00186
00187
00188
00189 function execute($sql) {
00190 $t = getMicrotime();
00191 $this->_result = $this->_execute($sql);
00192 $this->took = round((getMicrotime() - $t) * 1000, 0);
00193 $this->affected = $this->lastAffected();
00194 $this->error = $this->lastError();
00195 $this->numRows = $this->lastNumRows();
00196
00197 if (Configure::read() > 1) {
00198 $this->logQuery($sql);
00199 }
00200
00201 if ($this->error) {
00202 $this->showQuery($sql);
00203 return false;
00204 } else {
00205 return $this->_result;
00206 }
00207 }
00208
00209
00210
00211
00212
00213 function query() {
00214 $args = func_get_args();
00215 $fields = null;
00216 $order = null;
00217 $limit = null;
00218 $page = null;
00219 $recursive = null;
00220
00221 if (count($args) == 1) {
00222 return $this->fetchAll($args[0]);
00223
00224 } elseif (count($args) > 1 && (strpos(strtolower($args[0]), 'findby') === 0 || strpos(strtolower($args[0]), 'findallby') === 0)) {
00225 $params = $args[1];
00226
00227 if (strpos(strtolower($args[0]), 'findby') === 0) {
00228 $all = false;
00229 $field = Inflector::underscore(preg_replace('/findBy/i', '', $args[0]));
00230 } else {
00231 $all = true;
00232 $field = Inflector::underscore(preg_replace('/findAllBy/i', '', $args[0]));
00233 }
00234
00235 $or = (strpos($field, '_or_') !== false);
00236 if ($or) {
00237 $field = explode('_or_', $field);
00238 } else {
00239 $field = explode('_and_', $field);
00240 }
00241 $off = count($field) - 1;
00242
00243 if (isset($params[1 + $off])) {
00244 $fields = $params[1 + $off];
00245 }
00246
00247 if (isset($params[2 + $off])) {
00248 $order = $params[2 + $off];
00249 }
00250
00251 if (!array_key_exists(0, $params)) {
00252 return false;
00253 }
00254
00255 $c = 0;
00256 $query = array();
00257 foreach ($field as $f) {
00258 $query[$args[2]->alias . '.' . $f] = $params[$c];
00259 $c++;
00260 }
00261
00262 if ($or) {
00263 $query = array('OR' => $query);
00264 }
00265
00266 if ($all) {
00267
00268 if (isset($params[3 + $off])) {
00269 $limit = $params[3 + $off];
00270 }
00271
00272 if (isset($params[4 + $off])) {
00273 $page = $params[4 + $off];
00274 }
00275
00276 if (isset($params[5 + $off])) {
00277 $recursive = $params[5 + $off];
00278 }
00279 return $args[2]->findAll($query, $fields, $order, $limit, $page, $recursive);
00280 } else {
00281 if (isset($params[3 + $off])) {
00282 $recursive = $params[3 + $off];
00283 }
00284 return $args[2]->find($query, $fields, $order, $recursive);
00285 }
00286 } else {
00287 if (isset($args[1]) && $args[1] === true) {
00288 return $this->fetchAll($args[0], true);
00289 } else if (isset($args[1]) && !is_array($args[1]) ) {
00290 return $this->fetchAll($args[0], false);
00291 } else if (isset($args[1]) && is_array($args[1])) {
00292 $offset = 0;
00293 if (isset($args[2])) {
00294 $cache = $args[2];
00295 } else {
00296 $cache = true;
00297 }
00298 $args[1] = array_map(array(&$this, 'value'), $args[1]);
00299 return $this->fetchAll(String::insert($args[0], $args[1]), $cache);
00300 }
00301 }
00302 }
00303
00304
00305
00306
00307
00308 function fetchRow($sql = null) {
00309 if (!empty($sql) && is_string($sql) && strlen($sql) > 5) {
00310 if (!$this->execute($sql)) {
00311 return null;
00312 }
00313 }
00314
00315 if ($this->hasResult()) {
00316 $this->resultSet($this->_result);
00317 $resultRow = $this->fetchResult();
00318 return $resultRow;
00319 } else {
00320 return null;
00321 }
00322 }
00323
00324
00325
00326
00327
00328
00329
00330
00331 function fetchAll($sql, $cache = true, $modelName = null) {
00332 if ($cache && isset($this->_queryCache[$sql])) {
00333 if (preg_match('/^\s*select/i', $sql)) {
00334 return $this->_queryCache[$sql];
00335 }
00336 }
00337
00338 if ($this->execute($sql)) {
00339 $out = array();
00340
00341 $first = $this->fetchRow();
00342 if ($first != null){
00343 $out[] = $first;
00344 }
00345 while ($this->hasResult() && $item = $this->fetchResult()) {
00346 $out[] = $item;
00347 }
00348
00349 if ($cache) {
00350 if (strpos(trim(strtolower($sql)), 'select') !== false) {
00351 $this->_queryCache[$sql] = $out;
00352 }
00353 }
00354 return $out;
00355
00356 } else {
00357 return false;
00358 }
00359 }
00360
00361
00362
00363
00364
00365
00366
00367 function field($name, $sql) {
00368 $data = $this->fetchRow($sql);
00369
00370 if (!isset($data[$name]) || empty($data[$name])) {
00371 return false;
00372 } else {
00373 return $data[$name];
00374 }
00375 }
00376
00377
00378
00379
00380
00381
00382
00383 function name($data) {
00384 if ($data == '*') {
00385 return '*';
00386 }
00387 $array = is_array($data);
00388
00389 $data = (array)$data;
00390 $count = count($data);
00391
00392 for($i = 0; $i < $count; $i++) {
00393 if ($data[$i] == '*') {
00394 continue;
00395 }
00396 if (strpos($data[$i], '(') !== false && preg_match_all('/([^(]*)\((.*)\)(.*)/', $data[$i], $fields)) {
00397 $fields = Set::extract($fields, '{n}.0');
00398 if (!empty($fields[1])) {
00399 if (!empty($fields[2])) {
00400 $data[$i] = $fields[1] . '(' . $this->name($fields[2]) . ')' . $fields[3];
00401 } else {
00402 $data[$i] = $fields[1] . '()' . $fields[3];
00403 }
00404 }
00405 }
00406 $data[$i] = $this->startQuote . str_replace('.', $this->endQuote . '.' . $this->startQuote, $data[$i]) . $this->endQuote;
00407 $data[$i] = str_replace($this->startQuote . $this->startQuote, $this->startQuote, $data[$i]);
00408 if (strpos($data[$i], ' AS ')) {
00409 $data[$i] = str_replace(' AS ', $this->endQuote . ' AS ' . $this->startQuote, $data[$i]);
00410 }
00411
00412 if (!empty($this->endQuote) && $this->endQuote == $this->startQuote) {
00413 if (substr_count($data[$i], $this->endQuote) % 2 == 1) {
00414 $data[$i] = trim($data[$i], $this->endQuote);
00415 }
00416 }
00417 if (strpos($data[$i], '*')) {
00418 $data[$i] = str_replace($this->endQuote . '*' . $this->endQuote, '*', $data[$i]);
00419 }
00420 $data[$i] = str_replace($this->endQuote . $this->endQuote, $this->endQuote, $data[$i]);
00421 }
00422 if (!$array) {
00423 return $data[0];
00424 }
00425 return $data;
00426 }
00427
00428
00429
00430
00431
00432 function isConnected() {
00433 return $this->connected;
00434 }
00435
00436
00437
00438
00439
00440 function hasResult() {
00441 return is_resource($this->_result);
00442 }
00443
00444
00445
00446
00447
00448 function showLog($sorted = false) {
00449 if ($sorted) {
00450 $log = sortByKey($this->_queriesLog, 'took', 'desc', SORT_NUMERIC);
00451 } else {
00452 $log = $this->_queriesLog;
00453 }
00454
00455 if ($this->_queriesCnt > 1) {
00456 $text = 'queries';
00457 } else {
00458 $text = 'query';
00459 }
00460
00461 if (php_sapi_name() != 'cli') {
00462 print ("<table class=\"cake-sql-log\" id=\"cakeSqlLog_" . preg_replace('/[^A-Za-z0-9_]/', '_', uniqid(time(), true)) . "\" summary=\"Cake SQL Log\" cellspacing=\"0\" border = \"0\">\n<caption>{$this->_queriesCnt} {$text} took {$this->_queriesTime} ms</caption>\n");
00463 print ("<thead>\n<tr><th>Nr</th><th>Query</th><th>Error</th><th>Affected</th><th>Num. rows</th><th>Took (ms)</th></tr>\n</thead>\n<tbody>\n");
00464
00465 foreach ($log as $k => $i) {
00466 print ("<tr><td>" . ($k + 1) . "</td><td>" . h($i['query']) . "</td><td>{$i['error']}</td><td style = \"text-align: right\">{$i['affected']}</td><td style = \"text-align: right\">{$i['numRows']}</td><td style = \"text-align: right\">{$i['took']}</td></tr>\n");
00467 }
00468 print ("</tbody></table>\n");
00469 } else {
00470 foreach ($log as $k => $i) {
00471 print (($k + 1) . ". {$i['query']} {$i['error']}\n");
00472 }
00473 }
00474 }
00475
00476
00477
00478
00479
00480
00481 function logQuery($sql) {
00482 $this->_queriesCnt++;
00483 $this->_queriesTime += $this->took;
00484 $this->_queriesLog[] = array(
00485 'query' => $sql,
00486 'error' => $this->error,
00487 'affected' => $this->affected,
00488 'numRows' => $this->numRows,
00489 'took' => $this->took
00490 );
00491 if (count($this->_queriesLog) > $this->_queriesLogMax) {
00492 array_pop($this->_queriesLog);
00493 }
00494 if ($this->error) {
00495 return false;
00496 }
00497 }
00498
00499
00500
00501
00502
00503
00504 function showQuery($sql) {
00505 $error = $this->error;
00506 if (strlen($sql) > 200 && !$this->fullDebug && Configure::read() > 1) {
00507 $sql = substr($sql, 0, 200) . '[...]';
00508 }
00509 if ($error && Configure::read() > 0) {
00510 $out = null;
00511 if ($error) {
00512 trigger_error("<span style = \"color:Red;text-align:left\"><b>SQL Error:</b> {$this->error}</span>", E_USER_WARNING);
00513 } else {
00514 $out = ("<small>[Aff:{$this->affected} Num:{$this->numRows} Took:{$this->took}ms]</small>");
00515 }
00516 e(sprintf("<p style = \"text-align:left\"><b>Query:</b> %s %s</p>", $sql, $out));
00517 }
00518 }
00519
00520
00521
00522
00523
00524
00525
00526 function fullTableName($model, $quote = true) {
00527 if (is_object($model)) {
00528 $table = $model->tablePrefix . $model->table;
00529 } elseif (isset($this->config['prefix'])) {
00530 $table = $this->config['prefix'] . strval($model);
00531 } else {
00532 $table = strval($model);
00533 }
00534 if ($quote) {
00535 return $this->name($table);
00536 }
00537 return $table;
00538 }
00539
00540
00541
00542
00543
00544
00545
00546
00547 function create(&$model, $fields = null, $values = null) {
00548 $id = null;
00549
00550 if ($fields == null) {
00551 unset($fields, $values);
00552 $fields = array_keys($model->data);
00553 $values = array_values($model->data);
00554 }
00555 $count = count($fields);
00556
00557 for ($i = 0; $i < $count; $i++) {
00558 $valueInsert[] = $this->value($values[$i], $model->getColumnType($fields[$i]));
00559 }
00560 for ($i = 0; $i < $count; $i++) {
00561 $fieldInsert[] = $this->name($fields[$i]);
00562 if ($fields[$i] == $model->primaryKey) {
00563 $id = $values[$i];
00564 }
00565 }
00566
00567 if ($this->execute('INSERT INTO ' . $this->fullTableName($model) . ' (' . join(',', $fieldInsert). ') VALUES (' . join(',', $valueInsert) . ')')) {
00568 if (empty($id)) {
00569 $id = $this->lastInsertId($this->fullTableName($model, false), $model->primaryKey);
00570 }
00571 $model->setInsertID($id);
00572 $model->id = $id;
00573 return true;
00574 } else {
00575 $model->onError();
00576 return false;
00577 }
00578 }
00579
00580
00581
00582
00583
00584
00585
00586
00587 function read(&$model, $queryData = array(), $recursive = null) {
00588 $queryData = $this->__scrubQueryData($queryData);
00589 $null = null;
00590 $array = array();
00591 $linkedModels = array();
00592 $this->__bypass = false;
00593 $this->__booleans = array();
00594
00595 if ($recursive === null && isset($queryData['recursive'])) {
00596 $recursive = $queryData['recursive'];
00597 }
00598
00599 if (!is_null($recursive)) {
00600 $_recursive = $model->recursive;
00601 $model->recursive = $recursive;
00602 }
00603
00604 if (!empty($queryData['fields'])) {
00605 $this->__bypass = true;
00606 $queryData['fields'] = $this->fields($model, null, $queryData['fields']);
00607 } else {
00608 $queryData['fields'] = $this->fields($model);
00609 }
00610
00611 foreach ($model->__associations as $type) {
00612 foreach ($model->{$type} as $assoc => $assocData) {
00613 if ($model->recursive > -1) {
00614 $linkModel =& $model->{$assoc};
00615 $external = isset($assocData['external']);
00616
00617 if ($model->useDbConfig == $linkModel->useDbConfig) {
00618 if (true === $this->generateAssociationQuery($model, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null)) {
00619 $linkedModels[] = $type . '/' . $assoc;
00620 }
00621 }
00622 }
00623 }
00624 }
00625
00626 $query = $this->generateAssociationQuery($model, $null, null, null, null, $queryData, false, $null);
00627 $resultSet = $this->fetchAll($query, $model->cacheQueries, $model->alias);
00628
00629 if ($resultSet === false) {
00630 $model->onError();
00631 return false;
00632 }
00633
00634 $filtered = $this->__filterResults($resultSet, $model);
00635
00636 if ($model->recursive > 0) {
00637 foreach ($model->__associations as $type) {
00638 foreach ($model->{$type} as $assoc => $assocData) {
00639 $linkModel =& $model->{$assoc};
00640
00641 if (!in_array($type . '/' . $assoc, $linkedModels)) {
00642 if ($model->useDbConfig == $linkModel->useDbConfig) {
00643 $db =& $this;
00644 } else {
00645 $db =& ConnectionManager::getDataSource($linkModel->useDbConfig);
00646 }
00647 } elseif ($model->recursive > 1 && ($type == 'belongsTo' || $type == 'hasOne')) {
00648 $db =& $this;
00649 }
00650
00651 if (isset($db)) {
00652 $stack = array($assoc);
00653 $db->queryAssociation($model, $linkModel, $type, $assoc, $assocData, $array, true, $resultSet, $model->recursive - 1, $stack);
00654 unset($db);
00655 }
00656 }
00657 }
00658 $this->__filterResults($resultSet, $model, $filtered);
00659 }
00660
00661 if (!is_null($recursive)) {
00662 $model->recursive = $_recursive;
00663 }
00664 return $resultSet;
00665 }
00666
00667
00668
00669
00670
00671
00672
00673
00674 function __filterResults(&$results, &$model, $filtered = array()) {
00675 $filtering = array();
00676 $count = count($results);
00677
00678 for ($i = 0; $i < $count; $i++) {
00679 if (is_array($results[$i])) {
00680 $classNames = array_keys($results[$i]);
00681 $count2 = count($classNames);
00682
00683 for ($j = 0; $j < $count2; $j++) {
00684 $className = $classNames[$j];
00685
00686 if ($model->alias != $className && !in_array($className, $filtered)) {
00687 if (!in_array($className, $filtering)) {
00688 $filtering[] = $className;
00689 }
00690
00691 if (isset($model->{$className}) && is_object($model->{$className})) {
00692 $data = $model->{$className}->afterFind(array(array($className => $results[$i][$className])), false);
00693 }
00694 if (isset($data[0][$className])) {
00695 $results[$i][$className] = $data[0][$className];
00696 }
00697 }
00698 }
00699 }
00700 }
00701 return $filtering;
00702 }
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717 function queryAssociation(&$model, &$linkModel, $type, $association, $assocData, &$queryData, $external = false, &$resultSet, $recursive, $stack) {
00718 if ($query = $this->generateAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet)) {
00719 if (!isset($resultSet) || !is_array($resultSet)) {
00720 if (Configure::read() > 0) {
00721 e('<div style = "font: Verdana bold 12px; color: #FF0000">' . sprintf(__('SQL Error in model %s:', true), $model->alias) . ' ');
00722 if (isset($this->error) && $this->error != null) {
00723 e($this->error);
00724 }
00725 e('</div>');
00726 }
00727 return null;
00728 }
00729 $count = count($resultSet);
00730
00731 if ($type === 'hasMany' && empty($assocData['limit']) && !empty($assocData['foreignKey'])) {
00732 $ins = $fetch = array();
00733 for ($i = 0; $i < $count; $i++) {
00734 if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) {
00735 $ins[] = $in;
00736 }
00737 }
00738
00739 if (!empty($ins)) {
00740 $fetch = $this->fetchAssociated($model, $query, $ins);
00741 }
00742
00743 if (!empty($fetch) && is_array($fetch)) {
00744 if ($recursive > 0) {
00745 foreach ($linkModel->__associations as $type1) {
00746 foreach ($linkModel->{$type1} as $assoc1 => $assocData1) {
00747 $deepModel =& $linkModel->{$assoc1};
00748 $tmpStack = $stack;
00749 $tmpStack[] = $assoc1;
00750
00751 if ($linkModel->useDbConfig === $deepModel->useDbConfig) {
00752 $db =& $this;
00753 } else {
00754 $db =& ConnectionManager::getDataSource($deepModel->useDbConfig);
00755 }
00756 $db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack);
00757 }
00758 }
00759 }
00760 }
00761 return $this->__mergeHasMany($resultSet, $fetch, $association, $model, $linkModel, $recursive);
00762 } elseif ($type === 'hasAndBelongsToMany') {
00763 $ins = $fetch = array();
00764 for ($i = 0; $i < $count; $i++) {
00765 if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) {
00766 $ins[] = $in;
00767 }
00768 }
00769 if (!empty($ins)) {
00770 if (count($ins) > 1) {
00771 $query = str_replace('{$__cakeID__$}', '(' .join(', ', $ins) .')', $query);
00772 $query = str_replace('= (', 'IN (', $query);
00773 $query = str_replace('= (', 'IN (', $query);
00774 } else {
00775 $query = str_replace('{$__cakeID__$}',$ins[0], $query);
00776 }
00777
00778 $query = str_replace(' WHERE 1 = 1', '', $query);
00779 }
00780
00781 $foreignKey = $model->hasAndBelongsToMany[$association]['foreignKey'];
00782 $joinKeys = array($foreignKey, $model->hasAndBelongsToMany[$association]['associationForeignKey']);
00783 list($with, $habtmFields) = $model->joinModel($model->hasAndBelongsToMany[$association]['with'], $joinKeys);
00784 $habtmFieldsCount = count($habtmFields);
00785 $q = $this->insertQueryData($query, null, $association, $assocData, $model, $linkModel, $stack);
00786
00787 if ($q != false) {
00788 $fetch = $this->fetchAll($q, $model->cacheQueries, $model->alias);
00789 } else {
00790 $fetch = null;
00791 }
00792 }
00793
00794 for ($i = 0; $i < $count; $i++) {
00795 $row =& $resultSet[$i];
00796
00797 if ($type !== 'hasAndBelongsToMany') {
00798 $q = $this->insertQueryData($query, $resultSet[$i], $association, $assocData, $model, $linkModel, $stack);
00799 if ($q != false) {
00800 $fetch = $this->fetchAll($q, $model->cacheQueries, $model->alias);
00801 } else {
00802 $fetch = null;
00803 }
00804 }
00805 $selfJoin = false;
00806
00807 if ($linkModel->name === $model->name) {
00808 $selfJoin = true;
00809 }
00810
00811 if (!empty($fetch) && is_array($fetch)) {
00812 if ($recursive > 0) {
00813 foreach ($linkModel->__associations as $type1) {
00814 foreach ($linkModel->{$type1} as $assoc1 => $assocData1) {
00815 $deepModel =& $linkModel->{$assoc1};
00816
00817 if (($type1 === 'belongsTo') || ($deepModel->alias === $model->alias && $type === 'belongsTo') || ($deepModel->alias != $model->alias)) {
00818 $tmpStack = $stack;
00819 $tmpStack[] = $assoc1;
00820 if ($linkModel->useDbConfig == $deepModel->useDbConfig) {
00821 $db =& $this;
00822 } else {
00823 $db =& ConnectionManager::getDataSource($deepModel->useDbConfig);
00824 }
00825 $db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack);
00826 }
00827 }
00828 }
00829 }
00830 if ($type == 'hasAndBelongsToMany') {
00831 $uniqueIds = $merge = array();
00832
00833 foreach($fetch as $j => $data) {
00834 if (
00835 (isset($data[$with]) && $data[$with][$foreignKey] === $row[$model->alias][$model->primaryKey]) &&
00836 (!in_array($data[$with][$joinKeys[1]], $uniqueIds))
00837 ) {
00838 $uniqueIds[] = $data[$with][$joinKeys[1]];
00839
00840 if ($habtmFieldsCount <= 2) {
00841 unset($data[$with]);
00842 }
00843 $merge[] = $data;
00844 }
00845 }
00846 if (empty($merge) && !isset($row[$association])) {
00847 $row[$association] = $merge;
00848 } else {
00849 $this->__mergeAssociation($resultSet[$i], $merge, $association, $type);
00850 }
00851 } else {
00852 $this->__mergeAssociation($resultSet[$i], $fetch, $association, $type, $selfJoin);
00853 }
00854 $resultSet[$i][$association] = $linkModel->afterfind($resultSet[$i][$association]);
00855
00856 } else {
00857 $tempArray[0][$association] = false;
00858 $this->__mergeAssociation($resultSet[$i], $tempArray, $association, $type, $selfJoin);
00859 }
00860 }
00861 }
00862 }
00863
00864
00865
00866
00867
00868
00869
00870
00871 function fetchAssociated($model, $query, $ids) {
00872 $query = str_replace('{$__cakeID__$}', join(', ', $ids), $query);
00873 if (count($ids) > 1) {
00874 $query = str_replace('= (', 'IN (', $query);
00875 $query = str_replace('= (', 'IN (', $query);
00876 }
00877 return $this->fetchAll($query, $model->cacheQueries, $model->alias);
00878 }
00879
00880 function __mergeHasMany(&$resultSet, $merge, $association, &$model, &$linkModel) {
00881 foreach ($resultSet as $i => $value) {
00882 $count = 0;
00883 $merged[$association] = array();
00884 foreach ($merge as $j => $data) {
00885 if (isset($value[$model->alias]) && $value[$model->alias][$model->primaryKey] === $data[$association][$model->hasMany[$association]['foreignKey']]) {
00886 if (count($data) > 1) {
00887 $data = array_merge($data[$association], $data);
00888 unset($data[$association]);
00889 foreach ($data as $key => $name) {
00890 if (is_numeric($key)) {
00891 $data[$association][] = $name;
00892 unset($data[$key]);
00893 }
00894 }
00895 $merged[$association][] = $data;
00896 } else {
00897 $merged[$association][] = $data[$association];
00898 }
00899 }
00900 $count++;
00901 }
00902 if (isset($value[$model->alias])) {
00903 $resultSet[$i] = Set::pushDiff($resultSet[$i], $merged);
00904 unset($merged);
00905 }
00906 }
00907 }
00908
00909
00910
00911
00912
00913
00914
00915
00916
00917 function __mergeAssociation(&$data, $merge, $association, $type, $selfJoin = false) {
00918 if (isset($merge[0]) && !isset($merge[0][$association])) {
00919 $association = Inflector::pluralize($association);
00920 }
00921
00922 if ($type == 'belongsTo' || $type == 'hasOne') {
00923 if (isset($merge[$association])) {
00924 $data[$association] = $merge[$association][0];
00925 } else {
00926 if (count($merge[0][$association]) > 1) {
00927 foreach ($merge[0] as $assoc => $data2) {
00928 if ($assoc != $association) {
00929 $merge[0][$association][$assoc] = $data2;
00930 }
00931 }
00932 }
00933 if (!isset($data[$association])) {
00934 if ($merge[0][$association] != null) {
00935 $data[$association] = $merge[0][$association];
00936 } else {
00937 $data[$association] = array();
00938 }
00939 } else {
00940 if (is_array($merge[0][$association])) {
00941 foreach ($data[$association] as $k => $v) {
00942 if (!is_array($v)) {
00943 $dataAssocTmp[$k] = $v;
00944 }
00945 }
00946
00947 foreach ($merge[0][$association] as $k => $v) {
00948 if (!is_array($v)) {
00949 $mergeAssocTmp[$k] = $v;
00950 }
00951 }
00952 $dataKeys = array_keys($data);
00953 $mergeKeys = array_keys($merge[0]);
00954
00955 if ($mergeKeys[0] === $dataKeys[0] || $mergeKeys === $dataKeys) {
00956 $data[$association][$association] = $merge[0][$association];
00957 } else {
00958 $diff = Set::diff($dataAssocTmp, $mergeAssocTmp);
00959 $data[$association] = array_merge($merge[0][$association], $diff);
00960 }
00961 } elseif ($selfJoin && array_key_exists($association, $merge[0])) {
00962 $data[$association] = array_merge($data[$association], array($association => array()));
00963 }
00964 }
00965 }
00966 } else {
00967 if ($merge[0][$association] === false) {
00968 if (!isset($data[$association])) {
00969 $data[$association] = array();
00970 }
00971 } else {
00972 foreach ($merge as $i => $row) {
00973 if (count($row) == 1) {
00974 if (empty($data[$association]) || (isset($data[$association]) && !in_array($row[$association], $data[$association]))) {
00975 $data[$association][] = $row[$association];
00976 }
00977 } else {
00978 $tmp = array_merge($row[$association], $row);
00979 unset($tmp[$association]);
00980 $data[$association][] = $tmp;
00981 }
00982 }
00983 }
00984 }
00985 }
00986
00987
00988
00989
00990
00991
00992
00993
00994
00995
00996
00997
00998
00999 function generateAssociationQuery(&$model, &$linkModel, $type, $association = null, $assocData = array(), &$queryData, $external = false, &$resultSet) {
01000 $queryData = $this->__scrubQueryData($queryData);
01001 $assocData = $this->__scrubQueryData($assocData);
01002
01003 if (empty($queryData['fields'])) {
01004 $queryData['fields'] = $this->fields($model, $model->alias);
01005 } elseif (!empty($model->hasMany) && $model->recursive > -1) {
01006 $assocFields = $this->fields($model, $model->alias, array("{$model->alias}.{$model->primaryKey}"));
01007 $passedFields = $this->fields($model, $model->alias, $queryData['fields']);
01008
01009 if (count($passedFields) === 1) {
01010 $match = strpos($passedFields[0], $assocFields[0]);
01011 $match1 = strpos($passedFields[0], 'COUNT(');
01012 if ($match === false && $match1 === false) {
01013 $queryData['fields'] = array_merge($passedFields, $assocFields);
01014 } else {
01015 $queryData['fields'] = $passedFields;
01016 }
01017 } else {
01018 $queryData['fields'] = array_merge($passedFields, $assocFields);
01019 }
01020 unset($assocFields, $passedFields);
01021 }
01022
01023
01024
01025
01026
01027
01028
01029
01030
01031
01032
01033
01034
01035
01036
01037 if ($linkModel == null) {
01038 return $this->buildStatement(
01039 array(
01040 'fields' => array_unique($queryData['fields']),
01041 'table' => $this->fullTableName($model),
01042 'alias' => $model->alias,
01043 'limit' => $queryData['limit'],
01044 'offset' => $queryData['offset'],
01045 'joins' => $queryData['joins'],
01046 'conditions' => $queryData['conditions'],
01047 'order' => $queryData['order'],
01048 'group' => $queryData['group']
01049 ),
01050 $model
01051 );
01052 }
01053 if ($external && !empty($assocData['finderQuery'])) {
01054 return $assocData['finderQuery'];
01055 }
01056
01057 $alias = $association;
01058 $self = ($model->name == $linkModel->name);
01059 $fields = array();
01060
01061 if ((!$external && in_array($type, array('hasOne', 'belongsTo')) && $this->__bypass === false) || $external) {
01062 $fields = $this->fields($linkModel, $alias, $assocData['fields']);
01063 }
01064 if (empty($assocData['offset']) && !empty($assocData['page'])) {
01065 $assocData['offset'] = ($assocData['page'] - 1) * $assocData['limit'];
01066 }
01067 $assocData['limit'] = $this->limit($assocData['limit'], $assocData['offset']);
01068
01069 switch($type) {
01070 case 'hasOne':
01071 case 'belongsTo':
01072 $conditions = $this->__mergeConditions(
01073 $assocData['conditions'],
01074 $this->getConstraint($type, $model, $linkModel, $alias, array_merge($assocData, compact('external', 'self')))
01075 );
01076 if ($external) {
01077 $query = array_merge($assocData, array(
01078 'conditions' => $conditions,
01079 'table' => $this->fullTableName($linkModel),
01080 'fields' => $fields,
01081 'alias' => $alias,
01082 'group' => null
01083 ));
01084 $query = array_merge(array('order' => $assocData['order'], 'limit' => $assocData['limit']), $query);
01085 } else {
01086 $join = array(
01087 'table' => $this->fullTableName($linkModel),
01088 'alias' => $alias,
01089 'type' => isset($assocData['type']) ? $assocData['type'] : 'LEFT',
01090 'conditions' => trim($this->conditions($conditions, true, false, $model))
01091 );
01092 $queryData['fields'] = array_merge($queryData['fields'], $fields);
01093
01094 if (!empty($assocData['order'])) {
01095 $queryData['order'][] = $assocData['order'];
01096 }
01097 if (!in_array($join, $queryData['joins'])) {
01098 $queryData['joins'][] = $join;
01099 }
01100 return true;
01101 }
01102 break;
01103 case 'hasMany':
01104 $assocData['fields'] = $this->fields($linkModel, $alias, $assocData['fields']);
01105 if (!empty($assocData['foreignKey'])) {
01106 $assocData['fields'] = array_merge($assocData['fields'], $this->fields($linkModel, $alias, array("{$alias}.{$assocData['foreignKey']}")));
01107 }
01108 $query = array(
01109 'conditions' => $this->__mergeConditions($this->getConstraint('hasMany', $model, $linkModel, $alias, $assocData), $assocData['conditions']),
01110 'fields' => array_unique($assocData['fields']),
01111 'table' => $this->fullTableName($linkModel),
01112 'alias' => $alias,
01113 'order' => $assocData['order'],
01114 'limit' => $assocData['limit'],
01115 'group' => null
01116 );
01117 break;
01118 case 'hasAndBelongsToMany':
01119 $joinFields = array();
01120 $joinAssoc = null;
01121
01122 if (isset($assocData['with']) && !empty($assocData['with'])) {
01123 $joinKeys = array($assocData['foreignKey'], $assocData['associationForeignKey']);
01124 list($with, $joinFields) = $model->joinModel($assocData['with'], $joinKeys);
01125
01126 $joinTbl = $this->fullTableName($model->{$with});
01127 $joinAlias = $joinTbl;
01128
01129 if (is_array($joinFields) && !empty($joinFields)) {
01130 $joinFields = $this->fields($model->{$with}, $model->{$with}->alias, $joinFields);
01131 $joinAssoc = $joinAlias = $model->{$with}->alias;
01132 } else {
01133 $joinFields = array();
01134 }
01135 } else {
01136 $joinTbl = $this->fullTableName($assocData['joinTable']);
01137 $joinAlias = $joinTbl;
01138 }
01139 $query = array(
01140 'conditions' => $assocData['conditions'],
01141 'limit' => $assocData['limit'],
01142 'table' => $this->fullTableName($linkModel),
01143 'alias' => $alias,
01144 'fields' => array_merge($this->fields($linkModel, $alias, $assocData['fields']), $joinFields),
01145 'order' => $assocData['order'],
01146 'group' => null,
01147 'joins' => array(array(
01148 'table' => $joinTbl,
01149 'alias' => $joinAssoc,
01150 'conditions' => $this->getConstraint('hasAndBelongsToMany', $model, $linkModel, $joinAlias, $assocData, $alias)
01151 ))
01152 );
01153 break;
01154 }
01155 if (isset($query)) {
01156 return $this->buildStatement($query, $model);
01157 }
01158 return null;
01159 }
01160
01161
01162
01163
01164
01165
01166
01167
01168 function getConstraint($type, $model, $linkModel, $alias, $assoc, $alias2 = null) {
01169 $assoc = array_merge(array('external' => false, 'self' => false), $assoc);
01170
01171 if (array_key_exists('foreignKey', $assoc) && empty($assoc['foreignKey'])) {
01172 return array();
01173 }
01174
01175 switch (true) {
01176 case ($assoc['external'] && $type == 'hasOne'):
01177 return array("{$alias}.{$assoc['foreignKey']}" => '{$__cakeID__$}');
01178 break;
01179 case ($assoc['external'] && $type == 'belongsTo'):
01180 return array("{$alias}.{$linkModel->primaryKey}" => '{$__cakeForeignKey__$}');
01181 break;
01182 case (!$assoc['external'] && $type == 'hasOne'):
01183 return array("{$alias}.{$assoc['foreignKey']}" => $this->identifier("{$model->alias}.{$model->primaryKey}"));
01184 break;
01185 case (!$assoc['external'] && $type == 'belongsTo'):
01186 return array("{$model->alias}.{$assoc['foreignKey']}" => $this->identifier("{$alias}.{$linkModel->primaryKey}"));
01187 break;
01188 case ($type == 'hasMany'):
01189 return array("{$alias}.{$assoc['foreignKey']}" => array('{$__cakeID__$}'));
01190 break;
01191 case ($type == 'hasAndBelongsToMany'):
01192 return array(
01193 array("{$alias}.{$assoc['foreignKey']}" => '{$__cakeID__$}'),
01194 array("{$alias}.{$assoc['associationForeignKey']}" => $this->identifier("{$alias2}.{$linkModel->primaryKey}"))
01195 );
01196 break;
01197 }
01198 return array();
01199 }
01200
01201
01202
01203
01204
01205
01206
01207
01208 function buildJoinStatement($join) {
01209 $data = array_merge(array(
01210 'type' => null,
01211 'alias' => null,
01212 'table' => 'join_table',
01213 'conditions' => array()
01214 ), $join);
01215
01216 if (!empty($data['alias'])) {
01217 $data['alias'] = $this->alias . $this->name($data['alias']);
01218 }
01219 if (!empty($data['conditions'])) {
01220 $data['conditions'] = trim($this->conditions($data['conditions'], true, false));
01221 }
01222 return $this->renderJoinStatement($data);
01223 }
01224
01225
01226
01227
01228
01229
01230
01231
01232 function buildStatement($query, $model) {
01233 $query = array_merge(array('offset' => null, 'joins' => array()), $query);
01234 if (!empty($query['joins'])) {
01235 for ($i = 0; $i < count($query['joins']); $i++) {
01236 if (is_array($query['joins'][$i])) {
01237 $query['joins'][$i] = $this->buildJoinStatement($query['joins'][$i]);
01238 }
01239 }
01240 }
01241 return $this->renderStatement('select', array(
01242 'conditions' => $this->conditions($query['conditions'], true, true, $model),
01243 'fields' => join(', ', $query['fields']),
01244 'table' => $query['table'],
01245 'alias' => $this->alias . $this->name($query['alias']),
01246 'order' => $this->order($query['order']),
01247 'limit' => $this->limit($query['limit'], $query['offset']),
01248 'joins' => join(' ', $query['joins']),
01249 'group' => $this->group($query['group'])
01250 ));
01251 }
01252
01253
01254
01255
01256
01257
01258 function renderJoinStatement($data) {
01259 extract($data);
01260 return trim("{$type} JOIN {$table} {$alias} ON ({$conditions})");
01261 }
01262
01263
01264
01265
01266
01267
01268
01269 function renderStatement($type, $data) {
01270 extract($data);
01271 $aliases = null;
01272
01273 switch (strtolower($type)) {
01274 case 'select':
01275 return "SELECT {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order} {$limit}";
01276 break;
01277 case 'update':
01278 if (!empty($alias)) {
01279 $aliases = "{$this->alias}{$alias} {$joins} ";
01280 }
01281 return "UPDATE {$table} {$aliases}SET {$fields} {$conditions}";
01282 break;
01283 case 'delete':
01284 if (!empty($alias)) {
01285 $aliases = "{$this->alias}{$alias} {$joins} ";
01286 }
01287 return "DELETE {$alias} FROM {$table} {$aliases}{$conditions}";
01288 break;
01289 case 'schema':
01290 foreach (array('columns', 'indexes') as $var) {
01291 if (is_array(${$var})) {
01292 ${$var} = "\t" . join(",\n\t", array_filter(${$var}));
01293 }
01294 }
01295 if (trim($indexes) != '') {
01296 $columns .= ',';
01297 }
01298 return "CREATE TABLE {$table} (\n{$columns}{$indexes});";
01299 break;
01300 case 'alter':
01301 break;
01302 }
01303 }
01304
01305
01306
01307
01308
01309 function __mergeConditions($query, $assoc) {
01310 if (!empty($assoc)) {
01311 if (is_array($query)) {
01312 return array_merge((array)$assoc, $query);
01313 } else {
01314 if (!empty($query)) {
01315 $query = array($query);
01316 if (is_array($assoc)) {
01317 $query = array_merge($query, $assoc);
01318 } else {
01319 $query[] = $assoc;
01320 }
01321 return $query;
01322 } else {
01323 return $assoc;
01324 }
01325 }
01326 }
01327 return $query;
01328 }
01329
01330
01331
01332
01333
01334
01335
01336