1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
26: 27: 28:
29: App::import('Core', array('ClassRegistry', 'Overloadable', 'Validation', 'Behavior', 'ConnectionManager', 'Set', 'String'));
30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41:
42: class Model extends Overloadable {
43: 44: 45: 46: 47: 48: 49:
50: var $useDbConfig = 'default';
51: 52: 53: 54: 55: 56: 57:
58: var $useTable = null;
59: 60: 61: 62: 63: 64: 65:
66: var $displayField = null;
67: 68: 69: 70: 71: 72: 73:
74: var $id = false;
75: 76: 77: 78: 79: 80: 81:
82: var $data = array();
83: 84: 85: 86: 87: 88:
89: var $table = false;
90: 91: 92: 93: 94: 95: 96:
97: var $primaryKey = null;
98: 99: 100: 101: 102: 103: 104:
105: var $_schema = null;
106: 107: 108: 109: 110: 111: 112: 113: 114:
115: var $validate = array();
116: 117: 118: 119: 120: 121: 122:
123: var $validationErrors = array();
124: 125: 126: 127: 128: 129: 130:
131: var $tablePrefix = null;
132: 133: 134: 135: 136: 137: 138:
139: var $name = null;
140: 141: 142: 143: 144: 145:
146: var $alias = null;
147: 148: 149: 150: 151: 152:
153: var $tableToModel = array();
154: 155: 156: 157: 158: 159:
160: var $logTransactions = false;
161: 162: 163: 164: 165: 166:
167: var $transactional = false;
168: 169: 170: 171: 172: 173: 174: 175:
176: var $cacheQueries = false;
177: 178: 179: 180: 181: 182: 183:
184: var $belongsTo = array();
185: 186: 187: 188: 189: 190: 191:
192: var $hasOne = array();
193: 194: 195: 196: 197: 198: 199:
200: var $hasMany = array();
201: 202: 203: 204: 205: 206: 207:
208: var $hasAndBelongsToMany = array();
209: 210: 211: 212: 213: 214: 215: 216: 217: 218:
219: var $actsAs = null;
220: 221: 222: 223: 224: 225:
226: var $Behaviors = null;
227: 228: 229: 230: 231: 232:
233: var $whitelist = array();
234: 235: 236: 237: 238: 239:
240: var $cacheSources = true;
241: 242: 243: 244: 245: 246:
247: var $findQueryType = null;
248: 249: 250: 251: 252: 253: 254: 255:
256: var $recursive = 1;
257: 258: 259: 260: 261: 262: 263: 264: 265: 266:
267: var $order = null;
268: 269: 270: 271: 272: 273:
274: var $__exists = null;
275: 276: 277: 278: 279: 280:
281: var $__associationKeys = array(
282: 'belongsTo' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'counterCache'),
283: 'hasOne' => array('className', 'foreignKey','conditions', 'fields','order', 'dependent'),
284: 'hasMany' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'dependent', 'exclusive', 'finderQuery', 'counterQuery'),
285: 'hasAndBelongsToMany' => array('className', 'joinTable', 'with', 'foreignKey', 'associationForeignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'unique', 'finderQuery', 'deleteQuery', 'insertQuery')
286: );
287: 288: 289: 290: 291: 292:
293: var $__associations = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
294: 295: 296: 297: 298: 299:
300: var $__backAssociation = array();
301: 302: 303: 304: 305: 306:
307: var $__insertID = null;
308: 309: 310: 311: 312: 313:
314: var $__numRows = null;
315: 316: 317: 318: 319: 320:
321: var $__affectedRows = null;
322: 323: 324: 325: 326: 327:
328: var $_findMethods = array(
329: 'all' => true, 'first' => true, 'count' => true,
330: 'neighbors' => true, 'list' => true, 'threaded' => true
331: );
332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363:
364: function __construct($id = false, $table = null, $ds = null) {
365: parent::__construct();
366:
367: if (is_array($id)) {
368: extract(array_merge(
369: array(
370: 'id' => $this->id, 'table' => $this->useTable, 'ds' => $this->useDbConfig,
371: 'name' => $this->name, 'alias' => $this->alias
372: ),
373: $id
374: ));
375: }
376:
377: if ($this->name === null) {
378: $this->name = (isset($name) ? $name : get_class($this));
379: }
380:
381: if ($this->alias === null) {
382: $this->alias = (isset($alias) ? $alias : $this->name);
383: }
384:
385: if ($this->primaryKey === null) {
386: $this->primaryKey = 'id';
387: }
388:
389: ClassRegistry::addObject($this->alias, $this);
390:
391: $this->id = $id;
392: unset($id);
393:
394: if ($table === false) {
395: $this->useTable = false;
396: } elseif ($table) {
397: $this->useTable = $table;
398: }
399:
400: if ($ds !== null) {
401: $this->useDbConfig = $ds;
402: }
403:
404: if (is_subclass_of($this, 'AppModel')) {
405: $appVars = get_class_vars('AppModel');
406: $merge = array('_findMethods');
407:
408: if ($this->actsAs !== null || $this->actsAs !== false) {
409: $merge[] = 'actsAs';
410: }
411: $parentClass = get_parent_class($this);
412: if (strtolower($parentClass) !== 'appmodel') {
413: $parentVars = get_class_vars($parentClass);
414: foreach ($merge as $var) {
415: if (isset($parentVars[$var]) && !empty($parentVars[$var])) {
416: $appVars[$var] = Set::merge($appVars[$var], $parentVars[$var]);
417: }
418: }
419: }
420:
421: foreach ($merge as $var) {
422: if (isset($appVars[$var]) && !empty($appVars[$var]) && is_array($this->{$var})) {
423: $this->{$var} = Set::merge($appVars[$var], $this->{$var});
424: }
425: }
426: }
427: $this->Behaviors = new BehaviorCollection();
428:
429: if ($this->useTable !== false) {
430: $this->setDataSource($ds);
431:
432: if ($this->useTable === null) {
433: $this->useTable = Inflector::tableize($this->name);
434: }
435: if (method_exists($this, 'setTablePrefix')) {
436: $this->setTablePrefix();
437: }
438: $this->setSource($this->useTable);
439:
440: if ($this->displayField == null) {
441: $this->displayField = $this->hasField(array('title', 'name', $this->primaryKey));
442: }
443: } elseif ($this->table === false) {
444: $this->table = Inflector::tableize($this->name);
445: }
446: $this->__createLinks();
447: $this->Behaviors->init($this->alias, $this->actsAs);
448: }
449: 450: 451: 452: 453: 454: 455: 456: 457:
458: function call__($method, $params) {
459: $result = $this->Behaviors->dispatchMethod($this, $method, $params);
460:
461: if ($result !== array('unhandled')) {
462: return $result;
463: }
464: $db =& ConnectionManager::getDataSource($this->useDbConfig);
465: $return = $db->query($method, $params, $this);
466:
467: if (!PHP5) {
468: $this->resetAssociations();
469: }
470: return $return;
471: }
472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485:
486: function bind($model, $options = array(), $permanent = true) {
487: if (!is_array($model)) {
488: $model = array($model => $options);
489: }
490:
491: foreach ($model as $name => $options) {
492: if (isset($options['type'])) {
493: $assoc = $options['type'];
494: } elseif (isset($options[0])) {
495: $assoc = $options[0];
496: } else {
497: $assoc = 'belongsTo';
498: }
499:
500: if (!$permanent) {
501: $this->__backAssociation[$assoc] = $this->{$assoc};
502: }
503: foreach ($model as $key => $value) {
504: $assocName = $modelName = $key;
505:
506: if (isset($this->{$assoc}[$assocName])) {
507: $this->{$assoc}[$assocName] = array_merge($this->{$assoc}[$assocName], $options);
508: } else {
509: if (isset($value['className'])) {
510: $modelName = $value['className'];
511: }
512:
513: $this->__constructLinkedModel($assocName, $modelName);
514: $this->{$assoc}[$assocName] = $model[$assocName];
515: $this->__generateAssociation($assoc);
516: }
517: unset($this->{$assoc}[$assocName]['type'], $this->{$assoc}[$assocName][0]);
518: }
519: }
520: }
521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538:
539: function bindModel($params, $reset = true) {
540: foreach ($params as $assoc => $model) {
541: if ($reset === true && !isset($this->__backAssociation[$assoc])) {
542: $this->__backAssociation[$assoc] = $this->{$assoc};
543: }
544: foreach ($model as $key => $value) {
545: $assocName = $key;
546:
547: if (is_numeric($key)) {
548: $assocName = $value;
549: $value = array();
550: }
551: $modelName = $assocName;
552: $this->{$assoc}[$assocName] = $value;
553:
554: if ($reset === false && isset($this->__backAssociation[$assoc])) {
555: $this->__backAssociation[$assoc][$assocName] = $value;
556: }
557: }
558: }
559: $this->__createLinks();
560: return true;
561: }
562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575: 576: 577: 578: 579:
580: function unbindModel($params, $reset = true) {
581: foreach ($params as $assoc => $models) {
582: if ($reset === true && !isset($this->__backAssociation[$assoc])) {
583: $this->__backAssociation[$assoc] = $this->{$assoc};
584: }
585: foreach ($models as $model) {
586: if ($reset === false && isset($this->__backAssociation[$assoc][$model])) {
587: unset($this->__backAssociation[$assoc][$model]);
588: }
589: unset($this->{$assoc}[$model]);
590: }
591: }
592: return true;
593: }
594: 595: 596: 597: 598: 599:
600: function __createLinks() {
601: foreach ($this->__associations as $type) {
602: if (!is_array($this->{$type})) {
603: $this->{$type} = explode(',', $this->{$type});
604:
605: foreach ($this->{$type} as $i => $className) {
606: $className = trim($className);
607: unset ($this->{$type}[$i]);
608: $this->{$type}[$className] = array();
609: }
610: }
611:
612: if (!empty($this->{$type})) {
613: foreach ($this->{$type} as $assoc => $value) {
614: $plugin = null;
615:
616: if (is_numeric($assoc)) {
617: unset ($this->{$type}[$assoc]);
618: $assoc = $value;
619: $value = array();
620: $this->{$type}[$assoc] = $value;
621:
622: if (strpos($assoc, '.') !== false) {
623: $value = $this->{$type}[$assoc];
624: unset($this->{$type}[$assoc]);
625: list($plugin, $assoc) = explode('.', $assoc);
626: $this->{$type}[$assoc] = $value;
627: $plugin = $plugin . '.';
628: }
629: }
630: $className = $assoc;
631:
632: if (isset($value['className']) && !empty($value['className'])) {
633: $className = $value['className'];
634: if (strpos($className, '.') !== false) {
635: list($plugin, $className) = explode('.', $className);
636: $plugin = $plugin . '.';
637: $this->{$type}[$assoc]['className'] = $className;
638: }
639: }
640: $this->__constructLinkedModel($assoc, $plugin . $className);
641: }
642: $this->__generateAssociation($type);
643: }
644: }
645: }
646: 647: 648: 649: 650: 651: 652: 653: 654: 655: 656: 657: 658: 659:
660: function __constructLinkedModel($assoc, $className = null) {
661: if (empty($className)) {
662: $className = $assoc;
663: }
664:
665: if (!isset($this->{$assoc}) || $this->{$assoc}->name !== $className) {
666: $model = array('class' => $className, 'alias' => $assoc);
667: if (PHP5) {
668: $this->{$assoc} = ClassRegistry::init($model);
669: } else {
670: $this->{$assoc} =& ClassRegistry::init($model);
671: }
672: if (strpos($className, '.') !== false) {
673: ClassRegistry::addObject($className, $this->{$assoc});
674: }
675: if ($assoc) {
676: $this->tableToModel[$this->{$assoc}->table] = $assoc;
677: }
678: }
679: }
680: 681: 682: 683: 684: 685: 686:
687: function __generateAssociation($type) {
688: foreach ($this->{$type} as $assocKey => $assocData) {
689: $class = $assocKey;
690: $dynamicWith = false;
691:
692: foreach ($this->__associationKeys[$type] as $key) {
693:
694: if (!isset($this->{$type}[$assocKey][$key]) || $this->{$type}[$assocKey][$key] === null) {
695: $data = '';
696:
697: switch ($key) {
698: case 'fields':
699: $data = '';
700: break;
701:
702: case 'foreignKey':
703: $data = (($type == 'belongsTo') ? Inflector::underscore($assocKey) : Inflector::singularize($this->table)) . '_id';
704: break;
705:
706: case 'associationForeignKey':
707: $data = Inflector::singularize($this->{$class}->table) . '_id';
708: break;
709:
710: case 'with':
711: $data = Inflector::camelize(Inflector::singularize($this->{$type}[$assocKey]['joinTable']));
712: $dynamicWith = true;
713: break;
714:
715: case 'joinTable':
716: $tables = array($this->table, $this->{$class}->table);
717: sort ($tables);
718: $data = $tables[0] . '_' . $tables[1];
719: break;
720:
721: case 'className':
722: $data = $class;
723: break;
724:
725: case 'unique':
726: $data = true;
727: break;
728: }
729: $this->{$type}[$assocKey][$key] = $data;
730: }
731: }
732:
733: if (!empty($this->{$type}[$assocKey]['with'])) {
734: $joinClass = $this->{$type}[$assocKey]['with'];
735: if (is_array($joinClass)) {
736: $joinClass = key($joinClass);
737: }
738: $plugin = null;
739:
740: if (strpos($joinClass, '.') !== false) {
741: list($plugin, $joinClass) = explode('.', $joinClass);
742: $plugin = $plugin . '.';
743: $this->{$type}[$assocKey]['with'] = $joinClass;
744: }
745:
746: if (!ClassRegistry::isKeySet($joinClass) && $dynamicWith === true) {
747: $this->{$joinClass} = new AppModel(array(
748: 'name' => $joinClass,
749: 'table' => $this->{$type}[$assocKey]['joinTable'],
750: 'ds' => $this->useDbConfig
751: ));
752: } else {
753: $this->__constructLinkedModel($joinClass, $plugin . $joinClass);
754: $this->{$type}[$assocKey]['joinTable'] = $this->{$joinClass}->table;
755: }
756:
757: if (count($this->{$joinClass}->schema()) <= 2 && $this->{$joinClass}->primaryKey !== false) {
758: $this->{$joinClass}->primaryKey = $this->{$type}[$assocKey]['foreignKey'];
759: }
760: }
761: }
762: }
763: 764: 765: 766: 767: 768: 769:
770: function setSource($tableName) {
771: $this->setDataSource($this->useDbConfig);
772: $db =& ConnectionManager::getDataSource($this->useDbConfig);
773: $db->cacheSources = ($this->cacheSources && $db->cacheSources);
774:
775: if ($db->isInterfaceSupported('listSources')) {
776: $sources = $db->listSources();
777: if (is_array($sources) && !in_array(strtolower($this->tablePrefix . $tableName), array_map('strtolower', $sources))) {
778: return $this->cakeError('missingTable', array(array(
779: 'className' => $this->alias,
780: 'table' => $this->tablePrefix . $tableName
781: )));
782: }
783: $this->_schema = null;
784: }
785: $this->table = $this->useTable = $tableName;
786: $this->tableToModel[$this->table] = $this->alias;
787: $this->schema();
788: }
789: 790: 791: 792: 793: 794: 795: 796: 797: 798: 799: 800: 801: 802: 803:
804: function set($one, $two = null) {
805: if (!$one) {
806: return;
807: }
808: if (is_object($one)) {
809: $one = Set::reverse($one);
810: }
811:
812: if (is_array($one)) {
813: $data = $one;
814: if (empty($one[$this->alias])) {
815: if ($this->getAssociated(key($one)) === null) {
816: $data = array($this->alias => $one);
817: }
818: }
819: } else {
820: $data = array($this->alias => array($one => $two));
821: }
822:
823: foreach ($data as $modelName => $fieldSet) {
824: if (is_array($fieldSet)) {
825:
826: foreach ($fieldSet as $fieldName => $fieldValue) {
827: if (isset($this->validationErrors[$fieldName])) {
828: unset ($this->validationErrors[$fieldName]);
829: }
830:
831: if ($modelName === $this->alias) {
832: if ($fieldName === $this->primaryKey) {
833: $this->id = $fieldValue;
834: }
835: }
836: if (is_array($fieldValue) || is_object($fieldValue)) {
837: $fieldValue = $this->deconstruct($fieldName, $fieldValue);
838: }
839: $this->data[$modelName][$fieldName] = $fieldValue;
840: }
841: }
842: }
843: return $data;
844: }
845: 846: 847: 848: 849: 850: 851: 852:
853: function deconstruct($field, $data) {
854: if (!is_array($data)) {
855: return $data;
856: }
857:
858: $copy = $data;
859: $type = $this->getColumnType($field);
860:
861: if (in_array($type, array('datetime', 'timestamp', 'date', 'time'))) {
862: $useNewDate = (isset($data['year']) || isset($data['month']) ||
863: isset($data['day']) || isset($data['hour']) || isset($data['minute']));
864:
865: $dateFields = array('Y' => 'year', 'm' => 'month', 'd' => 'day', 'H' => 'hour', 'i' => 'min', 's' => 'sec');
866: $timeFields = array('H' => 'hour', 'i' => 'min', 's' => 'sec');
867:
868: $db =& ConnectionManager::getDataSource($this->useDbConfig);
869: $format = $db->columns[$type]['format'];
870: $date = array();
871:
872: if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] != 12 && 'pm' == $data['meridian']) {
873: $data['hour'] = $data['hour'] + 12;
874: }
875: if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] == 12 && 'am' == $data['meridian']) {
876: $data['hour'] = '00';
877: }
878: if ($type == 'time') {
879: foreach ($timeFields as $key => $val) {
880: if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') {
881: $data[$val] = '00';
882: } elseif ($data[$val] === '') {
883: $data[$val] = '';
884: } else {
885: $data[$val] = sprintf('%02d', $data[$val]);
886: }
887: if (!empty($data[$val])) {
888: $date[$key] = $data[$val];
889: } else {
890: return null;
891: }
892: }
893: }
894:
895: if ($type == 'datetime' || $type == 'timestamp' || $type == 'date') {
896: foreach ($dateFields as $key => $val) {
897: if ($val == 'hour' || $val == 'min' || $val == 'sec') {
898: if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') {
899: $data[$val] = '00';
900: } else {
901: $data[$val] = sprintf('%02d', $data[$val]);
902: }
903: }
904: if (!isset($data[$val]) || isset($data[$val]) && (empty($data[$val]) || $data[$val][0] === '-')) {
905: return null;
906: }
907: if (isset($data[$val]) && !empty($data[$val])) {
908: $date[$key] = $data[$val];
909: }
910: }
911: }
912: $date = str_replace(array_keys($date), array_values($date), $format);
913: if ($useNewDate && !empty($date)) {
914: return $date;
915: }
916: }
917: return $data;
918: }
919: 920: 921: 922: 923: 924: 925: 926:
927: function schema($field = false) {
928: if (!is_array($this->_schema) || $field === true) {
929: $db =& ConnectionManager::getDataSource($this->useDbConfig);
930: $db->cacheSources = ($this->cacheSources && $db->cacheSources);
931: if ($db->isInterfaceSupported('describe') && $this->useTable !== false) {
932: $this->_schema = $db->describe($this, $field);
933: } elseif ($this->useTable === false) {
934: $this->_schema = array();
935: }
936: }
937: if (is_string($field)) {
938: if (isset($this->_schema[$field])) {
939: return $this->_schema[$field];
940: } else {
941: return null;
942: }
943: }
944: return $this->_schema;
945: }
946: 947: 948: 949: 950: 951:
952: function getColumnTypes() {
953: $columns = $this->schema();
954: if (empty($columns)) {
955: trigger_error(__('(Model::getColumnTypes) Unable to build model field data. If you are using a model without a database table, try implementing schema()', true), E_USER_WARNING);
956: }
957: $cols = array();
958: foreach ($columns as $field => $values) {
959: $cols[$field] = $values['type'];
960: }
961: return $cols;
962: }
963: 964: 965: 966: 967: 968: 969:
970: function getColumnType($column) {
971: $db =& ConnectionManager::getDataSource($this->useDbConfig);
972: $cols = $this->schema();
973: $model = null;
974:
975: $column = str_replace(array($db->startQuote, $db->endQuote), '', $column);
976:
977: if (strpos($column, '.')) {
978: list($model, $column) = explode('.', $column);
979: }
980: if ($model != $this->alias && isset($this->{$model})) {
981: return $this->{$model}->getColumnType($column);
982: }
983: if (isset($cols[$column]) && isset($cols[$column]['type'])) {
984: return $cols[$column]['type'];
985: }
986: return null;
987: }
988: 989: 990: 991: 992: 993: 994: 995: 996:
997: function hasField($name) {
998: if (is_array($name)) {
999: foreach ($name as $n) {
1000: if ($this->hasField($n)) {
1001: return $n;
1002: }
1003: }
1004: return false;
1005: }
1006:
1007: if (empty($this->_schema)) {
1008: $this->schema();
1009: }
1010:
1011: if ($this->_schema != null) {
1012: return isset($this->_schema[$name]);
1013: }
1014: return false;
1015: }
1016: 1017: 1018: 1019: 1020: 1021: 1022: 1023: 1024: 1025: 1026: 1027:
1028: function create($data = array(), $filterKey = false) {
1029: $defaults = array();
1030: $this->id = false;
1031: $this->data = array();
1032: $this->__exists = null;
1033: $this->validationErrors = array();
1034:
1035: if ($data !== null && $data !== false) {
1036: foreach ($this->schema() as $field => $properties) {
1037: if ($this->primaryKey !== $field && isset($properties['default'])) {
1038: $defaults[$field] = $properties['default'];
1039: }
1040: }
1041: $this->set(Set::filter($defaults));
1042: $this->set($data);
1043: }
1044: if ($filterKey) {
1045: $this->set($this->primaryKey, false);
1046: }
1047: return $this->data;
1048: }
1049: 1050: 1051: 1052: 1053: 1054: 1055: 1056: 1057:
1058: function read($fields = null, $id = null) {
1059: $this->validationErrors = array();
1060:
1061: if ($id != null) {
1062: $this->id = $id;
1063: }
1064:
1065: $id = $this->id;
1066:
1067: if (is_array($this->id)) {
1068: $id = $this->id[0];
1069: }
1070:
1071: if ($id !== null && $id !== false) {
1072: $this->data = $this->find('first', array(
1073: 'conditions' => array($this->alias . '.' . $this->primaryKey => $id),
1074: 'fields' => $fields
1075: ));
1076: return $this->data;
1077: } else {
1078: return false;
1079: }
1080: }
1081: 1082: 1083: 1084: 1085: 1086: 1087: 1088: 1089: 1090: 1091:
1092: function field($name, $conditions = null, $order = null) {
1093: if ($conditions === null && $this->id !== false) {
1094: $conditions = array($this->alias . '.' . $this->primaryKey => $this->id);
1095: }
1096: if ($this->recursive >= 1) {
1097: $recursive = -1;
1098: } else {
1099: $recursive = $this->recursive;
1100: }
1101: if ($data = $this->find($conditions, $name, $order, $recursive)) {
1102: if (strpos($name, '.') === false) {
1103: if (isset($data[$this->alias][$name])) {
1104: return $data[$this->alias][$name];
1105: }
1106: } else {
1107: $name = explode('.', $name);
1108: if (isset($data[$name[0]][$name[1]])) {
1109: return $data[$name[0]][$name[1]];
1110: }
1111: }
1112: if (!empty($data[0])) {
1113: $name = key($data[0]);
1114: return $data[0][$name];
1115: }
1116: } else {
1117: return false;
1118: }
1119: }
1120: 1121: 1122: 1123: 1124: 1125: 1126: 1127: 1128: 1129: 1130: 1131:
1132: function saveField($name, $value, $validate = false) {
1133: $id = $this->id;
1134: $this->create(false);
1135:
1136: if (is_array($validate)) {
1137: $options = array_merge(array('validate' => false, 'fieldList' => array($name)), $validate);
1138: } else {
1139: $options = array('validate' => $validate, 'fieldList' => array($name));
1140: }
1141: return $this->save(array($this->alias => array($this->primaryKey => $id, $name => $value)), $options);
1142: }
1143: 1144: 1145: 1146: 1147: 1148: 1149: 1150: 1151: 1152: 1153: 1154: 1155:
1156: function save($data = null, $validate = true, $fieldList = array()) {
1157: $defaults = array('validate' => true, 'fieldList' => array(), 'callbacks' => true);
1158: $_whitelist = $this->whitelist;
1159: $fields = array();
1160:
1161: if (!is_array($validate)) {
1162: $options = array_merge($defaults, compact('validate', 'fieldList', 'callbacks'));
1163: } else {
1164: $options = array_merge($defaults, $validate);
1165: }
1166:
1167: if (!empty($options['fieldList'])) {
1168: $this->whitelist = $options['fieldList'];
1169: } elseif ($options['fieldList'] === null) {
1170: $this->whitelist = array();
1171: }
1172: $this->set($data);
1173:
1174: if (empty($this->data) && !$this->hasField(array('created', 'updated', 'modified'))) {
1175: return false;
1176: }
1177:
1178: foreach (array('created', 'updated', 'modified') as $field) {
1179: $keyPresentAndEmpty = (
1180: isset($this->data[$this->alias]) &&
1181: array_key_exists($field, $this->data[$this->alias]) &&
1182: $this->data[$this->alias][$field] === null
1183: );
1184: if ($keyPresentAndEmpty) {
1185: unset($this->data[$this->alias][$field]);
1186: }
1187: }
1188:
1189: $this->exists();
1190: $dateFields = array('modified', 'updated');
1191:
1192: if (!$this->__exists) {
1193: $dateFields[] = 'created';
1194: }
1195: if (isset($this->data[$this->alias])) {
1196: $fields = array_keys($this->data[$this->alias]);
1197: }
1198: if ($options['validate'] && !$this->validates($options)) {
1199: $this->whitelist = $_whitelist;
1200: return false;
1201: }
1202:
1203: $db =& ConnectionManager::getDataSource($this->useDbConfig);
1204:
1205: foreach ($dateFields as $updateCol) {
1206: if ($this->hasField($updateCol) && !in_array($updateCol, $fields)) {
1207: $default = array('formatter' => 'date');
1208: $colType = array_merge($default, $db->columns[$this->getColumnType($updateCol)]);
1209: if (!array_key_exists('format', $colType)) {
1210: $time = strtotime('now');
1211: } else {
1212: $time = $colType['formatter']($colType['format']);
1213: }
1214: if (!empty($this->whitelist)) {
1215: $this->whitelist[] = $updateCol;
1216: }
1217: $this->set($updateCol, $time);
1218: }
1219: }
1220:
1221: if ($options['callbacks'] === true || $options['callbacks'] === 'before') {
1222: $result = $this->Behaviors->trigger($this, 'beforeSave', array($options), array(
1223: 'break' => true, 'breakOn' => false
1224: ));
1225: if (!$result || !$this->beforeSave($options)) {
1226: $this->whitelist = $_whitelist;
1227: return false;
1228: }
1229: }
1230: $fields = $values = array();
1231:
1232: if (isset($this->data[$this->alias][$this->primaryKey]) && empty($this->data[$this->alias][$this->primaryKey])) {
1233: unset($this->data[$this->alias][$this->primaryKey]);
1234: }
1235:
1236: foreach ($this->data as $n => $v) {
1237: if (isset($this->hasAndBelongsToMany[$n])) {
1238: if (isset($v[$n])) {
1239: $v = $v[$n];
1240: }
1241: $joined[$n] = $v;
1242: } else {
1243: if ($n === $this->alias) {
1244: foreach (array('created', 'updated', 'modified') as $field) {
1245: if (array_key_exists($field, $v) && empty($v[$field])) {
1246: unset($v[$field]);
1247: }
1248: }
1249:
1250: foreach ($v as $x => $y) {
1251: if ($this->hasField($x) && (empty($this->whitelist) || in_array($x, $this->whitelist))) {
1252: list($fields[], $values[]) = array($x, $y);
1253: }
1254: }
1255: }
1256: }
1257: }
1258: $count = count($fields);
1259:
1260: if (!$this->__exists && $count > 0) {
1261: $this->id = false;
1262: }
1263: $success = true;
1264: $created = false;
1265:
1266: if ($count > 0) {
1267: $cache = $this->_prepareUpdateFields(array_combine($fields, $values));
1268:
1269: if (!empty($this->id)) {
1270: $success = (bool)$db->update($this, $fields, $values);
1271: } else {
1272: foreach ($this->_schema as $field => $properties) {
1273: if ($this->primaryKey === $field) {
1274: $fInfo = $this->_schema[$field];
1275: $isUUID = ($fInfo['length'] == 36 &&
1276: ($fInfo['type'] === 'string' || $fInfo['type'] === 'binary')
1277: );
1278: if (empty($this->data[$this->alias][$this->primaryKey]) && $isUUID) {
1279: if (array_key_exists($this->primaryKey, $this->data[$this->alias])) {
1280: $j = array_search($this->primaryKey, $fields);
1281: $values[$j] = String::uuid();
1282: } else {
1283: list($fields[], $values[]) = array($this->primaryKey, String::uuid());
1284: }
1285: }
1286: break;
1287: }
1288: }
1289:
1290: if (!$db->create($this, $fields, $values)) {
1291: $success = $created = false;
1292: } else {
1293: $created = true;
1294: }
1295: }
1296:
1297: if ($success && !empty($this->belongsTo)) {
1298: $this->updateCounterCache($cache, $created);
1299: }
1300: }
1301:
1302: if (!empty($joined) && $success === true) {
1303: $this->__saveMulti($joined, $this->id);
1304: }
1305:
1306: if ($success && $count > 0) {
1307: if (!empty($this->data)) {
1308: $success = $this->data;
1309: }
1310: if ($options['callbacks'] === true || $options['callbacks'] === 'after') {
1311: $this->Behaviors->trigger($this, 'afterSave', array($created, $options));
1312: $this->afterSave($created);
1313: }
1314: if (!empty($this->data)) {
1315: $success = Set::merge($success, $this->data);
1316: }
1317: $this->data = false;
1318: $this->__exists = null;
1319: $this->_clearCache();
1320: $this->validationErrors = array();
1321: }
1322: $this->whitelist = $_whitelist;
1323: return $success;
1324: }
1325: 1326: 1327: 1328: 1329: 1330: 1331:
1332: function __saveMulti($joined, $id) {
1333: $db =& ConnectionManager::getDataSource($this->useDbConfig);
1334:
1335: foreach ($joined as $assoc => $data) {
1336:
1337: if (isset($this->hasAndBelongsToMany[$assoc])) {
1338: list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']);
1339:
1340: $isUUID = !empty($this->{$join}->primaryKey) && (
1341: $this->{$join}->_schema[$this->{$join}->primaryKey]['length'] == 36 && (
1342: $this->{$join}->_schema[$this->{$join}->primaryKey]['type'] === 'string' ||
1343: $this->{$join}->_schema[$this->{$join}->primaryKey]['type'] === 'binary'
1344: )
1345: );
1346:
1347: $newData = $newValues = array();
1348: $primaryAdded = false;
1349:
1350: $fields = array(
1351: $db->name($this->hasAndBelongsToMany[$assoc]['foreignKey']),
1352: $db->name($this->hasAndBelongsToMany[$assoc]['associationForeignKey'])
1353: );
1354:
1355: $idField = $db->name($this->{$join}->primaryKey);
1356: if ($isUUID && !in_array($idField, $fields)) {
1357: $fields[] = $idField;
1358: $primaryAdded = true;
1359: }
1360:
1361: foreach ((array)$data as $row) {
1362: if ((is_string($row) && (strlen($row) == 36 || strlen($row) == 16)) || is_numeric($row)) {
1363: $values = array(
1364: $db->value($id, $this->getColumnType($this->primaryKey)),
1365: $db->value($row)
1366: );
1367: if ($isUUID && $primaryAdded) {
1368: $values[] = $db->value(String::uuid());
1369: }
1370: $values = implode(',', $values);
1371: $newValues[] = "({$values})";
1372: unset($values);
1373: } elseif (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
1374: $newData[] = $row;
1375: } elseif (isset($row[$join]) && isset($row[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
1376: $newData[] = $row[$join];
1377: }
1378: }
1379:
1380: if ($this->hasAndBelongsToMany[$assoc]['unique']) {
1381: $conditions = array(
1382: $join . '.' . $this->hasAndBelongsToMany[$assoc]['foreignKey'] => $id
1383: );
1384: if (!empty($this->hasAndBelongsToMany[$assoc]['conditions'])) {
1385: $conditions = array_merge($conditions, (array)$this->hasAndBelongsToMany[$assoc]['conditions']);
1386: }
1387: $links = $this->{$join}->find('all', array(
1388: 'conditions' => $conditions,
1389: 'recursive' => empty($this->hasAndBelongsToMany[$assoc]['conditions']) ? -1 : 0,
1390: 'fields' => $this->hasAndBelongsToMany[$assoc]['associationForeignKey']
1391: ));
1392:
1393: $associationForeignKey = "{$join}." . $this->hasAndBelongsToMany[$assoc]['associationForeignKey'];
1394: $oldLinks = Set::extract($links, "{n}.{$associationForeignKey}");
1395: if (!empty($oldLinks)) {
1396: $conditions[$associationForeignKey] = $oldLinks;
1397: $db->delete($this->{$join}, $conditions);
1398: }
1399: }
1400:
1401: if (!empty($newData)) {
1402: foreach ($newData as $data) {
1403: $data[$this->hasAndBelongsToMany[$assoc]['foreignKey']] = $id;
1404: $this->{$join}->create($data);
1405: $this->{$join}->save();
1406: }
1407: }
1408:
1409: if (!empty($newValues)) {
1410: $fields = implode(',', $fields);
1411: $db->insertMulti($this->{$join}, $fields, $newValues);
1412: }
1413: }
1414: }
1415: }
1416: 1417: 1418: 1419: 1420: 1421: 1422: 1423: 1424:
1425: function updateCounterCache($keys = array(), $created = false) {
1426: $keys = empty($keys) ? $this->data[$this->alias] : $keys;
1427: $keys['old'] = isset($keys['old']) ? $keys['old'] : array();
1428:
1429: foreach ($this->belongsTo as $parent => $assoc) {
1430: $foreignKey = $assoc['foreignKey'];
1431: $fkQuoted = $this->escapeField($assoc['foreignKey']);
1432:
1433: if (!empty($assoc['counterCache'])) {
1434: if ($assoc['counterCache'] === true) {
1435: $assoc['counterCache'] = Inflector::underscore($this->alias) . '_count';
1436: }
1437: if (!$this->{$parent}->hasField($assoc['counterCache'])) {
1438: continue;
1439: }
1440:
1441: if (!array_key_exists($foreignKey, $keys)) {
1442: $keys[$foreignKey] = $this->field($foreignKey);
1443: }
1444: $recursive = (isset($assoc['counterScope']) ? 1 : -1);
1445: $conditions = ($recursive == 1) ? (array)$assoc['counterScope'] : array();
1446:
1447: if (isset($keys['old'][$foreignKey])) {
1448: if ($keys['old'][$foreignKey] != $keys[$foreignKey]) {
1449: $conditions[$fkQuoted] = $keys['old'][$foreignKey];
1450: $count = intval($this->find('count', compact('conditions', 'recursive')));
1451:
1452: $this->{$parent}->updateAll(
1453: array($assoc['counterCache'] => $count),
1454: array($this->{$parent}->escapeField() => $keys['old'][$foreignKey])
1455: );
1456: }
1457: }
1458: $conditions[$fkQuoted] = $keys[$foreignKey];
1459:
1460: if ($recursive == 1) {
1461: $conditions = array_merge($conditions, (array)$assoc['counterScope']);
1462: }
1463: $count = intval($this->find('count', compact('conditions', 'recursive')));
1464:
1465: $this->{$parent}->updateAll(
1466: array($assoc['counterCache'] => $count),
1467: array($this->{$parent}->escapeField() => $keys[$foreignKey])
1468: );
1469: }
1470: }
1471: }
1472: 1473: 1474: 1475: 1476: 1477: 1478: 1479:
1480: function _prepareUpdateFields($data) {
1481: $foreignKeys = array();
1482: foreach ($this->belongsTo as $assoc => $info) {
1483: if ($info['counterCache']) {
1484: $foreignKeys[$assoc] = $info['foreignKey'];
1485: }
1486: }
1487: $included = array_intersect($foreignKeys, array_keys($data));
1488:
1489: if (empty($included) || empty($this->id)) {
1490: return array();
1491: }
1492: $old = $this->find('first', array(
1493: 'conditions' => array($this->primaryKey => $this->id),
1494: 'fields' => array_values($included),
1495: 'recursive' => -1
1496: ));
1497: return array_merge($data, array('old' => $old[$this->alias]));
1498: }
1499: 1500: 1501: 1502: 1503: 1504: 1505: 1506: 1507: 1508: 1509: 1510: 1511: 1512: 1513: 1514: 1515: 1516: 1517: 1518: 1519: 1520: 1521:
1522: function saveAll($data = null, $options = array()) {
1523: if (empty($data)) {
1524: $data = $this->data;
1525: }
1526: $db =& ConnectionManager::getDataSource($this->useDbConfig);
1527:
1528: $options = array_merge(array('validate' => true, 'atomic' => true), $options);
1529: $this->validationErrors = $validationErrors = array();
1530: $validates = true;
1531: $return = array();
1532:
1533: if ($options['atomic'] && $options['validate'] !== 'only') {
1534: $db->begin($this);
1535: }
1536:
1537: if (Set::numeric(array_keys($data))) {
1538: while ($validates) {
1539: foreach ($data as $key => $record) {
1540: if (!$currentValidates = $this->__save($record, $options)) {
1541: $validationErrors[$key] = $this->validationErrors;
1542: }
1543:
1544: if ($options['validate'] === 'only' || $options['validate'] === 'first') {
1545: $validating = true;
1546: if ($options['atomic']) {
1547: $validates = $validates && $currentValidates;
1548: } else {
1549: $validates = $currentValidates;
1550: }
1551: } else {
1552: $validating = false;
1553: $validates = $currentValidates;
1554: }
1555:
1556: if (!$options['atomic']) {
1557: $return[] = $validates;
1558: } elseif (!$validates && !$validating) {
1559: break;
1560: }
1561: }
1562: $this->validationErrors = $validationErrors;
1563:
1564: switch (true) {
1565: case ($options['validate'] === 'only'):
1566: return ($options['atomic'] ? $validates : $return);
1567: break;
1568: case ($options['validate'] === 'first'):
1569: $options['validate'] = true;
1570: continue;
1571: break;
1572: default:
1573: if ($options['atomic']) {
1574: if ($validates && ($db->commit($this) !== false)) {
1575: return true;
1576: }
1577: $db->rollback($this);
1578: return false;
1579: }
1580: return $return;
1581: break;
1582: }
1583: }
1584: return $return;
1585: }
1586: $associations = $this->getAssociated();
1587:
1588: while ($validates) {
1589: foreach ($data as $association => $values) {
1590: if (isset($associations[$association])) {
1591: switch ($associations[$association]) {
1592: case 'belongsTo':
1593: if ($this->{$association}->__save($values, $options)) {
1594: $data[$this->alias][$this->belongsTo[$association]['foreignKey']] = $this->{$association}->id;
1595: } else {
1596: $validationErrors[$association] = $this->{$association}->validationErrors;
1597: $validates = false;
1598: }
1599: if (!$options['atomic']) {
1600: $return[$association][] = $validates;
1601: }
1602: break;
1603: }
1604: }
1605: }
1606: if (!$this->__save($data, $options)) {
1607: $validationErrors[$this->alias] = $this->validationErrors;
1608: $validates = false;
1609: }
1610: if (!$options['atomic']) {
1611: $return[$this->alias] = $validates;
1612: }
1613: $validating = ($options['validate'] === 'only' || $options['validate'] === 'first');
1614:
1615: foreach ($data as $association => $values) {
1616: if (!$validates && !$validating) {
1617: break;
1618: }
1619: if (isset($associations[$association])) {
1620: $type = $associations[$association];
1621: switch ($type) {
1622: case 'hasOne':
1623: $values[$this->{$type}[$association]['foreignKey']] = $this->id;
1624: if (!$this->{$association}->__save($values, $options)) {
1625: $validationErrors[$association] = $this->{$association}->validationErrors;
1626: $validates = false;
1627: }
1628: if (!$options['atomic']) {
1629: $return[$association][] = $validates;
1630: }
1631: break;
1632: case 'hasMany':
1633: foreach ($values as $i => $value) {
1634: $values[$i][$this->{$type}[$association]['foreignKey']] = $this->id;
1635: }
1636: $_options = array_merge($options, array('atomic' => false));
1637:
1638: if ($_options['validate'] === 'first') {
1639: $_options['validate'] = 'only';
1640: }
1641: $_return = $this->{$association}->saveAll($values, $_options);
1642:
1643: if ($_return === false || (is_array($_return) && in_array(false, $_return, true))) {
1644: $validationErrors[$association] = $this->{$association}->validationErrors;
1645: $validates = false;
1646: }
1647: if (is_array($_return)) {
1648: foreach ($_return as $val) {
1649: if (!isset($return[$association])) {
1650: $return[$association] = array();
1651: } elseif (!is_array($return[$association])) {
1652: $return[$association] = array($return[$association]);
1653: }
1654: $return[$association][] = $val;
1655: }
1656: } else {
1657: $return[$association] = $_return;
1658: }
1659: break;
1660: }
1661: }
1662: }
1663: $this->validationErrors = $validationErrors;
1664:
1665: if (isset($validationErrors[$this->alias])) {
1666: $this->validationErrors = $validationErrors[$this->alias];
1667: }
1668:
1669: switch (true) {
1670: case ($options['validate'] === 'only'):
1671: return ($options['atomic'] ? $validates : $return);
1672: break;
1673: case ($options['validate'] === 'first'):
1674: $options['validate'] = true;
1675: continue;
1676: break;
1677: default:
1678: if ($options['atomic']) {
1679: if ($validates) {
1680: return ($db->commit($this) !== false);
1681: } else {
1682: $db->rollback($this);
1683: }
1684: }
1685: return $return;
1686: break;
1687: }
1688: }
1689: }
1690: 1691: 1692: 1693: 1694: 1695: 1696:
1697: function __save($data, $options) {
1698: if ($options['validate'] === 'first' || $options['validate'] === 'only') {
1699: if (!($this->create($data) && $this->validates($options))) {
1700: return false;
1701: }
1702: } elseif (!($this->create(null) !== null && $this->save($data, $options))) {
1703: return false;
1704: }
1705: return true;
1706: }
1707: 1708: 1709: 1710: 1711: 1712: 1713: 1714: 1715: 1716:
1717: function updateAll($fields, $conditions = true) {
1718: $db =& ConnectionManager::getDataSource($this->useDbConfig);
1719: return $db->update($this, $fields, null, $conditions);
1720: }
1721: 1722: 1723: 1724: 1725: 1726: 1727: 1728: 1729: 1730:
1731: function remove($id = null, $cascade = true) {
1732: return $this->del($id, $cascade);
1733: }
1734: 1735: 1736: 1737: 1738: 1739: 1740: 1741: 1742:
1743: function del($id = null, $cascade = true) {
1744: if (!empty($id)) {
1745: $this->id = $id;
1746: }
1747: $id = $this->id;
1748:
1749:
1750: if ($this->beforeDelete($cascade)) {
1751: $filters = $this->Behaviors->trigger($this, 'beforeDelete', array($cascade), array(
1752: 'break' => true, 'breakOn' => false
1753: ));
1754: if (!$filters || !$this->exists()) {
1755: return false;
1756: }
1757: $db =& ConnectionManager::getDataSource($this->useDbConfig);
1758:
1759: $this->_deleteDependent($id, $cascade);
1760: $this->_deleteLinks($id);
1761: $this->id = $id;
1762:
1763: if (!empty($this->belongsTo)) {
1764: $keys = $this->find('first', array('fields' => $this->__collectForeignKeys()));
1765: }
1766:
1767: if ($db->delete($this, array($this->alias . '.' . $this->primaryKey => $id))) {
1768: if (!empty($this->belongsTo)) {
1769: $this->updateCounterCache($keys[$this->alias]);
1770: }
1771: $this->Behaviors->trigger($this, 'afterDelete');
1772: $this->afterDelete();
1773: $this->_clearCache();
1774: $this->id = false;
1775: $this->__exists = null;
1776: return true;
1777: }
1778: }
1779: return false;
1780: }
1781: 1782: 1783: 1784: 1785: 1786: 1787: 1788: 1789:
1790: function delete($id = null, $cascade = true) {
1791: return $this->del($id, $cascade);
1792: }
1793: 1794: 1795: 1796: 1797: 1798: 1799: 1800:
1801: function _deleteDependent($id, $cascade) {
1802: if (!empty($this->__backAssociation)) {
1803: $savedAssociatons = $this->__backAssociation;
1804: $this->__backAssociation = array();
1805: }
1806: foreach (array_merge($this->hasMany, $this->hasOne) as $assoc => $data) {
1807: if ($data['dependent'] === true && $cascade === true) {
1808:
1809: $model =& $this->{$assoc};
1810: $conditions = array($model->escapeField($data['foreignKey']) => $id);
1811: if ($data['conditions']) {
1812: $conditions = array_merge($data['conditions'], $conditions);
1813: }
1814: $model->recursive = -1;
1815:
1816: if (isset($data['exclusive']) && $data['exclusive']) {
1817: $model->deleteAll($conditions);
1818: } else {
1819: $records = $model->find('all', array('conditions' => $conditions, 'fields' => $model->primaryKey));
1820:
1821: if (!empty($records)) {
1822: foreach ($records as $record) {
1823: $model->delete($record[$model->alias][$model->primaryKey]);
1824: }
1825: }
1826: }
1827: }
1828: }
1829: if (isset($savedAssociatons)) {
1830: $this->__backAssociation = $savedAssociatons;
1831: }
1832: }
1833: 1834: 1835: 1836: 1837: 1838: 1839:
1840: function _deleteLinks($id) {
1841: foreach ($this->hasAndBelongsToMany as $assoc => $data) {
1842: $joinModel = $data['with'];
1843: $records = $this->{$joinModel}->find('all', array(
1844: 'conditions' => array_merge(array($this->{$joinModel}->escapeField($data['foreignKey']) => $id)),
1845: 'fields' => $this->{$joinModel}->primaryKey,
1846: 'recursive' => -1
1847: ));
1848: if (!empty($records)) {
1849: foreach ($records as $record) {
1850: $this->{$joinModel}->delete($record[$this->{$joinModel}->alias][$this->{$joinModel}->primaryKey]);
1851: }
1852: }
1853: }
1854: }
1855: 1856: 1857: 1858: 1859: 1860: 1861: 1862: 1863: 1864:
1865: function deleteAll($conditions, $cascade = true, $callbacks = false) {
1866: if (empty($conditions)) {
1867: return false;
1868: }
1869: $db =& ConnectionManager::getDataSource($this->useDbConfig);
1870:
1871: if (!$cascade && !$callbacks) {
1872: return $db->delete($this, $conditions);
1873: } else {
1874: $ids = Set::extract(
1875: $this->find('all', array_merge(array('fields' => "{$this->alias}.{$this->primaryKey}", 'recursive' => 0), compact('conditions'))),
1876: "{n}.{$this->alias}.{$this->primaryKey}"
1877: );
1878:
1879: if (empty($ids)) {
1880: return true;
1881: }
1882:
1883: if ($callbacks) {
1884: $_id = $this->id;
1885: $result = true;
1886: foreach ($ids as $id) {
1887: $result = ($result && $this->delete($id, $cascade));
1888: }
1889: $this->id = $_id;
1890: return $result;
1891: } else {
1892: foreach ($ids as $id) {
1893: $this->_deleteLinks($id);
1894: if ($cascade) {
1895: $this->_deleteDependent($id, $cascade);
1896: }
1897: }
1898: return $db->delete($this, array($this->alias . '.' . $this->primaryKey => $ids));
1899: }
1900: }
1901: }
1902: 1903: 1904: 1905: 1906: 1907:
1908: function __collectForeignKeys($type = 'belongsTo') {
1909: $result = array();
1910:
1911: foreach ($this->{$type} as $assoc => $data) {
1912: if (isset($data['foreignKey']) && is_string($data['foreignKey'])) {
1913: $result[$assoc] = $data['foreignKey'];
1914: }
1915: }
1916: return $result;
1917: }
1918: 1919: 1920: 1921: 1922: 1923: 1924:
1925: function exists($reset = false) {
1926: if (is_array($reset)) {
1927: extract($reset, EXTR_OVERWRITE);
1928: }
1929:
1930: if ($this->getID() === false || $this->useTable === false) {
1931: return false;
1932: }
1933: if (!empty($this->__exists) && $reset !== true) {
1934: return $this->__exists;
1935: }
1936: $conditions = array($this->alias . '.' . $this->primaryKey => $this->getID());
1937: $query = array('conditions' => $conditions, 'recursive' => -1, 'callbacks' => false);
1938:
1939: if (is_array($reset)) {
1940: $query = array_merge($query, $reset);
1941: }
1942: return $this->__exists = ($this->find('count', $query) > 0);
1943: }
1944: 1945: 1946: 1947: 1948: 1949: 1950:
1951: function hasAny($conditions = null) {
1952: return ($this->find('count', array('conditions' => $conditions, 'recursive' => -1)) != false);
1953: }
1954: 1955: 1956: 1957: 1958: 1959: 1960: 1961: 1962: 1963: 1964: 1965: 1966: 1967: 1968: 1969: 1970: 1971: 1972: 1973: 1974: 1975: 1976: 1977: 1978: 1979: 1980: 1981: 1982: 1983: 1984: 1985: 1986: 1987:
1988: function find($conditions = null, $fields = array(), $order = null, $recursive = null) {
1989: if (!is_string($conditions) || (is_string($conditions) && !array_key_exists($conditions, $this->_findMethods))) {
1990: $type = 'first';
1991: $query = array_merge(compact('conditions', 'fields', 'order', 'recursive'), array('limit' => 1));
1992: } else {
1993: list($type, $query) = array($conditions, $fields);
1994: }
1995:
1996: $this->findQueryType = $type;
1997: $this->id = $this->getID();
1998:
1999: $query = array_merge(
2000: array(
2001: 'conditions' => null, 'fields' => null, 'joins' => array(), 'limit' => null,
2002: 'offset' => null, 'order' => null, 'page' => null, 'group' => null, 'callbacks' => true
2003: ),
2004: (array)$query
2005: );
2006:
2007: if ($type != 'all') {
2008: if ($this->_findMethods[$type] === true) {
2009: $query = $this->{'_find' . ucfirst($type)}('before', $query);
2010: }
2011: }
2012:
2013: if (!is_numeric($query['page']) || intval($query['page']) < 1) {
2014: $query['page'] = 1;
2015: }
2016: if ($query['page'] > 1 && !empty($query['limit'])) {
2017: $query['offset'] = ($query['page'] - 1) * $query['limit'];
2018: }
2019: if ($query['order'] === null && $this->order !== null) {
2020: $query['order'] = $this->order;
2021: }
2022: $query['order'] = array($query['order']);
2023:
2024: if ($query['callbacks'] === true || $query['callbacks'] === 'before') {
2025: $return = $this->Behaviors->trigger($this, 'beforeFind', array($query), array(
2026: 'break' => true, 'breakOn' => false, 'modParams' => true
2027: ));
2028: $query = (is_array($return)) ? $return : $query;
2029:
2030: if ($return === false) {
2031: return null;
2032: }
2033:
2034: $return = $this->beforeFind($query);
2035: $query = (is_array($return)) ? $return : $query;
2036:
2037: if ($return === false) {
2038: return null;
2039: }
2040: }
2041:
2042: if (!$db =& ConnectionManager::getDataSource($this->useDbConfig)) {
2043: return false;
2044: }
2045: $results = $db->read($this, $query);
2046: $this->resetAssociations();
2047: $this->findQueryType = null;
2048:
2049: if ($query['callbacks'] === true || $query['callbacks'] === 'after') {
2050: $results = $this->__filterResults($results);
2051: }
2052:
2053: if ($type === 'all') {
2054: return $results;
2055: } else {
2056: if ($this->_findMethods[$type] === true) {
2057: return $this->{'_find' . ucfirst($type)}('after', $query, $results);
2058: }
2059: }
2060: }
2061: 2062: 2063: 2064: 2065: 2066: 2067: 2068: 2069: 2070:
2071: function _findFirst($state, $query, $results = array()) {
2072: if ($state == 'before') {
2073: $query['limit'] = 1;
2074: if (empty($query['conditions']) && !empty($this->id)) {
2075: $query['conditions'] = array($this->escapeField() => $this->id);
2076: }
2077: return $query;
2078: } elseif ($state == 'after') {
2079: if (empty($results[0])) {
2080: return false;
2081: }
2082: return $results[0];
2083: }
2084: }
2085: 2086: 2087: 2088: 2089: 2090: 2091: 2092: 2093: 2094:
2095: function _findCount($state, $query, $results = array()) {
2096: if ($state == 'before') {
2097: $db =& ConnectionManager::getDataSource($this->useDbConfig);
2098: if (empty($query['fields'])) {
2099: $query['fields'] = $db->calculate($this, 'count');
2100: } elseif (is_string($query['fields']) && !preg_match('/count/i', $query['fields'])) {
2101: $query['fields'] = $db->calculate($this, 'count', array(
2102: $db->expression($query['fields']), 'count'
2103: ));
2104: }
2105: $query['order'] = false;
2106: return $query;
2107: } elseif ($state == 'after') {
2108: if (isset($results[0][0]['count'])) {
2109: return intval($results[0][0]['count']);
2110: } elseif (isset($results[0][$this->alias]['count'])) {
2111: return intval($results[0][$this->alias]['count']);
2112: }
2113: return false;
2114: }
2115: }
2116: 2117: 2118: 2119: 2120: 2121: 2122: 2123: 2124: 2125:
2126: function _findList($state, $query, $results = array()) {
2127: if ($state == 'before') {
2128: if (empty($query['fields'])) {
2129: $query['fields'] = array("{$this->alias}.{$this->primaryKey}", "{$this->alias}.{$this->displayField}");
2130: $list = array("{n}.{$this->alias}.{$this->primaryKey}", "{n}.{$this->alias}.{$this->displayField}", null);
2131: } else {
2132: if (!is_array($query['fields'])) {
2133: $query['fields'] = String::tokenize($query['fields']);
2134: }
2135:
2136: if (count($query['fields']) == 1) {
2137: if (strpos($query['fields'][0], '.') === false) {
2138: $query['fields'][0] = $this->alias . '.' . $query['fields'][0];
2139: }
2140:
2141: $list = array("{n}.{$this->alias}.{$this->primaryKey}", '{n}.' . $query['fields'][0], null);
2142: $query['fields'] = array("{$this->alias}.{$this->primaryKey}", $query['fields'][0]);
2143: } elseif (count($query['fields']) == 3) {
2144: for ($i = 0; $i < 3; $i++) {
2145: if (strpos($query['fields'][$i], '.') === false) {
2146: $query['fields'][$i] = $this->alias . '.' . $query['fields'][$i];
2147: }
2148: }
2149:
2150: $list = array('{n}.' . $query['fields'][0], '{n}.' . $query['fields'][1], '{n}.' . $query['fields'][2]);
2151: } else {
2152: for ($i = 0; $i < 2; $i++) {
2153: if (strpos($query['fields'][$i], '.') === false) {
2154: $query['fields'][$i] = $this->alias . '.' . $query['fields'][$i];
2155: }
2156: }
2157:
2158: $list = array('{n}.' . $query['fields'][0], '{n}.' . $query['fields'][1], null);
2159: }
2160: }
2161: if (!isset($query['recursive']) || $query['recursive'] === null) {
2162: $query['recursive'] = -1;
2163: }
2164: list($query['list']['keyPath'], $query['list']['valuePath'], $query['list']['groupPath']) = $list;
2165: return $query;
2166: } elseif ($state == 'after') {
2167: if (empty($results)) {
2168: return array();
2169: }
2170: $lst = $query['list'];
2171: return Set::combine($results, $lst['keyPath'], $lst['valuePath'], $lst['groupPath']);
2172: }
2173: }
2174: 2175: 2176: 2177: 2178: 2179: 2180: 2181: 2182: 2183:
2184: function _findNeighbors($state, $query, $results = array()) {
2185: if ($state == 'before') {
2186: $query = array_merge(array('recursive' => 0), $query);
2187: extract($query);
2188: $conditions = (array)$conditions;
2189: if (isset($field) && isset($value)) {
2190: if (strpos($field, '.') === false) {
2191: $field = $this->alias . '.' . $field;
2192: }
2193: } else {
2194: $field = $this->alias . '.' . $this->primaryKey;
2195: $value = $this->id;
2196: }
2197: $query['conditions'] = array_merge($conditions, array($field . ' <' => $value));
2198: $query['order'] = $field . ' DESC';
2199: $query['limit'] = 1;
2200: $query['field'] = $field;
2201: $query['value'] = $value;
2202: return $query;
2203: } elseif ($state == 'after') {
2204: extract($query);
2205: unset($query['conditions'][$field . ' <']);
2206: $return = array();
2207: if (isset($results[0])) {
2208: $prevVal = Set::extract('/' . str_replace('.', '/', $field), $results[0]);
2209: $query['conditions'][$field . ' >='] = $prevVal[0];
2210: $query['conditions'][$field . ' !='] = $value;
2211: $query['limit'] = 2;
2212: } else {
2213: $return['prev'] = null;
2214: $query['conditions'][$field . ' >'] = $value;
2215: $query['limit'] = 1;
2216: }
2217: $query['order'] = $field . ' ASC';
2218: $return2 = $this->find('all', $query);
2219: if (!array_key_exists('prev', $return)) {
2220: $return['prev'] = $return2[0];
2221: }
2222: if (count($return2) == 2) {
2223: $return['next'] = $return2[1];
2224: } elseif (count($return2) == 1 && !$return['prev']) {
2225: $return['next'] = $return2[0];
2226: } else {
2227: $return['next'] = null;
2228: }
2229: return $return;
2230: }
2231: }
2232: 2233: 2234: 2235: 2236: 2237: 2238: 2239: 2240: 2241:
2242: function _findThreaded($state, $query, $results = array()) {
2243: if ($state == 'before') {
2244: return $query;
2245: } elseif ($state == 'after') {
2246: $return = $idMap = array();
2247: $ids = Set::extract($results, '{n}.' . $this->alias . '.' . $this->primaryKey);
2248:
2249: foreach ($results as $result) {
2250: $result['children'] = array();
2251: $id = $result[$this->alias][$this->primaryKey];
2252: $parentId = $result[$this->alias]['parent_id'];
2253: if (isset($idMap[$id]['children'])) {
2254: $idMap[$id] = array_merge($result, (array)$idMap[$id]);
2255: } else {
2256: $idMap[$id] = array_merge($result, array('children' => array()));
2257: }
2258: if (!$parentId || !in_array($parentId, $ids)) {
2259: $return[] =& $idMap[$id];
2260: } else {
2261: $idMap[$parentId]['children'][] =& $idMap[$id];
2262: }
2263: }
2264: if (count($return) > 1) {
2265: $ids = array_unique(Set::extract('/' . $this->alias . '/parent_id', $return));
2266: if (count($ids) > 1) {
2267: $root = $return[0][$this->alias]['parent_id'];
2268: foreach ($return as $key => $value) {
2269: if ($value[$this->alias]['parent_id'] != $root) {
2270: unset($return[$key]);
2271: }
2272: }
2273: }
2274: }
2275: return $return;
2276: }
2277: }
2278: 2279: 2280: 2281: 2282: 2283: 2284: 2285:
2286: function __filterResults($results, $primary = true) {
2287: $return = $this->Behaviors->trigger($this, 'afterFind', array($results, $primary), array('modParams' => true));
2288: if ($return !== true) {
2289: $results = $return;
2290: }
2291: return $this->afterFind($results, $primary);
2292: }
2293: 2294: 2295: 2296: 2297: 2298: 2299: 2300:
2301: function resetAssociations() {
2302: if (!empty($this->__backAssociation)) {
2303: foreach ($this->__associations as $type) {
2304: if (isset($this->__backAssociation[$type])) {
2305: $this->{$type} = $this->__backAssociation[$type];
2306: }
2307: }
2308: $this->__backAssociation = array();
2309: }
2310:
2311: foreach ($this->__associations as $type) {
2312: foreach ($this->{$type} as $key => $name) {
2313: if (!empty($this->{$key}->__backAssociation)) {
2314: $this->{$key}->resetAssociations();
2315: }
2316: }
2317: }
2318: $this->__backAssociation = array();
2319: return true;
2320: }
2321: 2322: 2323: 2324: 2325: 2326: 2327: 2328:
2329: function isUnique($fields, $or = true) {
2330: if (!is_array($fields)) {
2331: $fields = func_get_args();
2332: if (is_bool($fields[count($fields) - 1])) {
2333: $or = $fields[count($fields) - 1];
2334: unset($fields[count($fields) - 1]);
2335: }
2336: }
2337:
2338: foreach ($fields as $field => $value) {
2339: if (is_numeric($field)) {
2340: unset($fields[$field]);
2341:
2342: $field = $value;
2343: if (isset($this->data[$this->alias][$field])) {
2344: $value = $this->data[$this->alias][$field];
2345: } else {
2346: $value = null;
2347: }
2348: }
2349:
2350: if (strpos($field, '.') === false) {
2351: unset($fields[$field]);
2352: $fields[$this->alias . '.' . $field] = $value;
2353: }
2354: }
2355: if ($or) {
2356: $fields = array('or' => $fields);
2357: }
2358: if (!empty($this->id)) {
2359: $fields[$this->alias . '.' . $this->primaryKey . ' !='] = $this->id;
2360: }
2361: return ($this->find('count', array('conditions' => $fields, 'recursive' => -1)) == 0);
2362: }
2363: 2364: 2365: 2366: 2367: 2368: 2369: 2370:
2371: function query() {
2372: $params = func_get_args();
2373: $db =& ConnectionManager::getDataSource($this->useDbConfig);
2374: return call_user_func_array(array(&$db, 'query'), $params);
2375: }
2376: 2377: 2378: 2379: 2380: 2381: 2382: 2383: 2384: 2385: 2386:
2387: function validates($options = array()) {
2388: $errors = $this->invalidFields($options);
2389: if (empty($errors) && $errors !== false) {
2390: $errors = $this->__validateWithModels($options);
2391: }
2392: if (is_array($errors)) {
2393: return count($errors) === 0;
2394: }
2395: return $errors;
2396: }
2397: 2398: 2399: 2400: 2401: 2402: 2403: 2404: 2405:
2406: function invalidFields($options = array()) {
2407: if (
2408: !$this->Behaviors->trigger(
2409: $this,
2410: 'beforeValidate',
2411: array($options),
2412: array('break' => true, 'breakOn' => false)
2413: ) ||
2414: $this->beforeValidate($options) === false
2415: ) {
2416: return false;
2417: }
2418:
2419: if (!isset($this->validate) || empty($this->validate)) {
2420: return $this->validationErrors;
2421: }
2422:
2423: $data = $this->data;
2424: $methods = array_map('strtolower', get_class_methods($this));
2425: $behaviorMethods = array_keys($this->Behaviors->methods());
2426:
2427: if (isset($data[$this->alias])) {
2428: $data = $data[$this->alias];
2429: } elseif (!is_array($data)) {
2430: $data = array();
2431: }
2432:
2433: $Validation =& Validation::getInstance();
2434: $this->exists();
2435:
2436: $_validate = $this->validate;
2437: $whitelist = $this->whitelist;
2438:
2439: if (!empty($options['fieldList'])) {
2440: $whitelist = $options['fieldList'];
2441: }
2442:
2443: if (!empty($whitelist)) {
2444: $validate = array();
2445: foreach ((array)$whitelist as $f) {
2446: if (!empty($this->validate[$f])) {
2447: $validate[$f] = $this->validate[$f];
2448: }
2449: }
2450: $this->validate = $validate;
2451: }
2452:
2453: foreach ($this->validate as $fieldName => $ruleSet) {
2454: if (!is_array($ruleSet) || (is_array($ruleSet) && isset($ruleSet['rule']))) {
2455: $ruleSet = array($ruleSet);
2456: }
2457: $default = array(
2458: 'allowEmpty' => null,
2459: 'required' => null,
2460: 'rule' => 'blank',
2461: 'last' => false,
2462: 'on' => null
2463: );
2464:
2465: foreach ($ruleSet as $index => $validator) {
2466: if (!is_array($validator)) {
2467: $validator = array('rule' => $validator);
2468: }
2469: $validator = array_merge($default, $validator);
2470:
2471: if (isset($validator['message'])) {
2472: $message = $validator['message'];
2473: } else {
2474: $message = __('This field cannot be left blank', true);
2475: }
2476:
2477: if (
2478: empty($validator['on']) || ($validator['on'] == 'create' &&
2479: !$this->__exists) || ($validator['on'] == 'update' && $this->__exists
2480: )) {
2481: $required = (
2482: (!isset($data[$fieldName]) && $validator['required'] === true) ||
2483: (
2484: isset($data[$fieldName]) && (empty($data[$fieldName]) &&
2485: !is_numeric($data[$fieldName])) && $validator['allowEmpty'] === false
2486: )
2487: );
2488:
2489: if ($required) {
2490: $this->invalidate($fieldName, $message);
2491: if ($validator['last']) {
2492: break;
2493: }
2494: } elseif (array_key_exists($fieldName, $data)) {
2495: if (empty($data[$fieldName]) && $data[$fieldName] != '0' && $validator['allowEmpty'] === true) {
2496: break;
2497: }
2498: if (is_array($validator['rule'])) {
2499: $rule = $validator['rule'][0];
2500: unset($validator['rule'][0]);
2501: $ruleParams = array_merge(array($data[$fieldName]), array_values($validator['rule']));
2502: } else {
2503: $rule = $validator['rule'];
2504: $ruleParams = array($data[$fieldName]);
2505: }
2506:
2507: $valid = true;
2508:
2509: if (in_array(strtolower($rule), $methods)) {
2510: $ruleParams[] = $validator;
2511: $ruleParams[0] = array($fieldName => $ruleParams[0]);
2512: $valid = $this->dispatchMethod($rule, $ruleParams);
2513: } elseif (in_array($rule, $behaviorMethods) || in_array(strtolower($rule), $behaviorMethods)) {
2514: $ruleParams[] = $validator;
2515: $ruleParams[0] = array($fieldName => $ruleParams[0]);
2516: $valid = $this->Behaviors->dispatchMethod($this, $rule, $ruleParams);
2517: } elseif (method_exists($Validation, $rule)) {
2518: $valid = $Validation->dispatchMethod($rule, $ruleParams);
2519: } elseif (!is_array($validator['rule'])) {
2520: $valid = preg_match($rule, $data[$fieldName]);
2521: }
2522:
2523: if (!$valid || (is_string($valid) && strlen($valid) > 0)) {
2524: if (is_string($valid) && strlen($valid) > 0) {
2525: $validator['message'] = $valid;
2526: } elseif (!isset($validator['message'])) {
2527: if (is_string($index)) {
2528: $validator['message'] = $index;
2529: } elseif (is_numeric($index) && count($ruleSet) > 1) {
2530: $validator['message'] = $index + 1;
2531: } else {
2532: $validator['message'] = $message;
2533: }
2534: }
2535: $this->invalidate($fieldName, $validator['message']);
2536:
2537: if ($validator['last']) {
2538: break;
2539: }
2540: }
2541: }
2542: }
2543: }
2544: }
2545: $this->validate = $_validate;
2546: return $this->validationErrors;
2547: }
2548: 2549: 2550: 2551: 2552: 2553: 2554: 2555: 2556:
2557: function __validateWithModels($options) {
2558: $valid = true;
2559: foreach ($this->hasAndBelongsToMany as $assoc => $association) {
2560: if (empty($association['with']) || !isset($this->data[$assoc])) {
2561: continue;
2562: }
2563: list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']);
2564: $data = $this->data[$assoc];
2565:
2566: $newData = array();
2567: foreach ((array)$data as $row) {
2568: if (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
2569: $newData[] = $row;
2570: } elseif (isset($row[$join]) && isset($row[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
2571: $newData[] = $row[$join];
2572: }
2573: }
2574: if (empty($newData)) {
2575: continue;
2576: }
2577: foreach ($newData as $data) {
2578: $data[$this->hasAndBelongsToMany[$assoc]['foreignKey']] = $this->id;
2579: $this->{$join}->create($data);
2580: $valid = ($valid && $this->{$join}->validates($options));
2581: }
2582: }
2583: return $valid;
2584: }
2585: 2586: 2587: 2588: 2589: 2590: 2591: 2592: 2593:
2594: function invalidate($field, $value = true) {
2595: if (!is_array($this->validationErrors)) {
2596: $this->validationErrors = array();
2597: }
2598: $this->validationErrors[$field] = $value;
2599: }
2600: 2601: 2602: 2603: 2604: 2605: 2606:
2607: function isForeignKey($field) {
2608: $foreignKeys = array();
2609: if (!empty($this->belongsTo)) {
2610: foreach ($this->belongsTo as $assoc => $data) {
2611: $foreignKeys[] = $data['foreignKey'];
2612: }
2613: }
2614: return in_array($field, $foreignKeys);
2615: }
2616: 2617: 2618: 2619: 2620: 2621: 2622:
2623: function getDisplayField() {
2624: return $this->displayField;
2625: }
2626: 2627: 2628: 2629: 2630: 2631: 2632: 2633:
2634: function escapeField($field = null, $alias = null) {
2635: if (empty($alias)) {
2636: $alias = $this->alias;
2637: }
2638: if (empty($field)) {
2639: $field = $this->primaryKey;
2640: }
2641: $db =& ConnectionManager::getDataSource($this->useDbConfig);
2642: if (strpos($field, $db->name($alias) . '.') === 0) {
2643: return $field;
2644: }
2645: return $db->name($alias . '.' . $field);
2646: }
2647: 2648: 2649: 2650: 2651: 2652: 2653:
2654: function getID($list = 0) {
2655: if (empty($this->id) || (is_array($this->id) && isset($this->id[0]) && empty($this->id[0]))) {
2656: return false;
2657: }
2658:
2659: if (!is_array($this->id)) {
2660: return $this->id;
2661: }
2662:
2663: if (empty($this->id)) {
2664: return false;
2665: }
2666:
2667: if (isset($this->id[$list]) && !empty($this->id[$list])) {
2668: return $this->id[$list];
2669: } elseif (isset($this->id[$list])) {
2670: return false;
2671: }
2672:
2673: foreach ($this->id as $id) {
2674: return $id;
2675: }
2676:
2677: return false;
2678: }
2679: 2680: 2681: 2682: 2683: 2684:
2685: function getLastInsertID() {
2686: return $this->getInsertID();
2687: }
2688: 2689: 2690: 2691: 2692: 2693:
2694: function getInsertID() {
2695: return $this->__insertID;
2696: }
2697: 2698: 2699: 2700: 2701: 2702:
2703: function setInsertID($id) {
2704: $this->__insertID = $id;
2705: }
2706: 2707: 2708: 2709: 2710: 2711:
2712: function getNumRows() {
2713: $db =& ConnectionManager::getDataSource($this->useDbConfig);
2714: return $db->lastNumRows();
2715: }
2716: 2717: 2718: 2719: 2720: 2721:
2722: function getAffectedRows() {
2723: $db =& ConnectionManager::getDataSource($this->useDbConfig);
2724: return $db->lastAffected();
2725: }
2726: 2727: 2728: 2729: 2730: 2731: 2732:
2733: function setDataSource($dataSource = null) {
2734: $oldConfig = $this->useDbConfig;
2735:
2736: if ($dataSource != null) {
2737: $this->useDbConfig = $dataSource;
2738: }
2739: $db =& ConnectionManager::getDataSource($this->useDbConfig);
2740: if (!empty($oldConfig) && isset($db->config['prefix'])) {
2741: $oldDb =& ConnectionManager::getDataSource($oldConfig);
2742:
2743: if (!isset($this->tablePrefix) || (!isset($oldDb->config['prefix']) || $this->tablePrefix == $oldDb->config['prefix'])) {
2744: $this->tablePrefix = $db->config['prefix'];
2745: }
2746: } elseif (isset($db->config['prefix'])) {
2747: $this->tablePrefix = $db->config['prefix'];
2748: }
2749:
2750: if (empty($db) || !is_object($db)) {
2751: return $this->cakeError('missingConnection', array(array('className' => $this->alias)));
2752: }
2753: }
2754: 2755: 2756: 2757: 2758: 2759: 2760:
2761: function &getDataSource() {
2762: $db =& ConnectionManager::getDataSource($this->useDbConfig);
2763: return $db;
2764: }
2765: 2766: 2767: 2768: 2769: 2770: 2771:
2772: function getAssociated($type = null) {
2773: if ($type == null) {
2774: $associated = array();
2775: foreach ($this->__associations as $assoc) {
2776: if (!empty($this->{$assoc})) {
2777: $models = array_keys($this->{$assoc});
2778: foreach ($models as $m) {
2779: $associated[$m] = $assoc;
2780: }
2781: }
2782: }
2783: return $associated;
2784: } elseif (in_array($type, $this->__associations)) {
2785: if (empty($this->{$type})) {
2786: return array();
2787: }
2788: return array_keys($this->{$type});
2789: } else {
2790: $assoc = array_merge($this->hasOne, $this->hasMany, $this->belongsTo, $this->hasAndBelongsToMany);
2791: if (array_key_exists($type, $assoc)) {
2792: foreach ($this->__associations as $a) {
2793: if (isset($this->{$a}[$type])) {
2794: $assoc[$type]['association'] = $a;
2795: break;
2796: }
2797: }
2798: return $assoc[$type];
2799: }
2800: return null;
2801: }
2802: }
2803: 2804: 2805: 2806: 2807: 2808: 2809: 2810: 2811:
2812: function joinModel($assoc, $keys = array()) {
2813: if (is_string($assoc)) {
2814: return array($assoc, array_keys($this->{$assoc}->schema()));
2815: } elseif (is_array($assoc)) {
2816: $with = key($assoc);
2817: return array($with, array_unique(array_merge($assoc[$with], $keys)));
2818: } else {
2819: trigger_error(sprintf(__('Invalid join model settings in %s', true), $model->alias), E_USER_WARNING);
2820: }
2821: }
2822: 2823: 2824: 2825: 2826: 2827: 2828: 2829: 2830:
2831: function beforeFind($queryData) {
2832: return true;
2833: }
2834: 2835: 2836: 2837: 2838: 2839: 2840: 2841: 2842: 2843:
2844: function afterFind($results, $primary = false) {
2845: return $results;
2846: }
2847: 2848: 2849: 2850: 2851: 2852: 2853: 2854:
2855: function beforeSave($options = array()) {
2856: return true;
2857: }
2858: 2859: 2860: 2861: 2862: 2863: 2864:
2865: function afterSave($created) {
2866: }
2867: 2868: 2869: 2870: 2871: 2872: 2873: 2874:
2875: function beforeDelete($cascade = true) {
2876: return true;
2877: }
2878: 2879: 2880: 2881: 2882: 2883:
2884: function afterDelete() {
2885: }
2886: 2887: 2888: 2889: 2890: 2891: 2892: 2893: 2894:
2895: function beforeValidate($options = array()) {
2896: return true;
2897: }
2898: 2899: 2900: 2901: 2902: 2903:
2904: function onError() {
2905: }
2906: 2907: 2908: 2909: 2910: 2911: 2912: 2913: 2914:
2915: function _clearCache($type = null) {
2916: if ($type === null) {
2917: if (Configure::read('Cache.check') === true) {
2918: $assoc[] = strtolower(Inflector::pluralize($this->alias));
2919: $assoc[] = strtolower(Inflector::underscore(Inflector::pluralize($this->alias)));
2920: foreach ($this->__associations as $key => $association) {
2921: foreach ($this->$association as $key => $className) {
2922: $check = strtolower(Inflector::pluralize($className['className']));
2923: if (!in_array($check, $assoc)) {
2924: $assoc[] = strtolower(Inflector::pluralize($className['className']));
2925: $assoc[] = strtolower(Inflector::underscore(Inflector::pluralize($className['className'])));
2926: }
2927: }
2928: }
2929: clearCache($assoc);
2930: return true;
2931: }
2932: } else {
2933:
2934: }
2935: }
2936: 2937: 2938: 2939: 2940: 2941:
2942: function __sleep() {
2943: $return = array_keys(get_object_vars($this));
2944: return $return;
2945: }
2946: 2947: 2948: 2949: 2950: 2951:
2952: function __wakeup() {
2953: }
2954: 2955: 2956: 2957:
2958: function findAll($conditions = null, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null) {
2959:
2960: return $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive'));
2961: }
2962: 2963: 2964: 2965:
2966: function findCount($conditions = null, $recursive = 0) {
2967:
2968: return $this->find('count', compact('conditions', 'recursive'));
2969: }
2970: 2971: 2972: 2973:
2974: function findAllThreaded($conditions = null, $fields = null, $order = null) {
2975:
2976: return $this->find('threaded', compact('conditions', 'fields', 'order'));
2977: }
2978: 2979: 2980: 2981:
2982: function findNeighbours($conditions = null, $field, $value) {
2983:
2984: $query = compact('conditions', 'field', 'value');
2985: $query['fields'] = $field;
2986: if (is_array($field)) {
2987: $query['field'] = $field[0];
2988: }
2989: return $this->find('neighbors', $query);
2990: }
2991: }
2992: if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
2993: Overloadable::overload('Model');
2994: }
2995: ?>
2996: