CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 1.2 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 1.2
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Classes

  • AclBase
  • AclBehavior
  • AclComponent
  • AclNode
  • AclShell
  • Aco
  • AcoAction
  • AjaxHelper
  • ApcEngine
  • ApiShell
  • App
  • AppController
  • AppHelper
  • AppModel
  • Aro
  • AuthComponent
  • BakeShell
  • BehaviorCollection
  • Cache
  • CacheEngine
  • CacheHelper
  • CakeErrorController
  • CakeLog
  • CakeSchema
  • CakeSession
  • CakeSocket
  • ClassRegistry
  • Component
  • Configure
  • ConnectionManager
  • ConsoleShell
  • ContainableBehavior
  • Controller
  • ControllerTask
  • CookieComponent
  • DataSource
  • DbAcl
  • DbAclSchema
  • DbConfigTask
  • DboAdodb
  • DboDb2
  • DboFirebird
  • DboMssql
  • DboMysql
  • DboMysqlBase
  • DboMysqli
  • DboOdbc
  • DboOracle
  • DboPostgres
  • DboSource
  • DboSqlite
  • DboSybase
  • Debugger
  • EmailComponent
  • ErrorHandler
  • ExtractTask
  • File
  • FileEngine
  • Flay
  • Folder
  • FormHelper
  • Helper
  • HtmlHelper
  • HttpSocket
  • I18n
  • I18nModel
  • i18nSchema
  • I18nShell
  • Inflector
  • IniAcl
  • JavascriptHelper
  • JsHelper
  • JsHelperObject
  • L10n
  • MagicDb
  • MagicFileResource
  • MediaView
  • MemcacheEngine
  • Model
  • ModelBehavior
  • ModelTask
  • Multibyte
  • NumberHelper
  • Object
  • Overloadable
  • Overloadable2
  • PagesController
  • PaginatorHelper
  • Permission
  • PluginTask
  • ProjectTask
  • RequestHandlerComponent
  • Router
  • RssHelper
  • Sanitize
  • Scaffold
  • ScaffoldView
  • SchemaShell
  • Security
  • SecurityComponent
  • SessionComponent
  • SessionHelper
  • SessionsSchema
  • Set
  • Shell
  • String
  • TestSuiteShell
  • TestTask
  • TextHelper
  • ThemeView
  • TimeHelper
  • TranslateBehavior
  • TreeBehavior
  • Validation
  • View
  • ViewTask
  • XcacheEngine
  • Xml
  • XmlElement
  • XmlHelper
  • XmlManager
  • XmlNode
  • XmlTextNode

Functions

  • __enclose
  • make_clean_css
  • mb_encode_mimeheader
  • mb_stripos
  • mb_stristr
  • mb_strlen
  • mb_strpos
  • mb_strrchr
  • mb_strrichr
  • mb_strripos
  • mb_strrpos
  • mb_strstr
  • mb_strtolower
  • mb_strtoupper
  • mb_substr
  • mb_substr_count
  • write_css_cache
   1: <?php
   2: /* SVN FILE: $Id$ */
   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://cakephp.org)
  11:  * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  12:  *
  13:  * Licensed under The MIT License
  14:  * Redistributions of files must retain the above copyright notice.
  15:  *
  16:  * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  17:  * @link          http://cakephp.org CakePHP(tm) Project
  18:  * @package       cake
  19:  * @subpackage    cake.cake.libs.model.datasources
  20:  * @since         CakePHP(tm) v 0.10.0.1076
  21:  * @version       $Revision$
  22:  * @modifiedby    $LastChangedBy$
  23:  * @lastmodified  $Date$
  24:  * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
  25:  */
  26: App::import('Core', array('Set', 'String'));
  27: 
  28: /**
  29:  * DboSource
  30:  *
  31:  * Creates DBO-descendant objects from a given db connection configuration
  32:  *
  33:  * @package       cake
  34:  * @subpackage    cake.cake.libs.model.datasources
  35:  */
  36: class DboSource extends DataSource {
  37: /**
  38:  * Description string for this Database Data Source.
  39:  *
  40:  * @var unknown_type
  41:  */
  42:     var $description = "Database Data Source";
  43: /**
  44:  * index definition, standard cake, primary, index, unique
  45:  *
  46:  * @var array
  47:  */
  48:     var $index = array('PRI' => 'primary', 'MUL' => 'index', 'UNI' => 'unique');
  49: /**
  50:  * Database keyword used to assign aliases to identifiers.
  51:  *
  52:  * @var string
  53:  */
  54:     var $alias = 'AS ';
  55: /**
  56:  * Caches fields quoted in DboSource::name()
  57:  *
  58:  * @var array
  59:  */
  60:     var $fieldCache = array();
  61: /**
  62:  * Bypass automatic adding of joined fields/associations.
  63:  *
  64:  * @var boolean
  65:  */
  66:     var $__bypass = false;
  67: /**
  68:  * The set of valid SQL operations usable in a WHERE statement
  69:  *
  70:  * @var array
  71:  */
  72:     var $__sqlOps = array('like', 'ilike', 'or', 'not', 'in', 'between', 'regexp', 'similar to');
  73: /**
  74:  * Index of basic SQL commands
  75:  *
  76:  * @var array
  77:  * @access protected
  78:  */
  79:     var $_commands = array(
  80:         'begin' => 'BEGIN',
  81:         'commit' => 'COMMIT',
  82:         'rollback' => 'ROLLBACK'
  83:     );
  84: /**
  85:  * Constructor
  86:  */
  87:     function __construct($config = null, $autoConnect = true) {
  88:         if (!isset($config['prefix'])) {
  89:             $config['prefix'] = '';
  90:         }
  91:         parent::__construct($config);
  92:         $this->fullDebug = Configure::read() > 1;
  93:         if (!$this->enabled()) {
  94:             return false;
  95:         }
  96:         if ($autoConnect) {
  97:             return $this->connect();
  98:         } else {
  99:             return true;
 100:         }
 101:     }
 102: /**
 103:  * Reconnects to database server with optional new settings
 104:  *
 105:  * @param array $config An array defining the new configuration settings
 106:  * @return boolean True on success, false on failure
 107:  */
 108:     function reconnect($config = array()) {
 109:         $this->disconnect();
 110:         $this->setConfig($config);
 111:         $this->_sources = null;
 112: 
 113:         return $this->connect();
 114:     }
 115: /**
 116:  * Prepares a value, or an array of values for database queries by quoting and escaping them.
 117:  *
 118:  * @param mixed $data A value or an array of values to prepare.
 119:  * @param string $column The column into which this data will be inserted
 120:  * @param boolean $read Value to be used in READ or WRITE context
 121:  * @return mixed Prepared value or array of values.
 122:  */
 123:     function value($data, $column = null, $read = true) {
 124:         if (is_array($data) && !empty($data)) {
 125:             return array_map(
 126:                 array(&$this, 'value'),
 127:                 $data, array_fill(0, count($data), $column), array_fill(0, count($data), $read)
 128:             );
 129:         } elseif (is_object($data) && isset($data->type)) {
 130:             if ($data->type == 'identifier') {
 131:                 return $this->name($data->value);
 132:             } elseif ($data->type == 'expression') {
 133:                 return $data->value;
 134:             }
 135:         } elseif (in_array($data, array('{$__cakeID__$}', '{$__cakeForeignKey__$}'), true)) {
 136:             return $data;
 137:         } else {
 138:             return null;
 139:         }
 140:     }
 141: /**
 142:  * Returns an object to represent a database identifier in a query
 143:  *
 144:  * @param string $identifier
 145:  * @return object An object representing a database identifier to be used in a query
 146:  */
 147:     function identifier($identifier) {
 148:         $obj = new stdClass();
 149:         $obj->type = 'identifier';
 150:         $obj->value = $identifier;
 151:         return $obj;
 152:     }
 153: /**
 154:  * Returns an object to represent a database expression in a query
 155:  *
 156:  * @param string $expression
 157:  * @return object An object representing a database expression to be used in a query
 158:  */
 159:     function expression($expression) {
 160:         $obj = new stdClass();
 161:         $obj->type = 'expression';
 162:         $obj->value = $expression;
 163:         return $obj;
 164:     }
 165: /**
 166:  * Executes given SQL statement.
 167:  *
 168:  * @param string $sql SQL statement
 169:  * @return unknown
 170:  */
 171:     function rawQuery($sql) {
 172:         $this->took = $this->error = $this->numRows = false;
 173:         return $this->execute($sql);
 174:     }
 175: /**
 176:  * Queries the database with given SQL statement, and obtains some metadata about the result
 177:  * (rows affected, timing, any errors, number of rows in resultset). The query is also logged.
 178:  * If DEBUG is set, the log is shown all the time, else it is only shown on errors.
 179:  *
 180:  * @param string $sql
 181:  * @param array $options
 182:  * @return mixed Resource or object representing the result set, or false on failure
 183:  */
 184:     function execute($sql, $options = array()) {
 185:         $defaults = array('stats' => true, 'log' => $this->fullDebug);
 186:         $options = array_merge($defaults, $options);
 187: 
 188:         $t = getMicrotime();
 189:         $this->_result = $this->_execute($sql);
 190:         if ($options['stats']) {
 191:             $this->took = round((getMicrotime() - $t) * 1000, 0);
 192:             $this->affected = $this->lastAffected();
 193:             $this->error = $this->lastError();
 194:             $this->numRows = $this->lastNumRows();
 195:         }
 196: 
 197:         if ($options['log']) {
 198:             $this->logQuery($sql);
 199:         }
 200: 
 201:         if ($this->error) {
 202:             $this->showQuery($sql);
 203:             return false;
 204:         }
 205:         return $this->_result;
 206:     }
 207: /**
 208:  * DataSource Query abstraction
 209:  *
 210:  * @return resource Result resource identifier
 211:  */
 212:     function query() {
 213:         $args     = func_get_args();
 214:         $fields   = null;
 215:         $order    = null;
 216:         $limit    = null;
 217:         $page     = null;
 218:         $recursive = null;
 219: 
 220:         if (count($args) == 1) {
 221:             return $this->fetchAll($args[0]);
 222: 
 223:         } elseif (count($args) > 1 && (strpos(strtolower($args[0]), 'findby') === 0 || strpos(strtolower($args[0]), 'findallby') === 0)) {
 224:             $params = $args[1];
 225: 
 226:             if (strpos(strtolower($args[0]), 'findby') === 0) {
 227:                 $all  = false;
 228:                 $field = Inflector::underscore(preg_replace('/^findBy/i', '', $args[0]));
 229:             } else {
 230:                 $all  = true;
 231:                 $field = Inflector::underscore(preg_replace('/^findAllBy/i', '', $args[0]));
 232:             }
 233: 
 234:             $or = (strpos($field, '_or_') !== false);
 235:             if ($or) {
 236:                 $field = explode('_or_', $field);
 237:             } else {
 238:                 $field = explode('_and_', $field);
 239:             }
 240:             $off = count($field) - 1;
 241: 
 242:             if (isset($params[1 + $off])) {
 243:                 $fields = $params[1 + $off];
 244:             }
 245: 
 246:             if (isset($params[2 + $off])) {
 247:                 $order = $params[2 + $off];
 248:             }
 249: 
 250:             if (!array_key_exists(0, $params)) {
 251:                 return false;
 252:             }
 253: 
 254:             $c = 0;
 255:             $conditions = array();
 256: 
 257:             foreach ($field as $f) {
 258:                 $conditions[$args[2]->alias . '.' . $f] = $params[$c];
 259:                 $c++;
 260:             }
 261: 
 262:             if ($or) {
 263:                 $conditions = array('OR' => $conditions);
 264:             }
 265: 
 266:             if ($all) {
 267:                 if (isset($params[3 + $off])) {
 268:                     $limit = $params[3 + $off];
 269:                 }
 270: 
 271:                 if (isset($params[4 + $off])) {
 272:                     $page = $params[4 + $off];
 273:                 }
 274: 
 275:                 if (isset($params[5 + $off])) {
 276:                     $recursive = $params[5 + $off];
 277:                 }
 278:                 return $args[2]->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive'));
 279:             } else {
 280:                 if (isset($params[3 + $off])) {
 281:                     $recursive = $params[3 + $off];
 282:                 }
 283:                 return $args[2]->find('first', compact('conditions', 'fields', 'order', 'recursive'));
 284:             }
 285:         } else {
 286:             if (isset($args[1]) && $args[1] === true) {
 287:                 return $this->fetchAll($args[0], true);
 288:             } else if (isset($args[1]) && !is_array($args[1]) ) {
 289:                 return $this->fetchAll($args[0], false);
 290:             } else if (isset($args[1]) && is_array($args[1])) {
 291:                 $offset = 0;
 292:                 if (isset($args[2])) {
 293:                     $cache = $args[2];
 294:                 } else {
 295:                     $cache = true;
 296:                 }
 297:                 $args[1] = array_map(array(&$this, 'value'), $args[1]);
 298:                 return $this->fetchAll(String::insert($args[0], $args[1]), $cache);
 299:             }
 300:         }
 301:     }
 302: /**
 303:  * Returns a row from current resultset as an array
 304:  *
 305:  * @return array The fetched row as an array
 306:  */
 307:     function fetchRow($sql = null) {
 308:         if (!empty($sql) && is_string($sql) && strlen($sql) > 5) {
 309:             if (!$this->execute($sql)) {
 310:                 return null;
 311:             }
 312:         }
 313: 
 314:         if ($this->hasResult()) {
 315:             $this->resultSet($this->_result);
 316:             $resultRow = $this->fetchResult();
 317:             return $resultRow;
 318:         } else {
 319:             return null;
 320:         }
 321:     }
 322: /**
 323:  * Returns an array of all result rows for a given SQL query.
 324:  * Returns false if no rows matched.
 325:  *
 326:  * @param string $sql SQL statement
 327:  * @param boolean $cache Enables returning/storing cached query results
 328:  * @return array Array of resultset rows, or false if no rows matched
 329:  */
 330:     function fetchAll($sql, $cache = true, $modelName = null) {
 331:         if ($cache && isset($this->_queryCache[$sql])) {
 332:             if (preg_match('/^\s*select/i', $sql)) {
 333:                 return $this->_queryCache[$sql];
 334:             }
 335:         }
 336: 
 337:         if ($this->execute($sql)) {
 338:             $out = array();
 339: 
 340:             $first = $this->fetchRow();
 341:             if ($first != null) {
 342:                 $out[] = $first;
 343:             }
 344:             while ($this->hasResult() && $item = $this->fetchResult()) {
 345:                 $out[] = $item;
 346:             }
 347: 
 348:             if ($cache) {
 349:                 if (strpos(trim(strtolower($sql)), 'select') !== false) {
 350:                     $this->_queryCache[$sql] = $out;
 351:                 }
 352:             }
 353:             return $out;
 354: 
 355:         } else {
 356:             return false;
 357:         }
 358:     }
 359: /**
 360:  * Returns a single field of the first of query results for a given SQL query, or false if empty.
 361:  *
 362:  * @param string $name Name of the field
 363:  * @param string $sql SQL query
 364:  * @return unknown
 365:  */
 366:     function field($name, $sql) {
 367:         $data = $this->fetchRow($sql);
 368: 
 369:         if (!isset($data[$name]) || empty($data[$name])) {
 370:             return false;
 371:         } else {
 372:             return $data[$name];
 373:         }
 374:     }
 375: /**
 376:  * Returns a quoted name of $data for use in an SQL statement.
 377:  * Strips fields out of SQL functions before quoting.
 378:  *
 379:  * @param string $data
 380:  * @return string SQL field
 381:  */
 382:     function name($data) {
 383:         if (is_object($data) && isset($data->type)) {
 384:             return $data->value;
 385:         }
 386:         if ($data == '*') {
 387:             return '*';
 388:         }
 389:         $array = is_array($data);
 390:         $data = (array)$data;
 391:         $count = count($data);
 392: 
 393:         for ($i = 0; $i < $count; $i++) {
 394:             if ($data[$i] == '*') {
 395:                 continue;
 396:             }
 397:             if (strpos($data[$i], '(') !== false && preg_match_all('/([^(]*)\((.*)\)(.*)/', $data[$i], $fields)) {
 398:                 $fields = Set::extract($fields, '{n}.0');
 399: 
 400:                 if (!empty($fields[1])) {
 401:                     if (!empty($fields[2])) {
 402:                         $data[$i] = $fields[1] . '(' . $this->name($fields[2]) . ')' . $fields[3];
 403:                     } else {
 404:                         $data[$i] = $fields[1] . '()' . $fields[3];
 405:                     }
 406:                 }
 407:             }
 408:             $data[$i] = str_replace('.', $this->endQuote . '.' . $this->startQuote, $data[$i]);
 409:             $data[$i] = $this->startQuote . $data[$i] . $this->endQuote;
 410:             $data[$i] = str_replace($this->startQuote . $this->startQuote, $this->startQuote, $data[$i]);
 411:             $data[$i] = str_replace($this->startQuote . '(', '(', $data[$i]);
 412:             $data[$i] = str_replace(')' . $this->startQuote, ')', $data[$i]);
 413:             $alias = !empty($this->alias) ? $this->alias : 'AS ';
 414: 
 415:             if (preg_match('/\s+' . $alias . '\s*/', $data[$i])) {
 416:                 if (preg_match('/\w+\s+' . $alias . '\s*/', $data[$i])) {
 417:                     $quoted = $this->endQuote . ' ' . $alias . $this->startQuote;
 418:                     $data[$i] = str_replace(' ' . $alias, $quoted, $data[$i]);
 419:                 } else {
 420:                     $quoted = $alias . $this->startQuote;
 421:                     $data[$i] = str_replace($alias, $quoted, $data[$i]) . $this->endQuote;
 422:                 }
 423:             }
 424: 
 425:             if (!empty($this->endQuote) && $this->endQuote == $this->startQuote) {
 426:                 if (substr_count($data[$i], $this->endQuote) % 2 == 1) {
 427:                     if (substr($data[$i], -2) == $this->endQuote . $this->endQuote) {
 428:                         $data[$i] = substr($data[$i], 0, -1);
 429:                     } else {
 430:                         $data[$i] = trim($data[$i], $this->endQuote);
 431:                     }
 432:                 }
 433:             }
 434:             if (strpos($data[$i], '*')) {
 435:                 $data[$i] = str_replace($this->endQuote . '*' . $this->endQuote, '*', $data[$i]);
 436:             }
 437:             $data[$i] = str_replace($this->endQuote . $this->endQuote, $this->endQuote, $data[$i]);
 438:         }
 439:         return (!$array) ? $data[0] : $data;
 440:     }
 441: /**
 442:  * Checks if it's connected to the database
 443:  *
 444:  * @return boolean True if the database is connected, else false
 445:  */
 446:     function isConnected() {
 447:         return $this->connected;
 448:     }
 449: /**
 450:  * Checks if the result is valid
 451:  *
 452:  * @return boolean True if the result is valid else false
 453:  */
 454:     function hasResult() {
 455:         return is_resource($this->_result);
 456:     }
 457: /**
 458:  * Outputs the contents of the queries log.
 459:  *
 460:  * @param boolean $sorted
 461:  */
 462:     function showLog($sorted = false) {
 463:         if ($sorted) {
 464:             $log = sortByKey($this->_queriesLog, 'took', 'desc', SORT_NUMERIC);
 465:         } else {
 466:             $log = $this->_queriesLog;
 467:         }
 468: 
 469:         if ($this->_queriesCnt > 1) {
 470:             $text = 'queries';
 471:         } else {
 472:             $text = 'query';
 473:         }
 474: 
 475:         if (PHP_SAPI != 'cli') {
 476:             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->configKeyName}) {$this->_queriesCnt} {$text} took {$this->_queriesTime} ms</caption>\n");
 477:             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");
 478: 
 479:             foreach ($log as $k => $i) {
 480:                 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");
 481:             }
 482:             print ("</tbody></table>\n");
 483:         } else {
 484:             foreach ($log as $k => $i) {
 485:                 print (($k + 1) . ". {$i['query']} {$i['error']}\n");
 486:             }
 487:         }
 488:     }
 489: /**
 490:  * Log given SQL query.
 491:  *
 492:  * @param string $sql SQL statement
 493:  * @todo: Add hook to log errors instead of returning false
 494:  */
 495:     function logQuery($sql) {
 496:         $this->_queriesCnt++;
 497:         $this->_queriesTime += $this->took;
 498:         $this->_queriesLog[] = array(
 499:             'query' => $sql,
 500:             'error'     => $this->error,
 501:             'affected'  => $this->affected,
 502:             'numRows'   => $this->numRows,
 503:             'took'      => $this->took
 504:         );
 505:         if (count($this->_queriesLog) > $this->_queriesLogMax) {
 506:             array_pop($this->_queriesLog);
 507:         }
 508:         if ($this->error) {
 509:             return false;
 510:         }
 511:     }
 512: /**
 513:  * Output information about an SQL query. The SQL statement, number of rows in resultset,
 514:  * and execution time in microseconds. If the query fails, an error is output instead.
 515:  *
 516:  * @param string $sql Query to show information on.
 517:  */
 518:     function showQuery($sql) {
 519:         $error = $this->error;
 520:         if (strlen($sql) > 200 && !$this->fullDebug && Configure::read() > 1) {
 521:             $sql = substr($sql, 0, 200) . '[...]';
 522:         }
 523:         if (Configure::read() > 0) {
 524:             $out = null;
 525:             if ($error) {
 526:                 trigger_error("<span style = \"color:Red;text-align:left\"><b>SQL Error:</b> {$this->error}</span>", E_USER_WARNING);
 527:             } else {
 528:                 $out = ("<small>[Aff:{$this->affected} Num:{$this->numRows} Took:{$this->took}ms]</small>");
 529:             }
 530:             pr(sprintf("<p style = \"text-align:left\"><b>Query:</b> %s %s</p>", $sql, $out));
 531:         }
 532:     }
 533: /**
 534:  * Gets full table name including prefix
 535:  *
 536:  * @param mixed $model
 537:  * @param boolean $quote
 538:  * @return string Full quoted table name
 539:  */
 540:     function fullTableName($model, $quote = true) {
 541:         if (is_object($model)) {
 542:             $table = $model->tablePrefix . $model->table;
 543:         } elseif (isset($this->config['prefix'])) {
 544:             $table = $this->config['prefix'] . strval($model);
 545:         } else {
 546:             $table = strval($model);
 547:         }
 548:         if ($quote) {
 549:             return $this->name($table);
 550:         }
 551:         return $table;
 552:     }
 553: /**
 554:  * The "C" in CRUD
 555:  *
 556:  * @param Model $model
 557:  * @param array $fields
 558:  * @param array $values
 559:  * @return boolean Success
 560:  */
 561:     function create(&$model, $fields = null, $values = null) {
 562:         $id = null;
 563: 
 564:         if ($fields == null) {
 565:             unset($fields, $values);
 566:             $fields = array_keys($model->data);
 567:             $values = array_values($model->data);
 568:         }
 569:         $count = count($fields);
 570: 
 571:         for ($i = 0; $i < $count; $i++) {
 572:             $valueInsert[] = $this->value($values[$i], $model->getColumnType($fields[$i]), false);
 573:         }
 574:         for ($i = 0; $i < $count; $i++) {
 575:             $fieldInsert[] = $this->name($fields[$i]);
 576:             if ($fields[$i] == $model->primaryKey) {
 577:                 $id = $values[$i];
 578:             }
 579:         }
 580:         $query = array(
 581:             'table' => $this->fullTableName($model),
 582:             'fields' => implode(', ', $fieldInsert),
 583:             'values' => implode(', ', $valueInsert)
 584:         );
 585: 
 586:         if ($this->execute($this->renderStatement('create', $query))) {
 587:             if (empty($id)) {
 588:                 $id = $this->lastInsertId($this->fullTableName($model, false), $model->primaryKey);
 589:             }
 590:             $model->setInsertID($id);
 591:             $model->id = $id;
 592:             return true;
 593:         } else {
 594:             $model->onError();
 595:             return false;
 596:         }
 597:     }
 598: /**
 599:  * The "R" in CRUD
 600:  *
 601:  * @param Model $model
 602:  * @param array $queryData
 603:  * @param integer $recursive Number of levels of association
 604:  * @return unknown
 605:  */
 606:     function read(&$model, $queryData = array(), $recursive = null) {
 607:         $queryData = $this->__scrubQueryData($queryData);
 608: 
 609:         $null = null;
 610:         $array = array();
 611:         $linkedModels = array();
 612:         $this->__bypass = false;
 613:         $this->__booleans = array();
 614: 
 615:         if ($recursive === null && isset($queryData['recursive'])) {
 616:             $recursive = $queryData['recursive'];
 617:         }
 618: 
 619:         if (!is_null($recursive)) {
 620:             $_recursive = $model->recursive;
 621:             $model->recursive = $recursive;
 622:         }
 623: 
 624:         if (!empty($queryData['fields'])) {
 625:             $this->__bypass = true;
 626:             $queryData['fields'] = $this->fields($model, null, $queryData['fields']);
 627:         } else {
 628:             $queryData['fields'] = $this->fields($model);
 629:         }
 630: 
 631:         $_associations = $model->__associations;
 632: 
 633:         if ($model->recursive == -1) {
 634:             $_associations = array();
 635:         } else if ($model->recursive == 0) {
 636:             unset($_associations[2], $_associations[3]);
 637:         }
 638: 
 639:         foreach ($_associations as $type) {
 640:             foreach ($model->{$type} as $assoc => $assocData) {
 641:                 $linkModel =& $model->{$assoc};
 642:                 $external = isset($assocData['external']);
 643: 
 644:                 if ($model->useDbConfig == $linkModel->useDbConfig) {
 645:                     if (true === $this->generateAssociationQuery($model, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null)) {
 646:                         $linkedModels[$type . '/' . $assoc] = true;
 647:                     }
 648:                 }
 649:             }
 650:         }
 651: 
 652:         $query = $this->generateAssociationQuery($model, $null, null, null, null, $queryData, false, $null);
 653: 
 654:         $resultSet = $this->fetchAll($query, $model->cacheQueries, $model->alias);
 655: 
 656:         if ($resultSet === false) {
 657:             $model->onError();
 658:             return false;
 659:         }
 660: 
 661:         $filtered = $this->__filterResults($resultSet, $model);
 662: 
 663:         if ($model->recursive > -1) {
 664:             foreach ($_associations as $type) {
 665:                 foreach ($model->{$type} as $assoc => $assocData) {
 666:                     $linkModel =& $model->{$assoc};
 667: 
 668:                     if (empty($linkedModels[$type . '/' . $assoc])) {
 669:                         if ($model->useDbConfig == $linkModel->useDbConfig) {
 670:                             $db =& $this;
 671:                         } else {
 672:                             $db =& ConnectionManager::getDataSource($linkModel->useDbConfig);
 673:                         }
 674:                     } elseif ($model->recursive > 1 && ($type == 'belongsTo' || $type == 'hasOne')) {
 675:                         $db =& $this;
 676:                     }
 677: 
 678:                     if (isset($db) && method_exists($db, 'queryAssociation')) {
 679:                         $stack = array($assoc);
 680:                         $db->queryAssociation($model, $linkModel, $type, $assoc, $assocData, $array, true, $resultSet, $model->recursive - 1, $stack);
 681:                         unset($db);
 682:                     }
 683:                 }
 684:             }
 685:             $this->__filterResults($resultSet, $model, $filtered);
 686:         }
 687: 
 688:         if (!is_null($recursive)) {
 689:             $model->recursive = $_recursive;
 690:         }
 691:         return $resultSet;
 692:     }
 693: /**
 694:  * Private method.  Passes association results thru afterFind filters of corresponding model
 695:  *
 696:  * @param array $results Reference of resultset to be filtered
 697:  * @param object $model Instance of model to operate against
 698:  * @param array $filtered List of classes already filtered, to be skipped
 699:  * @return return
 700:  */
 701:     function __filterResults(&$results, &$model, $filtered = array()) {
 702:         $filtering = array();
 703:         $count = count($results);
 704: 
 705:         for ($i = 0; $i < $count; $i++) {
 706:             if (is_array($results[$i])) {
 707:                 $classNames = array_keys($results[$i]);
 708:                 $count2 = count($classNames);
 709: 
 710:                 for ($j = 0; $j < $count2; $j++) {
 711:                     $className = $classNames[$j];
 712:                     if ($model->alias != $className && !in_array($className, $filtered)) {
 713:                         if (!in_array($className, $filtering)) {
 714:                             $filtering[] = $className;
 715:                         }
 716: 
 717:                         if (isset($model->{$className}) && is_object($model->{$className})) {
 718:                             $data = $model->{$className}->afterFind(array(array($className => $results[$i][$className])), false);
 719:                         }
 720:                         if (isset($data[0][$className])) {
 721:                             $results[$i][$className] = $data[0][$className];
 722:                         }
 723:                     }
 724:                 }
 725:             }
 726:         }
 727:         return $filtering;
 728:     }
 729: /**
 730:  * Enter description here...
 731:  *
 732:  * @param Model $model
 733:  * @param unknown_type $linkModel
 734:  * @param string $type Association type
 735:  * @param unknown_type $association
 736:  * @param unknown_type $assocData
 737:  * @param unknown_type $queryData
 738:  * @param unknown_type $external
 739:  * @param unknown_type $resultSet
 740:  * @param integer $recursive Number of levels of association
 741:  * @param array $stack
 742:  */
 743:     function queryAssociation(&$model, &$linkModel, $type, $association, $assocData, &$queryData, $external = false, &$resultSet, $recursive, $stack) {
 744:         if ($query = $this->generateAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet)) {
 745:             if (!isset($resultSet) || !is_array($resultSet)) {
 746:                 if (Configure::read() > 0) {
 747:                     echo '<div style = "font: Verdana bold 12px; color: #FF0000">' . sprintf(__('SQL Error in model %s:', true), $model->alias) . ' ';
 748:                     if (isset($this->error) && $this->error != null) {
 749:                         echo $this->error;
 750:                     }
 751:                     echo '</div>';
 752:                 }
 753:                 return null;
 754:             }
 755:             $count = count($resultSet);
 756: 
 757:             if ($type === 'hasMany' && empty($assocData['limit']) && !empty($assocData['foreignKey'])) {
 758:                 $ins = $fetch = array();
 759:                 for ($i = 0; $i < $count; $i++) {
 760:                     if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) {
 761:                         $ins[] = $in;
 762:                     }
 763:                 }
 764: 
 765:                 if (!empty($ins)) {
 766:                     $fetch = $this->fetchAssociated($model, $query, $ins);
 767:                 }
 768: 
 769:                 if (!empty($fetch) && is_array($fetch)) {
 770:                     if ($recursive > 0) {
 771:                         foreach ($linkModel->__associations as $type1) {
 772:                             foreach ($linkModel->{$type1} as $assoc1 => $assocData1) {
 773:                                 $deepModel =& $linkModel->{$assoc1};
 774:                                 $tmpStack = $stack;
 775:                                 $tmpStack[] = $assoc1;
 776: 
 777:                                 if ($linkModel->useDbConfig === $deepModel->useDbConfig) {
 778:                                     $db =& $this;
 779:                                 } else {
 780:                                     $db =& ConnectionManager::getDataSource($deepModel->useDbConfig);
 781:                                 }
 782:                                 $db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack);
 783:                             }
 784:                         }
 785:                     }
 786:                 }
 787:                 $this->__filterResults($fetch, $model);
 788:                 return $this->__mergeHasMany($resultSet, $fetch, $association, $model, $linkModel, $recursive);
 789:             } elseif ($type === 'hasAndBelongsToMany') {
 790:                 $ins = $fetch = array();
 791:                 for ($i = 0; $i < $count; $i++) {
 792:                     if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) {
 793:                         $ins[] = $in;
 794:                     }
 795:                 }
 796:                 if (!empty($ins)) {
 797:                     if (count($ins) > 1) {
 798:                         $query = str_replace('{$__cakeID__$}', '(' .implode(', ', $ins) .')', $query);
 799:                         $query = str_replace('= (', 'IN (', $query);
 800:                         $query = str_replace('= (', 'IN (', $query);
 801:                     } else {
 802:                         $query = str_replace('{$__cakeID__$}',$ins[0], $query);
 803:                     }
 804: 
 805:                     $query = str_replace(' WHERE 1 = 1', '', $query);
 806:                 }
 807: 
 808:                 $foreignKey = $model->hasAndBelongsToMany[$association]['foreignKey'];
 809:                 $joinKeys = array($foreignKey, $model->hasAndBelongsToMany[$association]['associationForeignKey']);
 810:                 list($with, $habtmFields) = $model->joinModel($model->hasAndBelongsToMany[$association]['with'], $joinKeys);
 811:                 $habtmFieldsCount = count($habtmFields);
 812:                 $q = $this->insertQueryData($query, null, $association, $assocData, $model, $linkModel, $stack);
 813: 
 814:                 if ($q != false) {
 815:                     $fetch = $this->fetchAll($q, $model->cacheQueries, $model->alias);
 816:                 } else {
 817:                     $fetch = null;
 818:                 }
 819:             }
 820: 
 821:             for ($i = 0; $i < $count; $i++) {
 822:                 $row =& $resultSet[$i];
 823: 
 824:                 if ($type !== 'hasAndBelongsToMany') {
 825:                     $q = $this->insertQueryData($query, $resultSet[$i], $association, $assocData, $model, $linkModel, $stack);
 826:                     if ($q != false) {
 827:                         $fetch = $this->fetchAll($q, $model->cacheQueries, $model->alias);
 828:                     } else {
 829:                         $fetch = null;
 830:                     }
 831:                 }
 832:                 $selfJoin = false;
 833: 
 834:                 if ($linkModel->name === $model->name) {
 835:                     $selfJoin = true;
 836:                 }
 837: 
 838:                 if (!empty($fetch) && is_array($fetch)) {
 839:                     if ($recursive > 0) {
 840:                         foreach ($linkModel->__associations as $type1) {
 841:                             foreach ($linkModel->{$type1} as $assoc1 => $assocData1) {
 842:                                 $deepModel =& $linkModel->{$assoc1};
 843: 
 844:                                 if (($type1 === 'belongsTo') || ($deepModel->alias === $model->alias && $type === 'belongsTo') || ($deepModel->alias != $model->alias)) {
 845:                                     $tmpStack = $stack;
 846:                                     $tmpStack[] = $assoc1;
 847:                                     if ($linkModel->useDbConfig == $deepModel->useDbConfig) {
 848:                                         $db =& $this;
 849:                                     } else {
 850:                                         $db =& ConnectionManager::getDataSource($deepModel->useDbConfig);
 851:                                     }
 852:                                     $db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack);
 853:                                 }
 854:                             }
 855:                         }
 856:                     }
 857:                     if ($type == 'hasAndBelongsToMany') {
 858:                         $uniqueIds = $merge = array();
 859: 
 860:                         foreach ($fetch as $j => $data) {
 861:                             if (
 862:                                 (isset($data[$with]) && $data[$with][$foreignKey] === $row[$model->alias][$model->primaryKey])
 863:                             ) {
 864:                                 if ($habtmFieldsCount <= 2) {
 865:                                     unset($data[$with]);
 866:                                 }
 867:                                 $merge[] = $data;
 868:                             }
 869:                         }
 870:                         if (empty($merge) && !isset($row[$association])) {
 871:                             $row[$association] = $merge;
 872:                         } else {
 873:                             $this->__mergeAssociation($resultSet[$i], $merge, $association, $type);
 874:                         }
 875:                     } else {
 876:                         $this->__mergeAssociation($resultSet[$i], $fetch, $association, $type, $selfJoin);
 877:                     }
 878:                     if (isset($resultSet[$i][$association])) {
 879:                         $resultSet[$i][$association] = $linkModel->afterFind($resultSet[$i][$association], false);
 880:                     }
 881:                 } else {
 882:                     $tempArray[0][$association] = false;
 883:                     $this->__mergeAssociation($resultSet[$i], $tempArray, $association, $type, $selfJoin);
 884:                 }
 885:             }
 886:         }
 887:     }
 888: /**
 889:  * A more efficient way to fetch associations.  Woohoo!
 890:  *
 891:  * @param model $model      Primary model object
 892:  * @param string $query     Association query
 893:  * @param array $ids        Array of IDs of associated records
 894:  * @return array Association results
 895:  */
 896:     function fetchAssociated($model, $query, $ids) {
 897:         $query = str_replace('{$__cakeID__$}', implode(', ', $ids), $query);
 898:         if (count($ids) > 1) {
 899:             $query = str_replace('= (', 'IN (', $query);
 900:             $query = str_replace('= (', 'IN (', $query);
 901:         }
 902:         return $this->fetchAll($query, $model->cacheQueries, $model->alias);
 903:     }
 904: /**
 905:  * mergeHasMany - Merge the results of hasMany relations.
 906:  *
 907:  *
 908:  * @param array $resultSet Data to merge into
 909:  * @param array $merge Data to merge
 910:  * @param string $association Name of Model being Merged
 911:  * @param object $model Model being merged onto
 912:  * @param object $linkModel Model being merged
 913:  * @return void
 914:  **/
 915:     function __mergeHasMany(&$resultSet, $merge, $association, &$model, &$linkModel) {
 916:         foreach ($resultSet as $i => $value) {
 917:             $count = 0;
 918:             $merged[$association] = array();
 919:             foreach ($merge as $j => $data) {
 920:                 if (isset($value[$model->alias]) && $value[$model->alias][$model->primaryKey] === $data[$association][$model->hasMany[$association]['foreignKey']]) {
 921:                     if (count($data) > 1) {
 922:                         $data = array_merge($data[$association], $data);
 923:                         unset($data[$association]);
 924:                         foreach ($data as $key => $name) {
 925:                             if (is_numeric($key)) {
 926:                                 $data[$association][] = $name;
 927:                                 unset($data[$key]);
 928:                             }
 929:                         }
 930:                         $merged[$association][] = $data;
 931:                     } else {
 932:                         $merged[$association][] = $data[$association];
 933:                     }
 934:                 }
 935:                 $count++;
 936:             }
 937:             if (isset($value[$model->alias])) {
 938:                 $resultSet[$i] = Set::pushDiff($resultSet[$i], $merged);
 939:                 unset($merged);
 940:             }
 941:         }
 942:     }
 943: /**
 944:  * Enter description here...
 945:  *
 946:  * @param unknown_type $data
 947:  * @param unknown_type $merge
 948:  * @param unknown_type $association
 949:  * @param unknown_type $type
 950:  * @param boolean $selfJoin
 951:  */
 952:     function __mergeAssociation(&$data, $merge, $association, $type, $selfJoin = false) {
 953:         if (isset($merge[0]) && !isset($merge[0][$association])) {
 954:             $association = Inflector::pluralize($association);
 955:         }
 956: 
 957:         if ($type == 'belongsTo' || $type == 'hasOne') {
 958:             if (isset($merge[$association])) {
 959:                 $data[$association] = $merge[$association][0];
 960:             } else {
 961:                 if (count($merge[0][$association]) > 1) {
 962:                     foreach ($merge[0] as $assoc => $data2) {
 963:                         if ($assoc != $association) {
 964:                             $merge[0][$association][$assoc] = $data2;
 965:                         }
 966:                     }
 967:                 }
 968:                 if (!isset($data[$association])) {
 969:                     if ($merge[0][$association] != null) {
 970:                         $data[$association] = $merge[0][$association];
 971:                     } else {
 972:                         $data[$association] = array();
 973:                     }
 974:                 } else {
 975:                     if (is_array($merge[0][$association])) {
 976:                         foreach ($data[$association] as $k => $v) {
 977:                             if (!is_array($v)) {
 978:                                 $dataAssocTmp[$k] = $v;
 979:                             }
 980:                         }
 981: 
 982:                         foreach ($merge[0][$association] as $k => $v) {
 983:                             if (!is_array($v)) {
 984:                                 $mergeAssocTmp[$k] = $v;
 985:                             }
 986:                         }
 987:                         $dataKeys = array_keys($data);
 988:                         $mergeKeys = array_keys($merge[0]);
 989: 
 990:                         if ($mergeKeys[0] === $dataKeys[0] || $mergeKeys === $dataKeys) {
 991:                             $data[$association][$association] = $merge[0][$association];
 992:                         } else {
 993:                             $diff = Set::diff($dataAssocTmp, $mergeAssocTmp);
 994:                             $data[$association] = array_merge($merge[0][$association], $diff);
 995:                         }
 996:                     } elseif ($selfJoin && array_key_exists($association, $merge[0])) {
 997:                         $data[$association] = array_merge($data[$association], array($association => array()));
 998:                     }
 999:                 }
1000:             }
1001:         } else {
1002:             if (isset($merge[0][$association]) && $merge[0][$association] === false) {
1003:                 if (!isset($data[$association])) {
1004:                     $data[$association] = array();
1005:                 }
1006:             } else {
1007:                 foreach ($merge as $i => $row) {
1008:                     if (count($row) == 1) {
1009:                         if (empty($data[$association]) || (isset($data[$association]) && !in_array($row[$association], $data[$association]))) {
1010:                             $data[$association][] = $row[$association];
1011:                         }
1012:                     } else if (!empty($row)) {
1013:                         $tmp = array_merge($row[$association], $row);
1014:                         unset($tmp[$association]);
1015:                         $data[$association][] = $tmp;
1016:                     }
1017:                 }
1018:             }
1019:         }
1020:     }
1021: /**
1022:  * Generates an array representing a query or part of a query from a single model or two associated models
1023:  *
1024:  * @param Model $model
1025:  * @param Model $linkModel
1026:  * @param string $type
1027:  * @param string $association
1028:  * @param array $assocData
1029:  * @param array $queryData
1030:  * @param boolean $external
1031:  * @param array $resultSet
1032:  * @return mixed
1033:  */
1034:     function generateAssociationQuery(&$model, &$linkModel, $type, $association = null, $assocData = array(), &$queryData, $external = false, &$resultSet) {
1035:         $queryData = $this->__scrubQueryData($queryData);
1036:         $assocData = $this->__scrubQueryData($assocData);
1037: 
1038:         if (empty($queryData['fields'])) {
1039:             $queryData['fields'] = $this->fields($model, $model->alias);
1040:         } elseif (!empty($model->hasMany) && $model->recursive > -1) {
1041:             $assocFields = $this->fields($model, $model->alias, array("{$model->alias}.{$model->primaryKey}"));
1042:             $passedFields = $this->fields($model, $model->alias, $queryData['fields']);
1043: 
1044:             if (count($passedFields) === 1) {
1045:                 $match = strpos($passedFields[0], $assocFields[0]);
1046:                 $match1 = strpos($passedFields[0], 'COUNT(');
1047:                 if ($match === false && $match1 === false) {
1048:                     $queryData['fields'] = array_merge($passedFields, $assocFields);
1049:                 } else {
1050:                     $queryData['fields'] = $passedFields;
1051:                 }
1052:             } else {
1053:                 $queryData['fields'] = array_merge($passedFields, $assocFields);
1054:             }
1055:             unset($assocFields, $passedFields);
1056:         }
1057: 
1058:         if ($linkModel == null) {
1059:             return $this->buildStatement(
1060:                 array(
1061:                     'fields' => array_unique($queryData['fields']),
1062:                     'table' => $this->fullTableName($model),
1063:                     'alias' => $model->alias,
1064:                     'limit' => $queryData['limit'],
1065:                     'offset' => $queryData['offset'],
1066:                     'joins' => $queryData['joins'],
1067:                     'conditions' => $queryData['conditions'],
1068:                     'order' => $queryData['order'],
1069:                     'group' => $queryData['group']
1070:                 ),
1071:                 $model
1072:             );
1073:         }
1074:         if ($external && !empty($assocData['finderQuery'])) {
1075:             return $assocData['finderQuery'];
1076:         }
1077: 
1078:         $alias = $association;
1079:         $self = ($model->name == $linkModel->name);
1080:         $fields = array();
1081: 
1082:         if ((!$external && in_array($type, array('hasOne', 'belongsTo')) && $this->__bypass === false) || $external) {
1083:             $fields = $this->fields($linkModel, $alias, $assocData['fields']);
1084:         }
1085:         if (empty($assocData['offset']) && !empty($assocData['page'])) {
1086:             $assocData['offset'] = ($assocData['page'] - 1) * $assocData['limit'];
1087:         }
1088:         $assocData['limit'] = $this->limit($assocData['limit'], $assocData['offset']);
1089: 
1090:         switch ($type) {
1091:             case 'hasOne':
1092:             case 'belongsTo':
1093:                 $conditions = $this->__mergeConditions(
1094:                     $assocData['conditions'],
1095:                     $this->getConstraint($type, $model, $linkModel, $alias, array_merge($assocData, compact('external', 'self')))
1096:                 );
1097: 
1098:                 if (!$self && $external) {
1099:                     foreach ($conditions as $key => $condition) {
1100:                         if (is_numeric($key) && strpos($condition, $model->alias . '.') !== false) {
1101:                             unset($conditions[$key]);
1102:                         }
1103:                     }
1104:                 }
1105: 
1106:                 if ($external) {
1107:                     $query = array_merge($assocData, array(
1108:                         'conditions' => $conditions,
1109:                         'table' => $this->fullTableName($linkModel),
1110:                         'fields' => $fields,
1111:                         'alias' => $alias,
1112:                         'group' => null
1113:                     ));
1114:                     $query = array_merge(array('order' => $assocData['order'], 'limit' => $assocData['limit']), $query);
1115:                 } else {
1116:                     $join = array(
1117:                         'table' => $this->fullTableName($linkModel),
1118:                         'alias' => $alias,
1119:                         'type' => isset($assocData['type']) ? $assocData['type'] : 'LEFT',
1120:                         'conditions' => trim($this->conditions($conditions, true, false, $model))
1121:                     );
1122:                     $queryData['fields'] = array_merge($queryData['fields'], $fields);
1123: 
1124:                     if (!empty($assocData['order'])) {
1125:                         $queryData['order'][] = $assocData['order'];
1126:                     }
1127:                     if (!in_array($join, $queryData['joins'])) {
1128:                         $queryData['joins'][] = $join;
1129:                     }
1130:                     return true;
1131:                 }
1132:             break;
1133:             case 'hasMany':
1134:                 $assocData['fields'] = $this->fields($linkModel, $alias, $assocData['fields']);
1135:                 if (!empty($assocData['foreignKey'])) {
1136:                     $assocData['fields'] = array_merge($assocData['fields'], $this->fields($linkModel, $alias, array("{$alias}.{$assocData['foreignKey']}")));
1137:                 }
1138:                 $query = array(
1139:                     'conditions' => $this->__mergeConditions($this->getConstraint('hasMany', $model, $linkModel, $alias, $assocData), $assocData['conditions']),
1140:                     'fields' => array_unique($assocData['fields']),
1141:                     'table' => $this->fullTableName($linkModel),
1142:                     'alias' => $alias,
1143:                     'order' => $assocData['order'],
1144:                     'limit' => $assocData['limit'],
1145:                     'group' => null
1146:                 );
1147:             break;
1148:             case 'hasAndBelongsToMany':
1149:                 $joinFields = array();
1150:                 $joinAssoc = null;
1151: 
1152:                 if (isset($assocData['with']) && !empty($assocData['with'])) {
1153:                     $joinKeys = array($assocData['foreignKey'], $assocData['associationForeignKey']);
1154:                     list($with, $joinFields) = $model->joinModel($assocData['with'], $joinKeys);
1155: 
1156:                     $joinTbl = $this->fullTableName($model->{$with});
1157:                     $joinAlias = $joinTbl;
1158: 
1159:                     if (is_array($joinFields) && !empty($joinFields)) {
1160:                         $joinFields = $this->fields($model->{$with}, $model->{$with}->alias, $joinFields);
1161:                         $joinAssoc = $joinAlias = $model->{$with}->alias;
1162:                     } else {
1163:                         $joinFields = array();
1164:                     }
1165:                 } else {
1166:                     $joinTbl = $this->fullTableName($assocData['joinTable']);
1167:                     $joinAlias = $joinTbl;
1168:                 }
1169:                 $query = array(
1170:                     'conditions' => $assocData['conditions'],
1171:                     'limit' => $assocData['limit'],
1172:                     'table' => $this->fullTableName($linkModel),
1173:                     'alias' => $alias,
1174:                     'fields' => array_merge($this->fields($linkModel, $alias, $assocData['fields']), $joinFields),
1175:                     'order' => $assocData['order'],
1176:                     'group' => null,
1177:                     'joins' => array(array(
1178:                         'table' => $joinTbl,
1179:                         'alias' => $joinAssoc,
1180:                         'conditions' => $this->getConstraint('hasAndBelongsToMany', $model, $linkModel, $joinAlias, $assocData, $alias)
1181:                     ))
1182:                 );
1183:             break;
1184:         }
1185:         if (isset($query)) {
1186:             return $this->buildStatement($query, $model);
1187:         }
1188:         return null;
1189:     }
1190: /**
1191:  * Returns a conditions array for the constraint between two models
1192:  *
1193:  * @param string $type Association type
1194:  * @param object $model Model object
1195:  * @param array $association Association array
1196:  * @return array Conditions array defining the constraint between $model and $association
1197:  */
1198:     function getConstraint($type, $model, $linkModel, $alias, $assoc, $alias2 = null) {
1199:         $assoc = array_merge(array('external' => false, 'self' => false), $assoc);
1200: 
1201:         if (array_key_exists('foreignKey', $assoc) && empty($assoc['foreignKey'])) {
1202:             return array();
1203:         }
1204: 
1205:         switch (true) {
1206:             case ($assoc['external'] && $type == 'hasOne'):
1207:                 return array("{$alias}.{$assoc['foreignKey']}" => '{$__cakeID__$}');
1208:             break;
1209:             case ($assoc['external'] && $type == 'belongsTo'):
1210:                 return array("{$alias}.{$linkModel->primaryKey}" => '{$__cakeForeignKey__$}');
1211:             break;
1212:             case (!$assoc['external'] && $type == 'hasOne'):
1213:                 return array("{$alias}.{$assoc['foreignKey']}" => $this->identifier("{$model->alias}.{$model->primaryKey}"));
1214:             break;
1215:             case (!$assoc['external'] && $type == 'belongsTo'):
1216:                 return array("{$model->alias}.{$assoc['foreignKey']}" => $this->identifier("{$alias}.{$linkModel->primaryKey}"));
1217:             break;
1218:             case ($type == 'hasMany'):
1219:                 return array("{$alias}.{$assoc['foreignKey']}" => array('{$__cakeID__$}'));
1220:             break;
1221:             case ($type == 'hasAndBelongsToMany'):
1222:                 return array(
1223:                     array("{$alias}.{$assoc['foreignKey']}" => '{$__cakeID__$}'),
1224:                     array("{$alias}.{$assoc['associationForeignKey']}" => $this->identifier("{$alias2}.{$linkModel->primaryKey}"))
1225:                 );
1226:             break;
1227:         }
1228:         return array();
1229:     }
1230: /**
1231:  * Builds and generates a JOIN statement from an array.  Handles final clean-up before conversion.
1232:  *
1233:  * @param array $join An array defining a JOIN statement in a query
1234:  * @return string An SQL JOIN statement to be used in a query
1235:  * @see DboSource::renderJoinStatement()
1236:  * @see DboSource::buildStatement()
1237:  */
1238:     function buildJoinStatement($join) {
1239:         $data = array_merge(array(
1240:             'type' => null,
1241:             'alias' => null,
1242:             'table' => 'join_table',
1243:             'conditions' => array()
1244:         ), $join);
1245: 
1246:         if (!empty($data['alias'])) {
1247:             $data['alias'] = $this->alias . $this->name($data['alias']);
1248:         }
1249:         if (!empty($data['conditions'])) {
1250:             $data['conditions'] = trim($this->conditions($data['conditions'], true, false));
1251:         }
1252:         return $this->renderJoinStatement($data);
1253:     }
1254: /**
1255:  * Builds and generates an SQL statement from an array.  Handles final clean-up before conversion.
1256:  *
1257:  * @param array $query An array defining an SQL query
1258:  * @param object $model The model object which initiated the query
1259:  * @return string An executable SQL statement
1260:  * @see DboSource::renderStatement()
1261:  */
1262:     function buildStatement($query, $model) {
1263:         $query = array_merge(array('offset' => null, 'joins' => array()), $query);
1264:         if (!empty($query['joins'])) {
1265:             $count = count($query['joins']);
1266:             for ($i = 0; $i < $count; $i++) {
1267:                 if (is_array($query['joins'][$i])) {
1268:                     $query['joins'][$i] = $this->buildJoinStatement($query['joins'][$i]);
1269:                 }
1270:             }
1271:         }
1272:         return $this->renderStatement('select', array(
1273:             'conditions' => $this->conditions($query['conditions'], true, true, $model),
1274:             'fields' => implode(', ', $query['fields']),
1275:             'table' => $query['table'],
1276:             'alias' => $this->alias . $this->name($query['alias']),
1277:             'order' => $this->order($query['order']),
1278:             'limit' => $this->limit($query['limit'], $query['offset']),
1279:             'joins' => implode(' ', $query['joins']),
1280:             'group' => $this->group($query['group'])
1281:         ));
1282:     }
1283: /**
1284:  * Renders a final SQL JOIN statement
1285:  *
1286:  * @param array $data
1287:  * @return string
1288:  */
1289:     function renderJoinStatement($data) {
1290:         extract($data);
1291:         return trim("{$type} JOIN {$table} {$alias} ON ({$conditions})");
1292:     }
1293: /**
1294:  * Renders a final SQL statement by putting together the component parts in the correct order
1295:  *
1296:  * @param string $type
1297:  * @param array $data
1298:  * @return string
1299:  */
1300:     function renderStatement($type, $data) {
1301:         extract($data);
1302:         $aliases = null;
1303: 
1304:         switch (strtolower($type)) {
1305:             case 'select':
1306:                 return "SELECT {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order} {$limit}";
1307:             break;
1308:             case 'create':
1309:                 return "INSERT INTO {$table} ({$fields}) VALUES ({$values})";
1310:             break;
1311:             case 'update':
1312:                 if (!empty($alias)) {
1313:                     $aliases = "{$this->alias}{$alias} {$joins} ";
1314:                 }
1315:                 return "UPDATE {$table} {$aliases}SET {$fields} {$conditions}";
1316:             break;
1317:             case 'delete':
1318:                 if (!empty($alias)) {
1319:                     $aliases = "{$this->alias}{$alias} {$joins} ";
1320:                 }
1321:                 return "DELETE {$alias} FROM {$table} {$aliases}{$conditions}";
1322:             break;
1323:             case 'schema':
1324:                 foreach (array('columns', 'indexes') as $var) {
1325:                     if (is_array(${$var})) {
1326:                         ${$var} = "\t" . implode(",\n\t", array_filter(${$var}));
1327:                     }
1328:                 }
1329:                 if (trim($indexes) != '') {
1330:                     $columns .= ',';
1331:                 }
1332:                 return "CREATE TABLE {$table} (\n{$columns}{$indexes});";
1333:             break;
1334:             case 'alter':
1335:             break;
1336:         }
1337:     }
1338: /**
1339:  * Merges a mixed set of string/array conditions
1340:  *
1341:  * @return array
1342:  */
1343:     function __mergeConditions($query, $assoc) {
1344:         if (empty($assoc)) {
1345:             return $query;
1346:         }
1347: 
1348:         if (is_array($query)) {
1349:             return array_merge((array)$assoc, $query);
1350:         }
1351: 
1352:         if (!empty($query)) {
1353:             $query = array($query);
1354:             if (is_array($assoc)) {
1355:                 $query = array_merge($query, $assoc);
1356:             } else {
1357:                 $query[] = $assoc;
1358:             }
1359:             return $query;
1360:         }
1361: 
1362:         return $assoc;
1363:     }
1364: /**
1365:  * Generates and executes an SQL UPDATE statement for given model, fields, and values.
1366:  * For databases that do not support aliases in UPDATE queries.
1367:  *
1368:  * @param Model $model
1369:  * @param array $fields
1370:  * @param array $values
1371:  * @param mixed $conditions
1372:  * @return boolean Success
1373:  */
1374:     function update(&$model, $fields = array(), $values = null, $conditions = null) {
1375:         if ($values == null) {
1376:             $combined = $fields;
1377:         } else {
1378:             $combined = array_combine($fields, $values);
1379:         }
1380: 
1381:         $fields = implode(', ', $this->_prepareUpdateFields($model, $combined, empty($conditions)));
1382: 
1383:         $alias = $joins = null;
1384:         $table = $this->fullTableName($model);
1385:         $conditions = $this->_matchRecords($model, $conditions);
1386: 
1387:         if ($conditions === false) {
1388:             return false;
1389:         }
1390:         $query = compact('table', 'alias', 'joins', 'fields', 'conditions');
1391: 
1392:         if (!$this->execute($this->renderStatement('update', $query))) {
1393:             $model->onError();
1394:             return false;
1395:         }
1396:         return true;
1397:     }
1398: /**
1399:  * Quotes and prepares fields and values for an SQL UPDATE statement
1400:  *
1401:  * @param Model $model
1402:  * @param array $fields
1403:  * @param boolean $quoteValues If values should be quoted, or treated as SQL snippets
1404:  * @param boolean $alias Include the model alias in the field name
1405:  * @return array Fields and values, quoted and preparted
1406:  * @access protected
1407:  */
1408:     function _prepareUpdateFields(&$model, $fields, $quoteValues = true, $alias = false) {
1409:         $quotedAlias = $this->startQuote . $model->alias . $this->endQuote;
1410: 
1411:         $updates = array();
1412:         foreach ($fields as $field => $value) {
1413:             if ($alias && strpos($field, '.') === false) {
1414:                 $quoted = $model->escapeField($field);
1415:             } elseif (!$alias && strpos($field, '.') !== false) {
1416:                 $quoted = $this->name(str_replace($quotedAlias . '.', '', str_replace(
1417:                     $model->alias . '.', '', $field
1418:                 )));
1419:             } else {
1420:                 $quoted = $this->name($field);
1421:             }
1422: 
1423:             if ($value === null) {
1424:                 $updates[] = $quoted . ' = NULL';
1425:                 continue;
1426:             }
1427:             $update = $quoted . ' = ';
1428: 
1429:             if ($quoteValues) {
1430:                 $update .= $this->value($value, $model->getColumnType($field), false);
1431:             } elseif (!$alias) {
1432:                 $update .= str_replace($quotedAlias . '.', '', str_replace(
1433:                     $model->alias . '.', '', $value
1434:                 ));
1435:             } else {
1436:                 $update .= $value;
1437:             }
1438:             $updates[] =  $update;
1439:         }
1440:         return $updates;
1441:     }
1442: /**
1443:  * Generates and executes an SQL DELETE statement.
1444:  * For databases that do not support aliases in UPDATE queries.
1445:  *
1446:  * @param Model $model
1447:  * @param mixed $conditions
1448:  * @return boolean Success
1449:  */
1450:     function delete(&$model, $conditions = null) {
1451:         $alias = $joins = null;
1452:         $table = $this->fullTableName($model);
1453:         $conditions = $this->_matchRecords($model, $conditions);
1454: 
1455:         if ($conditions === false) {
1456:             return false;
1457:         }
1458: 
1459:         if ($this->execute($this->renderStatement('delete', compact('alias', 'table', 'joins', 'conditions'))) === false) {
1460:             $model->onError();
1461:             return false;
1462:         }
1463:         return true;
1464:     }
1465: /**
1466:  * Gets a list of record IDs for the given conditions.  Used for multi-record updates and deletes
1467:  * in databases that do not support aliases in UPDATE/DELETE queries.
1468:  *
1469:  * @param Model $model
1470:  * @param mixed $conditions
1471:  * @return array List of record IDs
1472:  * @access protected
1473:  */
1474:     function _matchRecords(&$model, $conditions = null) {
1475:         if ($conditions === true) {
1476:             $conditions = $this->conditions(true);
1477:         } elseif ($conditions === null) {
1478:             $conditions = $this->conditions($this->defaultConditions($model, $conditions, false), true, true, $model);
1479:         } else {
1480:             $noJoin = true;
1481:             foreach ($conditions as $field => $value) {
1482:                 $originalField = $field;
1483:                 if (strpos($field, '.') !== false) {
1484:                     list($alias, $field) = explode('.', $field);
1485:                 }
1486:                 if (!$model->hasField($field)) {
1487:                     $noJoin = false;
1488:                     break;
1489:                 }
1490:                 if ($field !== $originalField) {
1491:                     $conditions[$field] = $value;
1492:                     unset($conditions[$originalField]);
1493:                 }
1494:             }
1495:             if ($noJoin === true) {
1496:                 return $this->conditions($conditions);
1497:             }
1498:             $idList = $model->find('all', array(
1499:                 'fields' => "{$model->alias}.{$model->primaryKey}",
1500:                 'conditions' => $conditions
1501:             ));
1502: 
1503:             if (empty($idList)) {
1504:                 return false;
1505:             }
1506:             $conditions = $this->conditions(array(
1507:                 $model->primaryKey => Set::extract($idList, "{n}.{$model->alias}.{$model->primaryKey}")
1508:             ));
1509:         }
1510:         return $conditions;
1511:     }
1512: /**
1513:  * Returns an array of SQL JOIN fragments from a model's associations
1514:  *
1515:  * @param object $model
1516:  * @return array
1517:  */
1518:     function _getJoins($model) {
1519:         $join = array();
1520:         $joins = array_merge($model->getAssociated('hasOne'), $model->getAssociated('belongsTo'));
1521: 
1522:         foreach ($joins as $assoc) {
1523:             if (isset($model->{$assoc}) && $model->useDbConfig == $model->{$assoc}->useDbConfig) {
1524:                 $assocData = $model->getAssociated($assoc);
1525:                 $join[] = $this->buildJoinStatement(array(
1526:                     'table' => $this->fullTableName($model->{$assoc}),
1527:                     'alias' => $assoc,
1528:                     'type' => isset($assocData['type']) ? $assocData['type'] : 'LEFT',
1529:                     'conditions' => trim($this->conditions(
1530:                         $this->__mergeConditions($assocData['conditions'], $this->getConstraint($assocData['association'], $model, $model->{$assoc}, $assoc, $assocData)),
1531:                         true, false, $model
1532:                     ))
1533:                 ));
1534:             }
1535:         }
1536:         return $join;
1537:     }
1538: /**
1539:  * Returns an SQL calculation, i.e. COUNT() or MAX()
1540:  *
1541:  * @param model $model
1542:  * @param string $func Lowercase name of SQL function, i.e. 'count' or 'max'
1543:  * @param array $params Function parameters (any values must be quoted manually)
1544:  * @return string An SQL calculation function
1545:  * @access public
1546:  */
1547:     function calculate(&$model, $func, $params = array()) {
1548:         $params = (array)$params;
1549: 
1550:         switch (strtolower($func)) {
1551:             case 'count':
1552:                 if (!isset($params[0])) {
1553:                     $params[0] = '*';
1554:                 }
1555:                 if (!isset($params[1])) {
1556:                     $params[1] = 'count';
1557:                 }
1558:                 return 'COUNT(' . $this->name($params[0]) . ') AS ' . $this->name($params[1]);
1559:             case 'max':
1560:             case 'min':
1561:                 if (!isset($params[1])) {
1562:                     $params[1] = $params[0];
1563:                 }
1564:                 return strtoupper($func) . '(' . $this->name($params[0]) . ') AS ' . $this->name($params[1]);
1565:             break;
1566:         }
1567:     }
1568: /**
1569:  * Deletes all the records in a table and resets the count of the auto-incrementing
1570:  * primary key, where applicable.
1571:  *
1572:  * @param mixed $table A string or model class representing the table to be truncated
1573:  * @return boolean  SQL TRUNCATE TABLE statement, false if not applicable.
1574:  * @access public
1575:  */
1576:     function truncate($table) {
1577:         return $this->execute('TRUNCATE TABLE ' . $this->fullTableName($table));
1578:     }
1579: /**
1580:  * Begin a transaction
1581:  *
1582:  * @param model $model
1583:  * @return boolean True on success, false on fail
1584:  * (i.e. if the database/model does not support transactions,
1585:  * or a transaction has not started).
1586:  */
1587:     function begin(&$model) {
1588:         if (parent::begin($model) && $this->execute($this->_commands['begin'])) {
1589:             $this->_transactionStarted = true;
1590:             return true;
1591:         }
1592:         return false;
1593:     }
1594: /**
1595:  * Commit a transaction
1596:  *
1597:  * @param model $model
1598:  * @return boolean True on success, false on fail
1599:  * (i.e. if the database/model does not support transactions,
1600:  * or a transaction has not started).
1601:  */
1602:     function commit(&$model) {
1603:         if (parent::commit($model) && $this->execute($this->_commands['commit'])) {
1604:             $this->_transactionStarted = false;
1605:             return true;
1606:         }
1607:         return false;
1608:     }
1609: /**
1610:  * Rollback a transaction
1611:  *
1612:  * @param model $model
1613:  * @return boolean True on success, false on fail
1614:  * (i.e. if the database/model does not support transactions,
1615:  * or a transaction has not started).
1616:  */
1617:     function rollback(&$model) {
1618:         if (parent::rollback($model) && $this->execute($this->_commands['rollback'])) {
1619:             $this->_transactionStarted = false;
1620:             return true;
1621:         }
1622:         return false;
1623:     }
1624: /**
1625:  * Creates a default set of conditions from the model if $conditions is null/empty.
1626:  * If conditions are supplied then they will be returned.  If a model doesn't exist and no conditions
1627:  * were provided either null or false will be returned based on what was input.
1628:  *
1629:  * @param object $model
1630:  * @param mixed $conditions Array of conditions, conditions string, null or false. If an array of conditions,
1631:  *   or string conditions those conditions will be returned.  With other values the model's existance will be checked.
1632:  *   If the model doesn't exist a null or false will be returned depending on the input value.
1633:  * @param boolean $useAlias Use model aliases rather than table names when generating conditions
1634:  * @return mixed Either null, false, $conditions or an array of default conditions to use.
1635:  * @see DboSource::update()
1636:  * @see DboSource::conditions()
1637:  */
1638:     function defaultConditions(&$model, $conditions, $useAlias = true) {
1639:         if (!empty($conditions)) {
1640:             return $conditions;
1641:         }
1642:         $exists = $model->exists();
1643:         if (!$exists && $conditions !== null) {
1644:             return false;
1645:         } elseif (!$exists) {
1646:             return null;
1647:         }
1648:         $alias = $model->alias;
1649: 
1650:         if (!$useAlias) {
1651:             $alias = $this->fullTableName($model, false);
1652:         }
1653:         return array("{$alias}.{$model->primaryKey}" => $model->getID());
1654:     }
1655: /**
1656:  * Returns a key formatted like a string Model.fieldname(i.e. Post.title, or Country.name)
1657:  *
1658:  * @param unknown_type $model
1659:  * @param unknown_type $key
1660:  * @param unknown_type $assoc
1661:  * @return string
1662:  */
1663:     function resolveKey($model, $key, $assoc = null) {
1664:         if (empty($assoc)) {
1665:             $assoc = $model->alias;
1666:         }
1667:         if (!strpos('.', $key)) {
1668:             return $this->name($model->alias) . '.' . $this->name($key);
1669:         }
1670:         return $key;
1671:     }
1672: /**
1673:  * Private helper method to remove query metadata in given data array.
1674:  *
1675:  * @param array $data
1676:  * @return array
1677:  */
1678:     function __scrubQueryData($data) {
1679:         foreach (array('conditions', 'fields', 'joins', 'order', 'limit', 'offset', 'group') as $key) {
1680:             if (!isset($data[$key]) || empty($data[$key])) {
1681:                 $data[$key] = array();
1682:             }
1683:         }
1684:         return $data;
1685:     }
1686: /**
1687:  * Generates the fields list of an SQL query.
1688:  *
1689:  * @param Model $model
1690:  * @param string $alias Alias tablename
1691:  * @param mixed $fields
1692:  * @param boolean $quote If false, returns fields array unquoted
1693:  * @return array
1694:  */
1695:     function fields(&$model, $alias = null, $fields = array(), $quote = true) {
1696:         if (empty($alias)) {
1697:             $alias = $model->alias;
1698:         }
1699:         if (empty($fields)) {
1700:             $fields = array_keys($model->schema());
1701:         } elseif (!is_array($fields)) {
1702:             $fields = String::tokenize($fields);
1703:         }
1704:         $fields = array_values(array_filter($fields));
1705: 
1706:         if (!$quote) {
1707:             return $fields;
1708:         }
1709:         $count = count($fields);
1710: 
1711:         if ($count >= 1 && !in_array($fields[0], array('*', 'COUNT(*)'))) {
1712:             for ($i = 0; $i < $count; $i++) {
1713:                 if (is_object($fields[$i]) && isset($fields[$i]->type) && $fields[$i]->type === 'expression') {
1714:                     $fields[$i] = $fields[$i]->value;
1715:                 } elseif (preg_match('/^\(.*\)\s' . $this->alias . '.*/i', $fields[$i])){
1716:                     continue;
1717:                 } elseif (!preg_match('/^.+\\(.*\\)/', $fields[$i])) {
1718:                     $prepend = '';
1719: 
1720:                     if (strpos($fields[$i], 'DISTINCT') !== false) {
1721:                         $prepend = 'DISTINCT ';
1722:                         $fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i]));
1723:                     }
1724:                     $dot = strpos($fields[$i], '.');
1725: 
1726:                     if ($dot === false) {
1727:                         $prefix = !(
1728:                             strpos($fields[$i], ' ') !== false ||
1729:                             strpos($fields[$i], '(') !== false
1730:                         );
1731:                         $fields[$i] = $this->name(($prefix ? $alias . '.' : '') . $fields[$i]);
1732:                     } else {
1733:                         $value = array();
1734:                         $comma = strpos($fields[$i], ',');
1735:                         if ($comma === false) {
1736:                             $build = explode('.', $fields[$i]);
1737:                             if (!Set::numeric($build)) {
1738:                                 $fields[$i] = $this->name($build[0] . '.' . $build[1]);
1739:                             }
1740:                             $comma = String::tokenize($fields[$i]);
1741:                             foreach ($comma as $string) {
1742:                                 if (preg_match('/^[0-9]+\.[0-9]+$/', $string)) {
1743:                                     $value[] = $string;
1744:                                 } else {
1745:                                     $build = explode('.', $string);
1746:                                     $value[] = $this->name(trim($build[0]) . '.' . trim($build[1]));
1747:                                 }
1748:                             }
1749:                             $fields[$i] = implode(', ', $value);
1750:                         }
1751:                     }
1752:                     $fields[$i] = $prepend . $fields[$i];
1753:                 } elseif (preg_match('/\(([\.\w]+)\)/', $fields[$i], $field)) {
1754:                     if (isset($field[1])) {
1755:                         if (strpos($field[1], '.') === false) {
1756:                             $field[1] = $this->name($alias . '.' . $field[1]);
1757:                         } else {
1758:                             $field[0] = explode('.', $field[1]);
1759:                             if (!Set::numeric($field[0])) {
1760:                                 $field[0] = implode('.', array_map(array($this, 'name'), $field[0]));
1761:                                 $fields[$i] = preg_replace('/\(' . $field[1] . '\)/', '(' . $field[0] . ')', $fields[$i], 1);
1762:                             }
1763:                         }
1764:                     }
1765:                 }
1766:             }
1767:         }
1768:         return array_unique($fields);
1769:     }
1770: /**
1771:  * Creates a WHERE clause by parsing given conditions data.  If an array or string
1772:  * conditions are provided those conditions will be parsed and quoted.  If a boolean
1773:  * is given it will be integer cast as condition.  Null will return 1 = 1.
1774:  *
1775:  * @param mixed $conditions Array or string of conditions, or any value.
1776:  * @param boolean $quoteValues If true, values should be quoted
1777:  * @param boolean $where If true, "WHERE " will be prepended to the return value
1778:  * @param Model $model A reference to the Model instance making the query
1779:  * @return string SQL fragment
1780:  */
1781:     function conditions($conditions, $quoteValues = true, $where = true, $model = null) {
1782:         $clause = $out = '';
1783: 
1784:         if ($where) {
1785:             $clause = ' WHERE ';
1786:         }
1787: 
1788:         if (is_array($conditions) && !empty($conditions)) {
1789:             $out = $this->conditionKeysToString($conditions, $quoteValues, $model);
1790: 
1791:             if (empty($out)) {
1792:                 return $clause . ' 1 = 1';
1793:             }
1794:             return $clause . implode(' AND ', $out);
1795:         }
1796:         if ($conditions === false || $conditions === true) {
1797:             return $clause . (int)$conditions . ' = 1';
1798:         }
1799: 
1800:         if (empty($conditions) || trim($conditions) == '') {
1801:             return $clause . '1 = 1';
1802:         }
1803:         $clauses = '/^WHERE\\x20|^GROUP\\x20BY\\x20|^HAVING\\x20|^ORDER\\x20BY\\x20/i';
1804: 
1805:         if (preg_match($clauses, $conditions, $match)) {
1806:             $clause = '';
1807:         }
1808:         if (trim($conditions) == '') {
1809:             $conditions = ' 1 = 1';
1810:         } else {
1811:             $conditions = $this->__quoteFields($conditions);
1812:         }
1813:         return $clause . $conditions;
1814:     }
1815: /**
1816:  * Creates a WHERE clause by parsing given conditions array.  Used by DboSource::conditions().
1817:  *
1818:  * @param array $conditions Array or string of conditions
1819:  * @param boolean $quoteValues If true, values should be quoted
1820:  * @param Model $model A reference to the Model instance making the query
1821:  * @return string SQL fragment
1822:  */
1823:     function conditionKeysToString($conditions, $quoteValues = true, $model = null) {
1824:         $c = 0;
1825:         $out = array();
1826:         $data = $columnType = null;
1827:         $bool = array('and', 'or', 'not', 'and not', 'or not', 'xor', '||', '&&');
1828: 
1829:         foreach ($conditions as $key => $value) {
1830:             $join = ' AND ';
1831:             $not = null;
1832: 
1833:             if (is_array($value)) {
1834:                 $valueInsert = (
1835:                     !empty($value) &&
1836:                     (substr_count($key, '?') == count($value) || substr_count($key, ':') == count($value))
1837:                 );
1838:             }
1839: 
1840:             if (is_numeric($key) && empty($value)) {
1841:                 continue;
1842:             } elseif (is_numeric($key) && is_string($value)) {
1843:                 $out[] = $not . $this->__quoteFields($value);
1844:             } elseif ((is_numeric($key) && is_array($value)) || in_array(strtolower(trim($key)), $bool)) {
1845:                 if (in_array(strtolower(trim($key)), $bool)) {
1846:                     $join = ' ' . strtoupper($key) . ' ';
1847:                 } else {
1848:                     $key = $join;
1849:                 }
1850:                 $value = $this->conditionKeysToString($value, $quoteValues, $model);
1851: 
1852:                 if (strpos($join, 'NOT') !== false) {
1853:                     if (strtoupper(trim($key)) == 'NOT') {
1854:                         $key = 'AND ' . trim($key);
1855:                     }
1856:                     $not = 'NOT ';
1857:                 }
1858: 
1859:                 if (empty($value[1])) {
1860:                     if ($not) {
1861:                         $out[] = $not . '(' . $value[0] . ')';
1862:                     } else {
1863:                         $out[] = $value[0] ;
1864:                     }
1865:                 } else {
1866:                     $out[] = '(' . $not . '(' . implode(') ' . strtoupper($key) . ' (', $value) . '))';
1867:                 }
1868: 
1869:             } else {
1870:                 if (is_object($value) && isset($value->type)) {
1871:                     if ($value->type == 'identifier') {
1872:                         $data .= $this->name($key) . ' = ' . $this->name($value->value);
1873:                     } elseif ($value->type == 'expression') {
1874:                         if (is_numeric($key)) {
1875:                             $data .= $value->value;
1876:                         } else {
1877:                             $data .= $this->name($key) . ' = ' . $value->value;
1878:                         }
1879:                     }
1880:                 } elseif (is_array($value) && !empty($value) && !$valueInsert) {
1881:                     $keys = array_keys($value);
1882:                     if (array_keys($value) === array_values(array_keys($value))) {
1883:                         $count = count($value);
1884:                         if ($count === 1) {
1885:                             $data = $this->__quoteFields($key) . ' = (';
1886:                         } else {
1887:                             $data = $this->__quoteFields($key) . ' IN (';
1888:                         }
1889:                         if ($quoteValues || strpos($value[0], '-!') !== 0) {
1890:                             if (is_object($model)) {
1891:                                 $columnType = $model->getColumnType($key);
1892:                             }
1893:                             $data .= implode(', ', $this->value($value, $columnType));
1894:                         }
1895:                         $data .= ')';
1896:                     } else {
1897:                         $ret = $this->conditionKeysToString($value, $quoteValues, $model);
1898:                         if (count($ret) > 1) {
1899:                             $data = '(' . implode(') AND (', $ret) . ')';
1900:                         } elseif (isset($ret[0])) {
1901:                             $data = $ret[0];
1902:                         }
1903:                     }
1904:                 } elseif (is_numeric($key) && !empty($value)) {
1905:                     $data = $this->__quoteFields($value);
1906:                 } else {
1907:                     $data = $this->__parseKey($model, trim($key), $value);
1908:                 }
1909: 
1910:                 if ($data != null) {
1911:                     if (preg_match('/^\(\(\((.+)\)\)\)$/', $data)) {
1912:                         $data = substr($data, 1, strlen($data) - 2);
1913:                     }
1914:                     $out[] = $data;
1915:                     $data = null;
1916:                 }
1917:             }
1918:             $c++;
1919:         }
1920:         return $out;
1921:     }
1922: /**
1923:  * Extracts a Model.field identifier and an SQL condition operator from a string, formats
1924:  * and inserts values, and composes them into an SQL snippet.
1925:  *
1926:  * @param Model $model Model object initiating the query
1927:  * @param string $key An SQL key snippet containing a field and optional SQL operator
1928:  * @param mixed $value The value(s) to be inserted in the string
1929:  * @return string
1930:  * @access private
1931:  */
1932:     function __parseKey($model, $key, $value) {
1933:         $operatorMatch = '/^((' . implode(')|(', $this->__sqlOps);
1934:         $operatorMatch .= '\\x20)|<[>=]?(?![^>]+>)\\x20?|[>=!]{1,3}(?!<)\\x20?)/is';
1935:         $bound = (strpos($key, '?') !== false || (is_array($value) && strpos($key, ':') !== false));
1936: 
1937:         if (!strpos($key, ' ')) {
1938:             $operator = '=';
1939:         } else {
1940:             list($key, $operator) = explode(' ', trim($key), 2);
1941: 
1942:             if (!preg_match($operatorMatch, trim($operator)) && strpos($operator, ' ') !== false) {
1943:                 $key = $key . ' ' . $operator;
1944:                 $split = strrpos($key, ' ');
1945:                 $operator = substr($key, $split);
1946:                 $key = substr($key, 0, $split);
1947:             }
1948:         }
1949: 
1950: 
1951:         $type = (is_object($model) ? $model->getColumnType($key) : null);
1952: 
1953:         $null = ($value === null || (is_array($value) && empty($value)));
1954: 
1955:         if (strtolower($operator) === 'not') {
1956:             $data = $this->conditionKeysToString(
1957:                 array($operator => array($key => $value)), true, $model
1958:             );
1959:             return $data[0];
1960:         }
1961: 
1962:         $value = $this->value($value, $type);
1963: 
1964:         if ($key !== '?') {
1965:             $isKey = (strpos($key, '(') !== false || strpos($key, ')') !== false);
1966:             $key = $isKey ? $this->__quoteFields($key) : $this->name($key);
1967:         }
1968: 
1969:         if ($bound) {
1970:             return String::insert($key . ' ' . trim($operator), $value);
1971:         }
1972: 
1973:         if (!preg_match($operatorMatch, trim($operator))) {
1974:             $operator .= ' =';
1975:         }
1976:         $operator = trim($operator);
1977: 
1978:         if (is_array($value)) {
1979:             $value = implode(', ', $value);
1980: 
1981:             switch ($operator) {
1982:                 case '=':
1983:                     $operator = 'IN';
1984:                 break;
1985:                 case '!=':
1986:                 case '<>':
1987:                     $operator = 'NOT IN';
1988:                 break;
1989:             }
1990:             $value = "({$value})";
1991:         } elseif ($null) {
1992:             switch ($operator) {
1993:                 case '=':
1994:                     $operator = 'IS';
1995:                 break;
1996:                 case '!=':
1997:                 case '<>':
1998:                     $operator = 'IS NOT';
1999:                 break;
2000:             }
2001:         }
2002: 
2003:         return "{$key} {$operator} {$value}";
2004:     }
2005: /**
2006:  * Quotes Model.fields
2007:  *
2008:  * @param string $conditions
2009:  * @return string or false if no match
2010:  * @access private
2011:  */
2012:     function __quoteFields($conditions) {
2013:         $start = $end  = null;
2014:         $original = $conditions;
2015: 
2016:         if (!empty($this->startQuote)) {
2017:             $start = preg_quote($this->startQuote);
2018:         }
2019:         if (!empty($this->endQuote)) {
2020:             $end = preg_quote($this->endQuote);
2021:         }
2022:         $conditions = str_replace(array($start, $end), '', $conditions);
2023:         preg_match_all('/(?:[\'\"][^\'\"\\\]*(?:\\\.[^\'\"\\\]*)*[\'\"])|([a-z0-9_' . $start . $end . ']*\\.[a-z0-9_' . $start . $end . ']*)/i', $conditions, $replace, PREG_PATTERN_ORDER);
2024: 
2025:         if (isset($replace['1']['0'])) {
2026:             $pregCount = count($replace['1']);
2027: 
2028:             for ($i = 0; $i < $pregCount; $i++) {
2029:                 if (!empty($replace['1'][$i]) && !is_numeric($replace['1'][$i])) {
2030:                     $conditions = preg_replace('/\b' . preg_quote($replace['1'][$i]) . '\b/', $this->name($replace['1'][$i]), $conditions);
2031:                 }
2032:             }
2033:             return $conditions;
2034:         }
2035:         return $original;
2036:     }
2037: /**
2038:  * Returns a limit statement in the correct format for the particular database.
2039:  *
2040:  * @param integer $limit Limit of results returned
2041:  * @param integer $offset Offset from which to start results
2042:  * @return string SQL limit/offset statement
2043:  */
2044:     function limit($limit, $offset = null) {
2045:         if ($limit) {
2046:             $rt = '';
2047:             if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) {
2048:                 $rt = ' LIMIT';
2049:             }
2050: 
2051:             if ($offset) {
2052:                 $rt .= ' ' . $offset . ',';
2053:             }
2054: 
2055:             $rt .= ' ' . $limit;
2056:             return $rt;
2057:         }
2058:         return null;
2059:     }
2060: /**
2061:  * Returns an ORDER BY clause as a string.
2062:  *
2063:  * @param string $key Field reference, as a key (i.e. Post.title)
2064:  * @param string $direction Direction (ASC or DESC)
2065:  * @return string ORDER BY clause
2066:  */
2067:     function order($keys, $direction = 'ASC') {
2068:         if (is_string($keys) && strpos($keys, ',') && !preg_match('/\(.+\,.+\)/', $keys)) {
2069:             $keys = array_map('trim', explode(',', $keys));
2070:         }
2071: 
2072:         if (is_array($keys)) {
2073:             $keys = array_filter($keys);
2074:         }
2075: 
2076:         if (empty($keys) || (is_array($keys) && isset($keys[0]) && empty($keys[0]))) {
2077:             return '';
2078:         }
2079: 
2080:         if (is_array($keys)) {
2081:             $keys = (Set::countDim($keys) > 1) ? array_map(array(&$this, 'order'), $keys) : $keys;
2082: 
2083:             foreach ($keys as $key => $value) {
2084:                 if (is_numeric($key)) {
2085:                     $key = $value = ltrim(str_replace('ORDER BY ', '', $this->order($value)));
2086:                     $value = (!preg_match('/\\x20ASC|\\x20DESC/i', $key) ? ' ' . $direction : '');
2087:                 } else {
2088:                     $value = ' ' . $value;
2089:                 }
2090: 
2091:                 if (!preg_match('/^.+\\(.*\\)/', $key) && !strpos($key, ',')) {
2092:                     if (preg_match('/\\x20ASC|\\x20DESC/i', $key, $dir)) {
2093:                         $dir = $dir[0];
2094:                         $key = preg_replace('/\\x20ASC|\\x20DESC/i', '', $key);
2095:                     } else {
2096:                         $dir = '';
2097:                     }
2098:                     $key = trim($key);
2099:                     if (!preg_match('/\s/', $key)) {
2100:                         $key = $this->name($key);
2101:                     }
2102:                     $key .= ' ' . trim($dir);
2103:                 }
2104:                 $order[] = $this->order($key . $value);
2105:             }
2106:             return ' ORDER BY ' . trim(str_replace('ORDER BY', '', implode(',', $order)));
2107:         }
2108:         $keys = preg_replace('/ORDER\\x20BY/i', '', $keys);
2109: 
2110:         if (strpos($keys, '.')) {
2111:             preg_match_all('/([a-zA-Z0-9_-]{1,})\\.([a-zA-Z0-9_-]{1,})/', $keys, $result, PREG_PATTERN_ORDER);
2112:             $pregCount = count($result[0]);
2113: 
2114:             for ($i = 0; $i < $pregCount; $i++) {
2115:                 if (!is_numeric($result[0][$i])) {
2116:                     $keys = preg_replace('/' . $result[0][$i] . '/', $this->name($result[0][$i]), $keys);
2117:                 }
2118:             }
2119:             $result = ' ORDER BY ' . $keys;
2120:             return $result . (!preg_match('/\\x20ASC|\\x20DESC/i', $keys) ? ' ' . $direction : '');
2121: 
2122:         } elseif (preg_match('/(\\x20ASC|\\x20DESC)/i', $keys, $match)) {
2123:             $direction = $match[1];
2124:             return ' ORDER BY ' . preg_replace('/' . $match[1] . '/', '', $keys) . $direction;
2125:         }
2126:         return ' ORDER BY ' . $keys . ' ' . $direction;
2127:     }
2128: /**
2129:  * Create a GROUP BY SQL clause
2130:  *
2131:  * @param string $group Group By Condition
2132:  * @return mixed string condition or null
2133:  */
2134:     function group($group) {
2135:         if ($group) {
2136:             if (is_array($group)) {
2137:                 $group = implode(', ', $group);
2138:             }
2139:             return ' GROUP BY ' . $this->__quoteFields($group);
2140:         }
2141:         return null;
2142:     }
2143: /**
2144:  * Disconnects database, kills the connection and says the connection is closed,
2145:  * and if DEBUG is turned on, the log for this object is shown.
2146:  *
2147:  */
2148:     function close() {
2149:         if (Configure::read() > 1) {
2150:             $this->showLog();
2151:         }
2152:         $this->disconnect();
2153:     }
2154: /**
2155:  * Checks if the specified table contains any record matching specified SQL
2156:  *
2157:  * @param Model $model Model to search
2158:  * @param string $sql SQL WHERE clause (condition only, not the "WHERE" part)
2159:  * @return boolean True if the table has a matching record, else false
2160:  */
2161:     function hasAny(&$Model, $sql) {
2162:         $sql = $this->conditions($sql);
2163:         $table = $this->fullTableName($Model);
2164:         $alias = $this->alias . $this->name($Model->alias);
2165:         $where = $sql ? "{$sql}" : ' WHERE 1 = 1';
2166:         $id = $Model->escapeField();
2167: 
2168:         $out = $this->fetchRow("SELECT COUNT({$id}) {$this->alias}count FROM {$table} {$alias}{$where}");
2169: 
2170:         if (is_array($out)) {
2171:             return $out[0]['count'];
2172:         }
2173:         return false;
2174:     }
2175: /**
2176:  * Gets the length of a database-native column description, or null if no length
2177:  *
2178:  * @param string $real Real database-layer column type (i.e. "varchar(255)")
2179:  * @return mixed An integer or string representing the length of the column
2180:  */
2181:     function length($real) {
2182:         if (!preg_match_all('/([\w\s]+)(?:\((\d+)(?:,(\d+))?\))?(\sunsigned)?(\szerofill)?/', $real, $result)) {
2183:             trigger_error(__('FIXME: Can\'t parse field: ' . $real, true), E_USER_WARNING);
2184:             $col = str_replace(array(')', 'unsigned'), '', $real);
2185:             $limit = null;
2186: 
2187:             if (strpos($col, '(') !== false) {
2188:                 list($col, $limit) = explode('(', $col);
2189:             }
2190:             if ($limit != null) {
2191:                 return intval($limit);
2192:             }
2193:             return null;
2194:         }
2195: 
2196:         $types = array(
2197:             'int' => 1, 'tinyint' => 1, 'smallint' => 1, 'mediumint' => 1, 'integer' => 1, 'bigint' => 1
2198:         );
2199: 
2200:         list($real, $type, $length, $offset, $sign, $zerofill) = $result;
2201:         $typeArr = $type;
2202:         $type = $type[0];
2203:         $length = $length[0];
2204:         $offset = $offset[0];
2205: 
2206:         $isFloat = in_array($type, array('dec', 'decimal', 'float', 'numeric', 'double'));
2207:         if ($isFloat && $offset) {
2208:             return $length.','.$offset;
2209:         }
2210: 
2211:         if (($real[0] == $type) && (count($real) == 1)) {
2212:             return null;
2213:         }
2214: 
2215:         if (isset($types[$type])) {
2216:             $length += $types[$type];
2217:             if (!empty($sign)) {
2218:                 $length--;
2219:             }
2220:         } elseif (in_array($type, array('enum', 'set'))) {
2221:             $length = 0;
2222:             foreach ($typeArr as $key => $enumValue) {
2223:                 if ($key == 0) {
2224:                     continue;
2225:                 }
2226:                 $tmpLength = strlen($enumValue);
2227:                 if ($tmpLength > $length) {
2228:                     $length = $tmpLength;
2229:                 }
2230:             }
2231:         }
2232:         return intval($length);
2233:     }
2234: /**
2235:  * Translates between PHP boolean values and Database (faked) boolean values
2236:  *
2237:  * @param mixed $data Value to be translated
2238:  * @return mixed Converted boolean value
2239:  */
2240:     function boolean($data) {
2241:         if ($data === true || $data === false) {
2242:             if ($data === true) {
2243:                 return 1;
2244:             }
2245:             return 0;
2246:         } else {
2247:             return !empty($data);
2248:         }
2249:     }
2250: /**
2251:  * Inserts multiple values into a table
2252:  *
2253:  * @param string $table
2254:  * @param string $fields
2255:  * @param array $values
2256:  * @access protected
2257:  */
2258:     function insertMulti($table, $fields, $values) {
2259:         $table = $this->fullTableName($table);
2260:         if (is_array($fields)) {
2261:             $fields = implode(', ', array_map(array(&$this, 'name'), $fields));
2262:         }
2263:         $count = count($values);
2264:         for ($x = 0; $x < $count; $x++) {
2265:             $this->query("INSERT INTO {$table} ({$fields}) VALUES {$values[$x]}");
2266:         }
2267:     }
2268: /**
2269:  * Returns an array of the indexes in given datasource name.
2270:  *
2271:  * @param string $model Name of model to inspect
2272:  * @return array Fields in table. Keys are column and unique
2273:  */
2274:     function index($model) {
2275:         return false;
2276:     }
2277: /**
2278:  * Generate a database-native schema for the given Schema object
2279:  *
2280:  * @param object $schema An instance of a subclass of CakeSchema
2281:  * @param string $tableName Optional.  If specified only the table name given will be generated.
2282:  *   Otherwise, all tables defined in the schema are generated.
2283:  * @return string
2284:  */
2285:     function createSchema($schema, $tableName = null) {
2286:         if (!is_a($schema, 'CakeSchema')) {
2287:             trigger_error(__('Invalid schema object', true), E_USER_WARNING);
2288:             return null;
2289:         }
2290:         $out = '';
2291: 
2292:         foreach ($schema->tables as $curTable => $columns) {
2293:             if (!$tableName || $tableName == $curTable) {
2294:                 $cols = $colList = $indexes = array();
2295:                 $primary = null;
2296:                 $table = $this->fullTableName($curTable);
2297: 
2298:                 foreach ($columns as $name => $col) {
2299:                     if (is_string($col)) {
2300:                         $col = array('type' => $col);
2301:                     }
2302:                     if (isset($col['key']) && $col['key'] == 'primary') {
2303:                         $primary = $name;
2304:                     }
2305:                     if ($name !== 'indexes') {
2306:                         $col['name'] = $name;
2307:                         if (!isset($col['type'])) {
2308:                             $col['type'] = 'string';
2309:                         }
2310:                         $cols[] = $this->buildColumn($col);
2311:                     } else {
2312:                         $indexes = array_merge($indexes, $this->buildIndex($col, $table));
2313:                     }
2314:                 }
2315:                 if (empty($indexes) && !empty($primary)) {
2316:                     $col = array('PRIMARY' => array('column' => $primary, 'unique' => 1));
2317:                     $indexes = array_merge($indexes, $this->buildIndex($col, $table));
2318:                 }
2319:                 $columns = $cols;
2320:                 $out .= $this->renderStatement('schema', compact('table', 'columns', 'indexes')) . "\n\n";
2321:             }
2322:         }
2323:         return $out;
2324:     }
2325: /**
2326:  * Generate a alter syntax from  CakeSchema::compare()
2327:  *
2328:  * @param unknown_type $schema
2329:  * @return unknown
2330:  */
2331:     function alterSchema($compare, $table = null) {
2332:         return false;
2333:     }
2334: /**
2335:  * Generate a "drop table" statement for the given Schema object
2336:  *
2337:  * @param object $schema An instance of a subclass of CakeSchema
2338:  * @param string $table Optional.  If specified only the table name given will be generated.
2339:  *   Otherwise, all tables defined in the schema are generated.
2340:  * @return string
2341:  */
2342:     function dropSchema($schema, $table = null) {
2343:         if (!is_a($schema, 'CakeSchema')) {
2344:             trigger_error(__('Invalid schema object', true), E_USER_WARNING);
2345:             return null;
2346:         }
2347:         $out = '';
2348: 
2349:         foreach ($schema->tables as $curTable => $columns) {
2350:             if (!$table || $table == $curTable) {
2351:                 $out .= 'DROP TABLE ' . $this->fullTableName($curTable) . ";\n";
2352:             }
2353:         }
2354:         return $out;
2355:     }
2356: /**
2357:  * Generate a database-native column schema string
2358:  *
2359:  * @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]),
2360:  *   where options can be 'default', 'length', or 'key'.
2361:  * @return string
2362:  */
2363:     function buildColumn($column) {
2364:         $name = $type = null;
2365:         extract(array_merge(array('null' => true), $column));
2366: 
2367:         if (empty($name) || empty($type)) {
2368:             trigger_error('Column name or type not defined in schema', E_USER_WARNING);
2369:             return null;
2370:         }
2371: 
2372:         if (!isset($this->columns[$type])) {
2373:             trigger_error("Column type {$type} does not exist", E_USER_WARNING);
2374:             return null;
2375:         }
2376: 
2377:         $real = $this->columns[$type];
2378:         $out = $this->name($name) . ' ' . $real['name'];
2379: 
2380:         if (isset($real['limit']) || isset($real['length']) || isset($column['limit']) || isset($column['length'])) {
2381:             if (isset($column['length'])) {
2382:                 $length = $column['length'];
2383:             } elseif (isset($column['limit'])) {
2384:                 $length = $column['limit'];
2385:             } elseif (isset($real['length'])) {
2386:                 $length = $real['length'];
2387:             } else {
2388:                 $length = $real['limit'];
2389:             }
2390:             $out .= '(' . $length . ')';
2391:         }
2392: 
2393:         if (($column['type'] == 'integer' || $column['type'] == 'float' ) && isset($column['default']) && $column['default'] === '') {
2394:             $column['default'] = null;
2395:         }
2396: 
2397:         if (isset($column['key']) && $column['key'] == 'primary' && $type == 'integer') {
2398:             $out .= ' ' . $this->columns['primary_key']['name'];
2399:         } elseif (isset($column['key']) && $column['key'] == 'primary') {
2400:             $out .= ' NOT NULL';
2401:         } elseif (isset($column['default']) && isset($column['null']) && $column['null'] == false) {
2402:             $out .= ' DEFAULT ' . $this->value($column['default'], $type) . ' NOT NULL';
2403:         } elseif (isset($column['default'])) {
2404:             $out .= ' DEFAULT ' . $this->value($column['default'], $type);
2405:         } elseif (isset($column['null']) && $column['null'] == true) {
2406:             $out .= ' DEFAULT NULL';
2407:         } elseif (isset($column['null']) && $column['null'] == false) {
2408:             $out .= ' NOT NULL';
2409:         }
2410:         return $out;
2411:     }
2412: /**
2413:  * Format indexes for create table
2414:  *
2415:  * @param array $indexes
2416:  * @param string $table
2417:  * @return array
2418:  */
2419:     function buildIndex($indexes, $table = null) {
2420:         $join = array();
2421:         foreach ($indexes as $name => $value) {
2422:             $out = '';
2423:             if ($name == 'PRIMARY') {
2424:                 $out .= 'PRIMARY ';
2425:                 $name = null;
2426:             } else {
2427:                 if (!empty($value['unique'])) {
2428:                     $out .= 'UNIQUE ';
2429:                 }
2430:                 $name = $this->startQuote . $name . $this->endQuote;
2431:             }
2432:             if (is_array($value['column'])) {
2433:                 $out .= 'KEY ' . $name . ' (' . implode(', ', array_map(array(&$this, 'name'), $value['column'])) . ')';
2434:             } else {
2435:                 $out .= 'KEY ' . $name . ' (' . $this->name($value['column']) . ')';
2436:             }
2437:             $join[] = $out;
2438:         }
2439:         return $join;
2440:     }
2441: /**
2442:  * Guesses the data type of an array
2443:  *
2444:  * @param string $value
2445:  * @return void
2446:  * @access public
2447:  */
2448:     function introspectType($value) {
2449:         if (!is_array($value)) {
2450:             if ($value === true || $value === false) {
2451:                 return 'boolean';
2452:             }
2453:             if (is_float($value) && floatval($value) === $value) {
2454:                 return 'float';
2455:             }
2456:             if (is_int($value) && intval($value) === $value) {
2457:                 return 'integer';
2458:             }
2459:             if (is_string($value) && strlen($value) > 255) {
2460:                 return 'text';
2461:             }
2462:             return 'string';
2463:         }
2464: 
2465:         $isAllFloat = $isAllInt = true;
2466:         $containsFloat = $containsInt = $containsString = false;
2467:         foreach ($value as $key => $valElement) {
2468:             $valElement = trim($valElement);
2469:             if (!is_float($valElement) && !preg_match('/^[\d]+\.[\d]+$/', $valElement)) {
2470:                 $isAllFloat = false;
2471:             } else {
2472:                 $containsFloat = true;
2473:                 continue;
2474:             }
2475:             if (!is_int($valElement) && !preg_match('/^[\d]+$/', $valElement)) {
2476:                 $isAllInt = false;
2477:             } else {
2478:                 $containsInt = true;
2479:                 continue;
2480:             }
2481:             $containsString = true;
2482:         }
2483: 
2484:         if ($isAllFloat) {
2485:             return 'float';
2486:         }
2487:         if ($isAllInt) {
2488:             return 'integer';
2489:         }
2490: 
2491:         if ($containsInt && !$containsString) {
2492:             return 'integer';
2493:         }
2494:         return 'string';
2495:     }
2496: }
2497: ?>
2498: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs