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: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46:
47: class AlphaDAOProviderMySQL implements AlphaDAOProviderInterface {
48: 49: 50: 51: 52: 53:
54: private static $logger = null;
55:
56: 57: 58: 59: 60: 61:
62: private static $connection;
63:
64: 65: 66: 67: 68: 69:
70: private $BO;
71:
72: 73: 74: 75: 76:
77: public function __construct() {
78: self::$logger = new Logger('AlphaDAOProviderMySQL');
79: self::$logger->debug('>>__construct()');
80:
81: self::$logger->debug('<<__construct');
82: }
83:
84: 85: 86: 87:
88: public static function getConnection() {
89: global $config;
90:
91: if (!isset(self::$connection)) {
92: self::$connection = new mysqli($config->get('db.hostname'), $config->get('db.username'), $config->get('db.password'), $config->get('db.name'));
93: self::$connection->set_charset('utf8');
94:
95: if (mysqli_connect_error()) {
96: self::$logger->fatal('Could not connect to database: ['.mysqli_connect_errno().'] '.mysqli_connect_error());
97: }
98: }
99:
100: return self::$connection;
101: }
102:
103: 104: 105: 106:
107: public static function disconnect() {
108: if (isset(self::$connection)) {
109: self::$connection->close();
110: self::$connection = null;
111: }
112: }
113:
114: 115: 116: 117:
118: public static function getLastDatabaseError() {
119: return self::getConnection()->error;
120: }
121:
122: 123: 124: 125:
126: public function query($sqlQuery) {
127: $this->BO->setLastQuery($sqlQuery);
128:
129: $resultArray = array();
130:
131: if(!$result = self::getConnection()->query($sqlQuery)) {
132: throw new CustomQueryException('Failed to run the custom query, MySql error is ['.self::getConnection()->error.'], query ['.$sqlQuery.']');
133: return array();
134: }else{
135: while($row = $result->fetch_array(MYSQLI_ASSOC)) {
136: array_push($resultArray, $row);
137: }
138:
139: return $resultArray;
140: }
141: }
142:
143: 144: 145: 146:
147: public function load($OID) {
148: self::$logger->debug('>>load(OID=['.print_r($OID, true).'])');
149:
150: global $config;
151:
152: $attributes = $this->BO->getPersistentAttributes();
153: $fields = '';
154: foreach($attributes as $att)
155: $fields .= $att.',';
156: $fields = mb_substr($fields, 0, -1);
157:
158: $sqlQuery = 'SELECT '.$fields.' FROM '.$this->BO->getTableName().' WHERE OID = ? LIMIT 1;';
159: $this->BO->setLastQuery($sqlQuery);
160: $stmt = self::getConnection()->stmt_init();
161:
162: $row = array();
163:
164: if($stmt->prepare($sqlQuery)) {
165: $stmt->bind_param('i', $OID);
166: $stmt->execute();
167:
168: $result = $this->bindResult($stmt);
169: if(isset($result[0]))
170: $row = $result[0];
171:
172: $stmt->close();
173: }else{
174: self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.'], OID is ['.print_r($OID, true).'], MySql error is ['.self::getConnection()->error.']');
175: if(!$this->BO->checkTableExists()) {
176: $this->BO->makeTable();
177:
178: throw new BONotFoundException('Failed to load object of OID ['.$OID.'], table ['.$this->BO->getTableName().'] did not exist so had to create!');
179: }
180:
181: return;
182: }
183:
184: if(!isset($row['OID']) || $row['OID'] < 1) {
185: throw new BONotFoundException('Failed to load object of OID ['.$OID.'] not found in database.');
186: self::$logger->debug('<<load');
187: return;
188: }
189:
190:
191: $reflection = new ReflectionClass(get_class($this->BO));
192: $properties = $reflection->getProperties();
193:
194: try {
195: foreach($properties as $propObj) {
196: $propName = $propObj->name;
197:
198:
199: if(!in_array($propName, $this->BO->getTransientAttributes())) {
200: $this->BO->set($propName, $row[$propName]);
201: }elseif(!$propObj->isPrivate() && $this->BO->getPropObject($propName) instanceof Relation) {
202: $prop = $this->BO->getPropObject($propName);
203:
204:
205: if($prop->getRelationType() == 'ONE-TO-MANY') {
206: $this->BO->set($propObj->name, $this->BO->getOID());
207: }
208:
209:
210: if($prop->getRelationType() == 'MANY-TO-ONE') {
211: $this->BO->set($propObj->name, $row[$propName]);
212: }
213: }
214: }
215: }catch (IllegalArguementException $e) {
216: self::$logger->warn('Bad data stored in the table ['.$this->BO->getTableName().'], field ['.$propObj->name.'] bad value['.$row[$propObj->name].'], exception ['.$e->getMessage().']');
217: }catch (PHPException $e) {
218:
219: if($this->BO->checkTableNeedsUpdate()) {
220: $missingFields = $this->BO->findMissingFields();
221:
222: $count = count($missingFields);
223:
224: for($i = 0; $i < $count; $i++)
225: $this->BO->addProperty($missingFields[$i]);
226:
227: throw new BONotFoundException('Failed to load object of OID ['.$OID.'], table ['.$this->BO->getTableName().'] was out of sync with the database so had to be updated!');
228: self::$logger->warn('<<load');
229: return;
230: }
231: }
232:
233: self::$logger->debug('<<load ['.$OID.']');
234: }
235:
236: 237: 238: 239:
240: public function loadByAttribute($attribute, $value, $ignoreClassType=false, $loadAttributes=array()) {
241: self::$logger->debug('>>loadByAttribute(attribute=['.$attribute.'], value=['.$value.'], ignoreClassType=['.$ignoreClassType.'],
242: loadAttributes=['.var_export($loadAttributes, true).'])');
243:
244: if(count($loadAttributes) == 0)
245: $attributes = $this->BO->getPersistentAttributes();
246: else
247: $attributes = $loadAttributes;
248:
249: $fields = '';
250: foreach($attributes as $att)
251: $fields .= $att.',';
252: $fields = mb_substr($fields, 0, -1);
253:
254: if(!$ignoreClassType && $this->BO->isTableOverloaded())
255: $sqlQuery = 'SELECT '.$fields.' FROM '.$this->BO->getTableName().' WHERE '.$attribute.' = ? AND classname = ? LIMIT 1;';
256: else
257: $sqlQuery = 'SELECT '.$fields.' FROM '.$this->BO->getTableName().' WHERE '.$attribute.' = ? LIMIT 1;';
258:
259: self::$logger->debug('Query=['.$sqlQuery.']');
260:
261: $this->BO->setLastQuery($sqlQuery);
262: $stmt = self::getConnection()->stmt_init();
263:
264: $row = array();
265:
266: if($stmt->prepare($sqlQuery)) {
267: if($this->BO->getPropObject($attribute) instanceof Integer) {
268: if(!$ignoreClassType && $this->BO->isTableOverloaded()) {
269: $stmt->bind_param('is', $value, get_class($this->BO));
270: }else{
271: $stmt->bind_param('i', $value);
272: }
273: }else{
274: if(!$ignoreClassType && $this->BO->isTableOverloaded()) {
275: $stmt->bind_param('ss', $value, get_class($this->BO));
276: }else{
277: $stmt->bind_param('s', $value);
278: }
279: }
280:
281: $stmt->execute();
282:
283: $result = $this->bindResult($stmt);
284:
285: if(isset($result[0]))
286: $row = $result[0];
287:
288: $stmt->close();
289: }else{
290: self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
291: if(!$this->BO->checkTableExists()) {
292: $this->BO->makeTable();
293:
294: throw new BONotFoundException('Failed to load object by attribute ['.$attribute.'] and value ['.$value.'], table did not exist so had to create!');
295: }
296: return;
297: }
298:
299: if(!isset($row['OID']) || $row['OID'] < 1) {
300: throw new BONotFoundException('Failed to load object by attribute ['.$attribute.'] and value ['.$value.'], not found in database.');
301: self::$logger->debug('<<loadByAttribute');
302: return;
303: }
304:
305: $this->OID = $row['OID'];
306:
307:
308: $reflection = new ReflectionClass(get_class($this->BO));
309: $properties = $reflection->getProperties();
310:
311: try {
312: foreach($properties as $propObj) {
313: $propName = $propObj->name;
314:
315: if(isset($row[$propName])) {
316:
317: if(!in_array($propName, $this->BO->getTransientAttributes())) {
318: $this->BO->set($propName, $row[$propName]);
319: }elseif(!$propObj->isPrivate() && $this->BO->get($propName) != '' && $this->BO->getPropObject($propName) instanceof Relation) {
320: $prop = $this->BO->getPropObject($propName);
321:
322:
323: if($prop->getRelationType() == 'ONE-TO-MANY') {
324: $this->BO->set($propObj->name, $this->BO->getOID());
325: }
326: }
327: }
328: }
329: }catch (IllegalArguementException $e) {
330: self::$logger->warn('Bad data stored in the table ['.$this->BO->getTableName().'], field ['.$propObj->name.'] bad value['.$row[$propObj->name].'], exception ['.$e->getMessage().']');
331: }catch (PHPException $e) {
332:
333: if($this->BO->checkTableNeedsUpdate()) {
334: $missingFields = $this->BO->findMissingFields();
335:
336: $count = count($missingFields);
337:
338: for($i = 0; $i < $count; $i++)
339: $this->BO->addProperty($missingFields[$i]);
340:
341: throw new BONotFoundException('Failed to load object by attribute ['.$attribute.'] and value ['.$value.'], table ['.$this->BO->getTableName().'] was out of sync with the database so had to be updated!');
342: self::$logger->debug('<<loadByAttribute');
343: return;
344: }
345: }
346:
347: self::$logger->debug('<<loadByAttribute');
348: }
349:
350: 351: 352: 353:
354: public function loadAll($start=0, $limit=0, $orderBy='OID', $order='ASC', $ignoreClassType=false) {
355: self::$logger->debug('>>loadAll(start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
356:
357:
358: try {
359: $field = $this->BO->get($orderBy);
360: }catch(AlphaException $e) {
361: throw new AlphaException('The field name ['.$orderBy.'] provided in the param orderBy does not exist on the class ['.get_class($this->BO).']');
362: }
363:
364: if(!$ignoreClassType && $this->BO->isTableOverloaded()) {
365: if($limit == 0) {
366: $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' WHERE classname=\''.get_class($this->BO).'\' ORDER BY '.$orderBy.' '.$order.';';
367: }else{
368: $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' WHERE classname=\''.get_class($this->BO).'\' ORDER BY '.$orderBy.' '.$order.' LIMIT '.
369: $start.', '.$limit.';';
370: }
371: }else{
372: if($limit == 0)
373: $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' ORDER BY '.$orderBy.' '.$order.';';
374: else
375: $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' ORDER BY '.$orderBy.' '.$order.' LIMIT '.$start.', '.$limit.';';
376: }
377:
378: $this->BO->setLastQuery($sqlQuery);
379:
380: if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
381: throw new BONotFoundException('Failed to load object OIDs, MySql error is ['.AlphaDAOProviderMySQL::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
382: self::$logger->debug('<<loadAll [0]');
383: return array();
384: }
385:
386:
387: $objects = array();
388: $count = 0;
389: $BO_Class = get_class($this->BO);
390:
391: while($row = $result->fetch_array(MYSQLI_ASSOC)) {
392: try {
393: $obj = new $BO_Class();
394: $obj->load($row['OID']);
395: $objects[$count] = $obj;
396: $count++;
397: }catch(ResourceNotAllowedException $e) {
398:
399: }
400: }
401:
402: self::$logger->debug('<<loadAll ['.count($objects).']');
403: return $objects;
404: }
405:
406: 407: 408: 409:
410: public function loadAllByAttribute($attribute, $value, $start=0, $limit=0, $orderBy="OID", $order="ASC", $ignoreClassType=false, $constructorArgs=array()) {
411: self::$logger->debug('>>loadAllByAttribute(attribute=['.$attribute.'], value=['.$value.'], start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.'], constructorArgs=['.print_r($constructorArgs, true).']');
412:
413: if ($limit != 0)
414: $limit = ' LIMIT '.$start.', '.$limit.';';
415: else
416: $limit = ';';
417:
418: if(!$ignoreClassType && $this->BO->isTableOverloaded())
419: $sqlQuery = "SELECT OID FROM ".$this->BO->getTableName()." WHERE $attribute = ? AND classname = ? ORDER BY ".$orderBy." ".$order.$limit;
420: else
421: $sqlQuery = "SELECT OID FROM ".$this->BO->getTableName()." WHERE $attribute = ? ORDER BY ".$orderBy." ".$order.$limit;
422:
423: $this->BO->setLastQuery($sqlQuery);
424: self::$logger->debug($sqlQuery);
425:
426: $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
427:
428: $row = array();
429:
430: if($stmt->prepare($sqlQuery)) {
431: if($this->BO->getPropObject($attribute) instanceof Integer) {
432: if($this->BO->isTableOverloaded()) {
433: $stmt->bind_param('is', $value, get_class($this->BO));
434: }else{
435: $stmt->bind_param('i', $value);
436: }
437: }else{
438: if($this->BO->isTableOverloaded()) {
439: $stmt->bind_param('ss', $value, get_class($this->BO));
440: }else{
441: $stmt->bind_param('s', $value);
442: }
443: }
444:
445: $stmt->execute();
446:
447: $result = $this->bindResult($stmt);
448:
449: $stmt->close();
450: }else{
451: self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
452: if(!$this->BO->checkTableExists()) {
453: $this->BO->makeTable();
454:
455: throw new BONotFoundException('Failed to load objects by attribute ['.$attribute.'] and value ['.$value.'], table did not exist so had to create!');
456: }
457: self::$logger->debug('<<loadAllByAttribute []');
458: return array();
459: }
460:
461:
462: $objects = array();
463: $count = 0;
464: $BO_Class = get_class($this->BO);
465:
466: foreach($result as $row) {
467: try {
468: $argsCount = count($constructorArgs);
469:
470: if($argsCount < 1) {
471: $obj = new $BO_Class();
472: }else{
473: switch ($argsCount) {
474: case 1:
475: $obj = new $BO_Class($constructorArgs[0]);
476: break;
477: case 2:
478: $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1]);
479: break;
480: case 3:
481: $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1],$constructorArgs[2]);
482: break;
483: case 4:
484: $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1],$constructorArgs[2],$constructorArgs[3]);
485: break;
486: case 5:
487: $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1],$constructorArgs[2],$constructorArgs[3],$constructorArgs[4]);
488: break;
489: default:
490: throw new IllegalArguementException('Too many elements in the $constructorArgs array passed to the loadAllByAttribute method!');
491: break;
492: }
493: }
494:
495: $obj->load($row['OID']);
496: $objects[$count] = $obj;
497: $count++;
498: }catch(ResourceNotAllowedException $e) {
499:
500: }
501: }
502:
503: self::$logger->debug('<<loadAllByAttribute ['.count($objects).']');
504: return $objects;
505: }
506:
507: 508: 509: 510:
511: public function loadAllByAttributes($attributes=array(), $values=array(), $start=0, $limit=0, $orderBy='OID', $order='ASC', $ignoreClassType=false, $constructorArgs=array()) {
512: self::$logger->debug('>>loadAllByAttributes(attributes=['.var_export($attributes, true).'], values=['.var_export($values, true).'], start=['.
513: $start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.'], constructorArgs=['.print_r($constructorArgs, true).']');
514:
515: $whereClause = ' WHERE';
516:
517: $count = count($attributes);
518:
519: for($i = 0; $i < $count; $i++) {
520: $whereClause .= ' '.$attributes[$i].' = ? AND';
521: self::$logger->debug($whereClause);
522: }
523:
524: if(!$ignoreClassType && $this->BO->isTableOverloaded())
525: $whereClause .= ' classname = ? AND';
526:
527:
528: $whereClause = mb_substr($whereClause, 0, -4);
529:
530: if ($limit != 0)
531: $limit = ' LIMIT '.$start.', '.$limit.';';
532: else
533: $limit = ';';
534:
535: $sqlQuery = "SELECT OID FROM ".$this->BO->getTableName().$whereClause." ORDER BY ".$orderBy." ".$order.$limit;
536:
537: $this->BO->setLastQuery($sqlQuery);
538:
539: $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
540:
541: if($stmt->prepare($sqlQuery)) {
542:
543: if(count($attributes) > 0 && count($attributes) == count($values)) {
544: $stmt = $this->bindParams($stmt, $attributes, $values);
545: }else{
546:
547: if($this->BO->isTableOverloaded())
548: $stmt->bind_param('s', get_class($this->BO));
549: }
550: $stmt->execute();
551:
552: $result = $this->bindResult($stmt);
553:
554: $stmt->close();
555: }else{
556: self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
557:
558: if(!$this->BO->checkTableExists()) {
559: $this->BO->makeTable();
560:
561: throw new BONotFoundException('Failed to load objects by attributes ['.var_export($attributes, true).'] and values ['.
562: var_export($values, true).'], table did not exist so had to create!');
563: }
564:
565: self::$logger->debug('<<loadAllByAttributes []');
566: return array();
567: }
568:
569:
570: $objects = array();
571: $count = 0;
572: $BO_Class = get_class($this->BO);
573:
574: foreach($result as $row) {
575: try {
576: $argsCount = count($constructorArgs);
577:
578: if($argsCount < 1) {
579: $obj = new $BO_Class();
580: }else{
581: switch ($argsCount) {
582: case 1:
583: $obj = new $BO_Class($constructorArgs[0]);
584: break;
585: case 2:
586: $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1]);
587: break;
588: case 3:
589: $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1],$constructorArgs[2]);
590: break;
591: case 4:
592: $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1],$constructorArgs[2],$constructorArgs[3]);
593: break;
594: case 5:
595: $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1],$constructorArgs[2],$constructorArgs[3],$constructorArgs[4]);
596: break;
597: default:
598: throw new IllegalArguementException('Too many elements in the $constructorArgs array passed to the loadAllByAttribute method!');
599: break;
600: }
601: }
602:
603: $obj->load($row['OID']);
604: $objects[$count] = $obj;
605: $count++;
606: }catch(ResourceNotAllowedException $e) {
607:
608: }
609: }
610:
611: self::$logger->debug('<<loadAllByAttributes ['.count($objects).']');
612: return $objects;
613: }
614:
615: 616: 617: 618:
619: public function loadAllByDayUpdated($date, $start=0, $limit=0, $orderBy="OID", $order="ASC", $ignoreClassType=false) {
620: self::$logger->debug('>>loadAllByDayUpdated(date=['.$date.'], start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
621:
622: if ($start != 0 && $limit != 0)
623: $limit = ' LIMIT '.$start.', '.$limit.';';
624: else
625: $limit = ';';
626:
627: if(!$ignoreClassType && $this->BO->isTableOverloaded())
628: $sqlQuery = "SELECT OID FROM ".$this->BO->getTableName()." WHERE updated_ts >= '".$date." 00:00:00' AND updated_ts <= '".$date." 23:59:59' AND classname='".get_class($this->BO)."' ORDER BY ".$orderBy." ".$order.$limit;
629: else
630: $sqlQuery = "SELECT OID FROM ".$this->BO->getTableName()." WHERE updated_ts >= '".$date." 00:00:00' AND updated_ts <= '".$date." 23:59:59' ORDER BY ".$orderBy." ".$order.$limit;
631:
632: $this->BO->setLastQuery($sqlQuery);
633:
634: if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
635: throw new BONotFoundException('Failed to load object OIDs, MySql error is ['.self::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
636: self::$logger->debug('<<loadAllByDayUpdated []');
637: return array();
638: }
639:
640:
641: $objects = array();
642: $count = 0;
643: $BO_Class = get_class($this->BO);
644:
645: while($row = $result->fetch_array(MYSQLI_ASSOC)) {
646: $obj = new $BO_Class();
647: $obj->load($row['OID']);
648: $objects[$count] = $obj;
649: $count++;
650: }
651:
652: self::$logger->debug('<<loadAllByDayUpdated ['.count($objects).']');
653: return $objects;
654: }
655:
656: 657: 658: 659:
660: public function loadAllFieldValuesByAttribute($attribute, $value, $returnAttribute, $order='ASC', $ignoreClassType=false) {
661: self::$logger->debug('>>loadAllFieldValuesByAttribute(attribute=['.$attribute.'], value=['.$value.'], returnAttribute=['.$returnAttribute.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
662:
663: if(!$ignoreClassType && $this->BO->isTableOverloaded())
664: $sqlQuery = "SELECT ".$returnAttribute." FROM ".$this->BO->getTableName()." WHERE $attribute = '$value' AND classname='".get_class($this->BO)."' ORDER BY OID ".$order.";";
665: else
666: $sqlQuery = "SELECT ".$returnAttribute." FROM ".$this->BO->getTableName()." WHERE $attribute = '$value' ORDER BY OID ".$order.";";
667:
668: $this->BO->setLastQuery($sqlQuery);
669:
670: self::$logger->debug('lastQuery ['.$sqlQuery.']');
671:
672: if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
673: throw new BONotFoundException('Failed to load field ['.$returnAttribute.'] values, MySql error is ['.AlphaDAOProviderMySQL::getConnection()->error.'], query ['.$this->getLastQuery().']');
674: self::$logger->debug('<<loadAllFieldValuesByAttribute []');
675: return array();
676: }
677:
678:
679: $values = array();
680: $count = 0;
681: $BO_Class = get_class($this->BO);
682:
683: while($row = $result->fetch_array(MYSQLI_ASSOC)) {
684: $values[$count] = $row[$returnAttribute];
685: $count++;
686: }
687:
688: self::$logger->debug('<<loadAllFieldValuesByAttribute ['.count($values).']');
689: return $values;
690: }
691:
692: 693: 694: 695:
696: public function save() {
697: self::$logger->debug('>>save()');
698:
699:
700: $reflection = new ReflectionClass(get_class($this->BO));
701: $properties = $reflection->getProperties();
702: $sqlQuery = '';
703: $stmt = null;
704:
705: if($this->BO->getVersion() != $this->BO->getVersionNumber()->getValue()){
706: throw new LockingException('Could not save the object as it has been updated by another user. Please try saving again.');
707: return;
708: }
709:
710:
711: if(isset($_SESSION['currentUser']))
712: $this->BO->set('updated_by', $_SESSION['currentUser']->getOID());
713:
714: $this->BO->set('updated_ts', new Timestamp(date("Y-m-d H:i:s")));
715:
716:
717: if($this->BO->isTransient()) {
718: $savedFieldsCount = 0;
719: $sqlQuery = 'INSERT INTO '.$this->BO->getTableName().' (';
720:
721: foreach($properties as $propObj) {
722: $propName = $propObj->name;
723: if (!in_array($propName, $this->BO->getTransientAttributes())) {
724:
725: if($propName != 'OID' && $propName != 'version_num') {
726: $sqlQuery .= "$propName,";
727: $savedFieldsCount++;
728: }
729:
730: if($propName == 'version_num') {
731: $sqlQuery .= 'version_num,';
732: $savedFieldsCount++;
733: }
734: }
735: }
736: if($this->BO->isTableOverloaded())
737: $sqlQuery .= 'classname,';
738:
739: $sqlQuery = rtrim($sqlQuery, ",");
740:
741: $sqlQuery .= ') VALUES (';
742:
743: for($i = 0; $i < $savedFieldsCount; $i++)
744: $sqlQuery.= '?,';
745:
746: if($this->BO->isTableOverloaded())
747: $sqlQuery.= '?,';
748:
749: $sqlQuery = rtrim($sqlQuery, ',').')';
750:
751: $this->BO->setLastQuery($sqlQuery);
752: self::$logger->debug('Query ['.$sqlQuery.']');
753:
754: $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
755:
756: if($stmt->prepare($sqlQuery)) {
757: $stmt = $this->bindParams($stmt);
758: $stmt->execute();
759: }else{
760: throw new FailedSaveException('Failed to save object, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
761: }
762: }else{
763:
764: $savedFieldsCount = 0;
765: $sqlQuery = 'UPDATE '.$this->BO->getTableName().' SET ';
766:
767: foreach($properties as $propObj) {
768: $propName = $propObj->name;
769: if (!in_array($propName, $this->BO->getTransientAttributes())) {
770:
771: if($propName != 'OID' && $propName != 'version_num') {
772: $sqlQuery .= "$propName = ?,";
773: $savedFieldsCount++;
774: }
775:
776: if($propName == 'version_num') {
777: $sqlQuery .= 'version_num = ?,';
778: $savedFieldsCount++;
779: }
780: }
781: }
782:
783: if($this->BO->isTableOverloaded())
784: $sqlQuery .= 'classname = ?,';
785:
786: $sqlQuery = rtrim($sqlQuery, ",");
787:
788: $sqlQuery .= " WHERE OID=?;";
789:
790: $this->BO->setLastQuery($sqlQuery);
791: $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
792:
793: if($stmt->prepare($sqlQuery)) {
794: $this->bindParams($stmt);
795: $stmt->execute();
796: }else{
797: throw new FailedSaveException('Failed to save object, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
798: }
799: }
800:
801: if ($stmt != null && $stmt->error == '') {
802:
803: if($this->BO->isTransient())
804: $this->BO->setOID(AlphaDAOProviderMySQL::getConnection()->insert_id);
805:
806: try {
807: foreach($properties as $propObj) {
808: $propName = $propObj->name;
809:
810: if($this->BO->getPropObject($propName) instanceof Relation) {
811: $prop = $this->BO->getPropObject($propName);
812:
813:
814: if($prop->getRelationType() == 'MANY-TO-MANY' && isset($_POST[$propName]) && $_POST[$propName] != '00000000000') {
815: try {
816: try{
817:
818: $side = $prop->getSide(get_class($this->BO));
819: }catch (IllegalArguementException $iae) {
820: $side = $prop->getSide(ucfirst($this->BO->getTableName()).'Object');
821: }
822:
823: $lookUp = $prop->getLookup();
824:
825:
826: try {
827: if($side == 'left')
828: $lookUp->deleteAllByAttribute('leftID', $this->BO->getOID());
829: else
830: $lookUp->deleteAllByAttribute('rightID', $this->BO->getOID());
831: }catch (Exception $e) {
832: throw new FailedSaveException('Failed to delete old RelationLookup objects on the table ['.$prop->getLookup()->getTableName().'], error is ['.$e->getMessage().']');
833: }
834:
835: $OIDs = explode(',', $_POST[$propName]);
836:
837: if(isset($OIDs) && !empty($OIDs[0])) {
838:
839: foreach ($OIDs as $oid) {
840: $newLookUp = new RelationLookup($lookUp->get('leftClassName'), $lookUp->get('rightClassName'));
841: if($side == 'left') {
842: $newLookUp->set('leftID', $this->BO->getOID());
843: $newLookUp->set('rightID', $oid);
844: }else{
845: $newLookUp->set('rightID', $this->BO->getOID());
846: $newLookUp->set('leftID', $oid);
847: }
848: $newLookUp->save();
849: }
850: }
851: }catch (Exception $e) {
852: throw new FailedSaveException('Failed to update a MANY-TO-MANY relation on the object, error is ['.$e->getMessage().']');
853: return;
854: }
855: }
856:
857:
858: if($prop->getRelationType() == 'ONE-TO-MANY') {
859: $prop->setValue($this->BO->getOID());
860: }
861: }
862: }
863: }catch (Exception $e) {
864: throw new FailedSaveException('Failed to save object, error is ['.$e->getMessage().']');
865: return;
866: }
867:
868: $stmt->close();
869: }else{
870:
871: $temp = $this->BO->getVersionNumber()->getValue();
872: $this->BO->set('version_num', $temp-1);
873:
874:
875: if(AlphaDAOProviderMySQL::getConnection()->errno == '1062') {
876: throw new ValidationException('Failed to save, the value '.$this->findOffendingValue(AlphaDAOProviderMySQL::getConnection()->error).' is already in use!');
877: return;
878: }else{
879: throw new FailedSaveException('Failed to save object, MySql error is ['.AlphaDAOProviderMySQL::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
880: }
881: }
882:
883: if($this->BO->getMaintainHistory())
884: $this->BO->saveHistory();
885: }
886:
887: 888: 889: 890:
891: public function saveAttribute($attribute, $value) {
892: self::$logger->debug('>>saveAttribute(attribute=['.$attribute.'], value=['.$value.'])');
893:
894:
895: $sqlQuery = 'UPDATE '.$this->BO->getTableName().' SET '.$attribute.'=?, version_num = ? WHERE OID=?;';
896:
897: $this->BO->setLastQuery($sqlQuery);
898: $stmt = self::getConnection()->stmt_init();
899:
900: $newVersionNumber = $this->BO->getVersionNumber()->getValue()+1;
901:
902: if($stmt->prepare($sqlQuery)) {
903: if($this->BO->getPropObject($attribute) instanceof Integer)
904: $bindingsType = 'i';
905: else
906: $bindingsType = 's';
907: $stmt->bind_param($bindingsType.'ii', $value, $newVersionNumber, $this->BO->getOID());
908: self::$logger->debug('Binding params ['.$bindingsType.'i, '.$value.', '.$this->BO->getOID().']');
909: $stmt->execute();
910: }else{
911: throw new FailedSaveException('Failed to save attribute, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
912: }
913:
914: $stmt->close();
915:
916: $this->BO->set($attribute, $value);
917: $this->BO->set('version_num', $newVersionNumber);
918:
919: if($this->BO->getMaintainHistory())
920: $this->BO->saveHistory();
921:
922: self::$logger->debug('<<saveAttribute');
923: }
924:
925: 926: 927: 928:
929: public function saveHistory() {
930: self::$logger->debug('>>saveHistory()');
931:
932:
933: $reflection = new ReflectionClass(get_class($this->BO));
934: $properties = $reflection->getProperties();
935: $sqlQuery = '';
936: $stmt = null;
937:
938: $savedFieldsCount = 0;
939: $attributeNames = array();
940: $attributeValues = array();
941:
942: $sqlQuery = 'INSERT INTO '.$this->BO->getTableName().'_history (';
943:
944: foreach($properties as $propObj) {
945: $propName = $propObj->name;
946: if (!in_array($propName, $this->BO->getTransientAttributes())) {
947: $sqlQuery .= "$propName,";
948: $attributeNames[] = $propName;
949: $attributeValues[] = $this->BO->get($propName);
950: $savedFieldsCount++;
951: }
952: }
953:
954: if($this->BO->isTableOverloaded())
955: $sqlQuery .= 'classname,';
956:
957: $sqlQuery = rtrim($sqlQuery, ",");
958:
959: $sqlQuery .= ') VALUES (';
960:
961: for($i = 0; $i < $savedFieldsCount; $i++)
962: $sqlQuery.= '?,';
963:
964: if($this->BO->isTableOverloaded())
965: $sqlQuery.= '?,';
966:
967: $sqlQuery = rtrim($sqlQuery, ',').')';
968:
969: $this->BO->setLastQuery($sqlQuery);
970: self::$logger->debug('Query ['.$sqlQuery.']');
971:
972: $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
973:
974: if($stmt->prepare($sqlQuery)) {
975: $stmt = $this->bindParams($stmt, $attributeNames, $attributeValues);
976: $stmt->execute();
977: }else{
978: throw new FailedSaveException('Failed to save object history, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
979: }
980: }
981:
982: 983: 984: 985:
986: public function delete() {
987: self::$logger->debug('>>delete()');
988:
989: $sqlQuery = "DELETE FROM ".$this->BO->getTableName()." WHERE OID = ?;";
990:
991: $this->BO->setLastQuery($sqlQuery);
992:
993: $stmt = self::getConnection()->stmt_init();
994:
995: if($stmt->prepare($sqlQuery)) {
996: $stmt->bind_param('i', $this->BO->getOID());
997: $stmt->execute();
998: self::$logger->debug('Deleted the object ['.$this->BO->getOID().'] of class ['.get_class($this->BO).']');
999: }else{
1000: throw new FailedDeleteException('Failed to delete object ['.$this->BO->getOID().'], error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
1001: }
1002:
1003: $stmt->close();
1004:
1005: self::$logger->debug('<<delete');
1006: }
1007:
1008: 1009: 1010: 1011:
1012: public function getVersion() {
1013: self::$logger->debug('>>getVersion()');
1014:
1015: $sqlQuery = 'SELECT version_num FROM '.$this->BO->getTableName().' WHERE OID = ?;';
1016: $this->BO->setLastQuery($sqlQuery);
1017:
1018: $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
1019:
1020: if($stmt->prepare($sqlQuery)) {
1021: $stmt->bind_param('i', $this->BO->getOID());
1022:
1023: $stmt->execute();
1024:
1025: $result = $this->bindResult($stmt);
1026: if(isset($result[0]))
1027: $row = $result[0];
1028:
1029: $stmt->close();
1030: }else{
1031: self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
1032: if(!$this->BO->checkTableExists()) {
1033: $this->BO->makeTable();
1034:
1035: throw new BONotFoundException('Failed to get the version number, table did not exist so had to create!');
1036: }
1037: return;
1038: }
1039:
1040: if(!isset($row['version_num']) || $row['version_num'] < 1) {
1041: self::$logger->debug('<<getVersion [0]');
1042: return 0;
1043: }else{
1044: $version_num = $row['version_num'];
1045:
1046: self::$logger->debug('<<getVersion ['.$version_num.']');
1047: return $version_num;
1048: }
1049: }
1050:
1051: 1052: 1053: 1054:
1055: public function makeTable() {
1056: self::$logger->debug('>>makeTable()');
1057:
1058: $sqlQuery = "CREATE TABLE ".$this->BO->getTableName()." (OID INT(11) ZEROFILL NOT NULL AUTO_INCREMENT,";
1059:
1060:
1061: $reflection = new ReflectionClass(get_class($this->BO));
1062: $properties = $reflection->getProperties();
1063:
1064: foreach($properties as $propObj) {
1065: $propName = $propObj->name;
1066:
1067: if(!in_array($propName, $this->BO->getTransientAttributes()) && $propName != "OID") {
1068: $propClass = get_class($this->BO->getPropObject($propName));
1069:
1070: switch (mb_strtoupper($propClass)) {
1071: case "INTEGER":
1072:
1073: if($this->BO instanceof RelationLookup && ($propName == 'leftID' || $propName == 'rightID'))
1074: $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize().") ZEROFILL NOT NULL,";
1075: else
1076: $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize()."),";
1077: break;
1078: case "DOUBLE":
1079: $sqlQuery .= "$propName DOUBLE(".$this->BO->getPropObject($propName)->getSize(true)."),";
1080: break;
1081: case "STRING":
1082: $sqlQuery .= "$propName VARCHAR(".$this->BO->getPropObject($propName)->getSize().") CHARACTER SET utf8,";
1083: break;
1084: case "TEXT":
1085: $sqlQuery .= "$propName TEXT CHARACTER SET utf8,";
1086: break;
1087: case "BOOLEAN":
1088: $sqlQuery .= "$propName CHAR(1) DEFAULT '0',";
1089: break;
1090: case "DATE":
1091: $sqlQuery .= "$propName DATE,";
1092: break;
1093: case "TIMESTAMP":
1094: $sqlQuery .= "$propName DATETIME,";
1095: break;
1096: case "ENUM":
1097: $sqlQuery .= "$propName ENUM(";
1098: $enumVals = $this->BO->getPropObject($propName)->getOptions();
1099: foreach($enumVals as $val) {
1100: $sqlQuery .= "'".$val."',";
1101: }
1102: $sqlQuery = rtrim($sqlQuery, ",");
1103: $sqlQuery .= ") CHARACTER SET utf8,";
1104: break;
1105: case "DENUM":
1106: $tmp = new DEnum(get_class($this->BO).'::'.$propName);
1107: $sqlQuery .= "$propName INT(11) ZEROFILL,";
1108: break;
1109: case "RELATION":
1110: $sqlQuery .= "$propName INT(11) ZEROFILL UNSIGNED,";
1111: break;
1112: default:
1113: $sqlQuery .= "";
1114: break;
1115: }
1116: }
1117: }
1118: if($this->BO->isTableOverloaded())
1119: $sqlQuery .= "classname VARCHAR(100),";
1120:
1121: $sqlQuery .= "PRIMARY KEY (OID)) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
1122:
1123: $this->BO->setLastQuery($sqlQuery);
1124:
1125: if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
1126: throw new AlphaException('Failed to create the table ['.$this->BO->getTableName().'] for the class ['.get_class($this->BO).'], database error is ['.AlphaDAOProviderMySQL::getConnection()->error.']');
1127: self::$logger->debug('<<makeTable');
1128: }
1129:
1130:
1131: $this->checkIndexes();
1132:
1133: if($this->BO->getMaintainHistory())
1134: $this->BO->makeHistoryTable();
1135:
1136: self::$logger->debug('<<makeTable');
1137: }
1138:
1139: 1140: 1141: 1142:
1143: public function makeHistoryTable() {
1144: self::$logger->debug('>>makeHistoryTable()');
1145:
1146: $sqlQuery = "CREATE TABLE ".$this->BO->getTableName()."_history (OID INT(11) ZEROFILL NOT NULL,";
1147:
1148:
1149: $reflection = new ReflectionClass(get_class($this->BO));
1150: $properties = $reflection->getProperties();
1151:
1152: foreach($properties as $propObj) {
1153: $propName = $propObj->name;
1154:
1155: if(!in_array($propName, $this->BO->getTransientAttributes()) && $propName != "OID") {
1156: $propClass = get_class($this->BO->getPropObject($propName));
1157:
1158: switch (mb_strtoupper($propClass)) {
1159: case "INTEGER":
1160:
1161: if($this->BO instanceof RelationLookup && ($propName == 'leftID' || $propName == 'rightID'))
1162: $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize().") ZEROFILL NOT NULL,";
1163: else
1164: $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize()."),";
1165: break;
1166: case "DOUBLE":
1167: $sqlQuery .= "$propName DOUBLE(".$this->BO->getPropObject($propName)->getSize(true)."),";
1168: break;
1169: case "STRING":
1170: $sqlQuery .= "$propName VARCHAR(".$this->BO->getPropObject($propName)->getSize()."),";
1171: break;
1172: case "TEXT":
1173: $sqlQuery .= "$propName TEXT,";
1174: break;
1175: case "BOOLEAN":
1176: $sqlQuery .= "$propName CHAR(1) DEFAULT '0',";
1177: break;
1178: case "DATE":
1179: $sqlQuery .= "$propName DATE,";
1180: break;
1181: case "TIMESTAMP":
1182: $sqlQuery .= "$propName DATETIME,";
1183: break;
1184: case "ENUM":
1185: $sqlQuery .= "$propName ENUM(";
1186:
1187: $enumVals = $this->BO->getPropObject($propName)->getOptions();
1188:
1189: foreach($enumVals as $val) {
1190: $sqlQuery .= "'".$val."',";
1191: }
1192:
1193: $sqlQuery = rtrim($sqlQuery, ",");
1194: $sqlQuery .= "),";
1195: break;
1196: case "DENUM":
1197: $tmp = new DEnum(get_class($this->BO).'::'.$propName);
1198: $sqlQuery .= "$propName INT(11) ZEROFILL,";
1199: break;
1200: case "RELATION":
1201: $sqlQuery .= "$propName INT(11) ZEROFILL UNSIGNED,";
1202: break;
1203: default:
1204: $sqlQuery .= "";
1205: break;
1206: }
1207: }
1208: }
1209:
1210: if($this->BO->isTableOverloaded())
1211: $sqlQuery .= "classname VARCHAR(100),";
1212:
1213: $sqlQuery .= "PRIMARY KEY (OID, version_num)) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
1214:
1215: $this->BO->setLastQuery($sqlQuery);
1216:
1217: if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
1218: throw new AlphaException('Failed to create the table ['.$this->BO->getTableName().'_history] for the class ['.get_class($this->BO).'], database error is ['.AlphaDAOProviderMySQL::getConnection()->error.']');
1219: self::$logger->debug('<<makeHistoryTable');
1220: }
1221:
1222: self::$logger->debug('<<makeHistoryTable');
1223: }
1224:
1225: 1226: 1227: 1228:
1229: public function rebuildTable() {
1230: self::$logger->debug('>>rebuildTable()');
1231:
1232: $sqlQuery = 'DROP TABLE IF EXISTS '.$this->BO->getTableName().';';
1233:
1234: $this->BO->setLastQuery($sqlQuery);
1235:
1236: if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
1237: throw new AlphaException('Failed to drop the table ['.$this->BO->getTableName().'] for the class ['.get_class($this->BO).'], database error is ['.AlphaDAOProviderMySQL::getConnection()->error.']');
1238: self::$logger->debug('<<rebuildTable');
1239: }
1240:
1241: $this->BO->makeTable();
1242:
1243: self::$logger->debug('<<rebuildTable');
1244: }
1245:
1246: 1247: 1248: 1249:
1250: public function dropTable($tableName=null) {
1251: self::$logger->debug('>>dropTable()');
1252:
1253: if($tableName == null)
1254: $tableName = $this->BO->getTableName();
1255:
1256: $sqlQuery = 'DROP TABLE IF EXISTS '.$tableName.';';
1257:
1258: $this->BO->setLastQuery($sqlQuery);
1259:
1260: if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
1261: throw new AlphaException('Failed to drop the table ['.$tableName.'] for the class ['.get_class($this->BO).'], query is ['.$this->BO->getLastQuery().']');
1262: self::$logger->debug('<<dropTable');
1263: }
1264:
1265: self::$logger->debug('<<dropTable');
1266: }
1267:
1268: 1269: 1270: 1271:
1272: public function addProperty($propName) {
1273: self::$logger->debug('>>addProperty(propName=['.$propName.'])');
1274:
1275: $sqlQuery = 'ALTER TABLE '.$this->BO->getTableName().' ADD ';
1276:
1277: if($this->isTableOverloaded() && $propName == 'classname') {
1278: $sqlQuery .= 'classname VARCHAR(100)';
1279: }else{
1280: if(!in_array($propName, $this->BO->getDefaultAttributes()) && !in_array($propName, $this->BO->getTransientAttributes())) {
1281: $propClass = get_class($this->BO->getPropObject($propName));
1282:
1283: switch (mb_strtoupper($propClass)) {
1284: case 'INTEGER':
1285: $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize().")";
1286: break;
1287: case 'DOUBLE':
1288: $sqlQuery .= "$propName DOUBLE(".$this->BO->getPropObject($propName)->getSize(true).")";
1289: break;
1290: case 'STRING':
1291: $sqlQuery .= "$propName VARCHAR(".$this->BO->getPropObject($propName)->getSize().")";
1292: break;
1293: case 'SEQUENCE':
1294: $sqlQuery .= "$propName VARCHAR(".$this->BO->getPropObject($propName)->getSize().")";
1295: break;
1296: case 'TEXT':
1297: $sqlQuery .= "$propName TEXT";
1298: break;
1299: case 'BOOLEAN':
1300: $sqlQuery .= "$propName CHAR(1) DEFAULT '0'";
1301: break;
1302: case 'DATE':
1303: $sqlQuery .= "$propName DATE";
1304: break;
1305: case 'TIMESTAMP':
1306: $sqlQuery .= "$propName DATETIME";
1307: break;
1308: case 'ENUM':
1309: $sqlQuery .= "$propName ENUM(";
1310: $enumVals = $this->BO->getPropObject($propName)->getOptions();
1311: foreach($enumVals as $val) {
1312: $sqlQuery .= "'".$val."',";
1313: }
1314: $sqlQuery = rtrim($sqlQuery, ",");
1315: $sqlQuery .= ')';
1316: break;
1317: case 'DENUM':
1318: $tmp = new DEnum(get_class($this->BO).'::'.$propName);
1319: $tmp->save();
1320: $sqlQuery .= "$propName INT(11) ZEROFILL";
1321: break;
1322: case 'RELATION':
1323: $sqlQuery .= "$propName INT(11) ZEROFILL UNSIGNED";
1324: break;
1325: default:
1326: $sqlQuery .= '';
1327: break;
1328: }
1329: }
1330: }
1331:
1332: $this->BO->setLastQuery($sqlQuery);
1333:
1334: if(!$result = self::getConnection()->query($sqlQuery)) {
1335: throw new AlphaException('Failed to add the new attribute ['.$propName.'] to the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
1336: self::$logger->debug('<<addProperty');
1337: }else{
1338: self::$logger->info('Successfully added the ['.$propName.'] column onto the ['.$this->BO->getTableName().'] table for the class ['.get_class($this->BO).']');
1339: }
1340:
1341: self::$logger->debug('<<addProperty');
1342: }
1343:
1344: 1345: 1346: 1347:
1348: public function getMAX() {
1349: self::$logger->debug('>>getMAX()');
1350:
1351: $sqlQuery = 'SELECT MAX(OID) AS max_OID FROM '.$this->BO->getTableName();
1352:
1353: $this->BO->setLastQuery($sqlQuery);
1354:
1355: try {
1356: $result = $this->BO->query($sqlQuery);
1357:
1358: $row = $result[0];
1359:
1360: if (isset($row['max_OID'])) {
1361: self::$logger->debug('<<getMAX ['.$row['max_OID'].']');
1362: return $row['max_OID'];
1363: }else{
1364: throw new AlphaException('Failed to get the MAX ID for the class ['.get_class($this->BO).'] from the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
1365: }
1366: }catch (Exception $e) {
1367: throw new AlphaException($e->getMessage());
1368: self::$logger->debug('<<getMAX [0]');
1369: return 0;
1370: }
1371: }
1372:
1373: 1374: 1375: 1376:
1377: public function getCount($attributes=array(), $values=array()) {
1378: self::$logger->debug('>>getCount(attributes=['.var_export($attributes, true).'], values=['.var_export($values, true).'])');
1379:
1380: if($this->BO->isTableOverloaded())
1381: $whereClause = ' WHERE classname = \''.get_class($this->BO).'\' AND';
1382: else
1383: $whereClause = ' WHERE';
1384:
1385: $count = count($attributes);
1386:
1387: for($i = 0; $i < $count; $i++) {
1388: $whereClause .= ' '.$attributes[$i].' = \''.$values[$i].'\' AND';
1389: self::$logger->debug($whereClause);
1390: }
1391:
1392: $whereClause = mb_substr($whereClause, 0, -4);
1393:
1394: if($whereClause != ' WHERE')
1395: $sqlQuery = 'SELECT COUNT(OID) AS class_count FROM '.$this->BO->getTableName().$whereClause;
1396: else
1397: $sqlQuery = 'SELECT COUNT(OID) AS class_count FROM '.$this->BO->getTableName();
1398:
1399: $this->BO->setLastQuery($sqlQuery);
1400:
1401: $result = self::getConnection()->query($sqlQuery);
1402:
1403: if ($result) {
1404: $row = $result->fetch_array(MYSQLI_ASSOC);
1405:
1406: self::$logger->debug('<<getCount ['.$row['class_count'].']');
1407: return $row['class_count'];
1408: }else{
1409: throw new AlphaException('Failed to get the count for the class ['.get_class($this->BO).'] from the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
1410: self::$logger->debug('<<getCount [0]');
1411: return 0;
1412: }
1413: }
1414:
1415: 1416: 1417: 1418:
1419: public function getHistoryCount() {
1420: self::$logger->debug('>>getHistoryCount()');
1421:
1422: if(!$this->BO->getMaintainHistory())
1423: throw new AlphaException('getHistoryCount method called on a DAO where no history is maintained!');
1424:
1425: $sqlQuery = 'SELECT COUNT(OID) AS object_count FROM '.$this->BO->getTableName().'_history WHERE OID='.$this->BO->getOID();
1426:
1427: $this->BO->setLastQuery($sqlQuery);
1428:
1429: $result = self::getConnection()->query($sqlQuery);
1430:
1431: if ($result) {
1432: $row = $result->fetch_array(MYSQLI_ASSOC);
1433:
1434: self::$logger->debug('<<getHistoryCount ['.$row['object_count'].']');
1435: return $row['object_count'];
1436: }else{
1437: throw new AlphaException('Failed to get the history count for the business object ['.$this->BO->getOID().'] from the table ['.$this->BO->getTableName().'_history], query is ['.$this->BO->getLastQuery().']');
1438: self::$logger->debug('<<getHistoryCount [0]');
1439: return 0;
1440: }
1441: }
1442:
1443: 1444: 1445: 1446: 1447:
1448: public function setEnumOptions() {
1449: self::$logger->debug('>>setEnumOptions()');
1450:
1451:
1452: $reflection = new ReflectionClass(get_class($this->BO));
1453: $properties = $reflection->getProperties();
1454:
1455:
1456: $dbError = false;
1457:
1458: foreach($properties as $propObj) {
1459: $propName = $propObj->name;
1460: if(!in_array($propName, $this->BO->getDefaultAttributes()) && !in_array($propName, $this->BO->getTransientAttributes())) {
1461: $propClass = get_class($this->BO->getPropObject($propName));
1462: if ($propClass == 'Enum') {
1463: $sqlQuery = "SHOW COLUMNS FROM ".$this->BO->getTableName()." LIKE '$propName'";
1464:
1465: $this->BO->setLastQuery($sqlQuery);
1466:
1467: $result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery);
1468:
1469: if ($result) {
1470: $row = $result->fetch_array(MYSQLI_NUM);
1471: $options = explode("','",preg_replace("/(enum|set)\('(.+?)'\)/","\\2",$row[1]));
1472:
1473: $this->BO->getPropObject($propName)->setOptions($options);
1474: }else{
1475: $dbError = true;
1476: break;
1477: }
1478: }
1479: }
1480: }
1481:
1482: if (!$dbError) {
1483: if(method_exists($this, 'after_setEnumOptions_callback'))
1484: $this->after_setEnumOptions_callback();
1485: }else{
1486: throw new AlphaException('Failed to load enum options correctly for object instance of class ['.get_class($this).']');
1487: }
1488: self::$logger->debug('<<setEnumOptions');
1489: }
1490:
1491: 1492: 1493: 1494:
1495: public function checkTableExists($checkHistoryTable = false) {
1496: self::$logger->debug('>>checkTableExists(checkHistoryTable=['.$checkHistoryTable.'])');
1497:
1498: global $config;
1499:
1500: $tableExists = false;
1501:
1502: $sqlQuery = 'SHOW TABLES;';
1503: $this->BO->setLastQuery($sqlQuery);
1504:
1505: $result = self::getConnection()->query($sqlQuery);
1506:
1507: if ($result) {
1508: $tableName = ($checkHistoryTable ? $this->BO->getTableName().'_history' : $this->BO->getTableName());
1509:
1510: while ($row = $result->fetch_array(MYSQLI_NUM)) {
1511: if (strtolower($row[0]) == mb_strtolower($tableName))
1512: $tableExists = true;
1513: }
1514:
1515: self::$logger->debug('<<checkTableExists ['.$tableExists.']');
1516: return $tableExists;
1517: }else{
1518: throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
1519: self::$logger->debug('<<checkTableExists [false]');
1520: return false;
1521: }
1522: }
1523:
1524: 1525: 1526: 1527:
1528: public static function checkBOTableExists($BOClassName, $checkHistoryTable = false) {
1529: if(self::$logger == null)
1530: self::$logger = new Logger('AlphaDAOProvidermySQL');
1531: self::$logger->debug('>>checkBOTableExists(BOClassName=['.$BOClassName.'], checkHistoryTable=['.$checkHistoryTable.'])');
1532:
1533: eval('$tableName = '.$BOClassName.'::TABLE_NAME;');
1534:
1535: if(empty($tableName))
1536: $tableName = mb_substr($BOClassName, 0, mb_strpos($BOClassName, '_'));
1537:
1538: if($checkHistoryTable)
1539: $tableName .= '_history';
1540:
1541: $tableExists = false;
1542:
1543: $sqlQuery = 'SHOW TABLES;';
1544:
1545: $result = self::getConnection()->query($sqlQuery);
1546:
1547: while ($row = $result->fetch_array(MYSQLI_NUM)) {
1548: if ($row[0] == $tableName)
1549: $tableExists = true;
1550: }
1551:
1552: if ($result) {
1553: self::$logger->debug('<<checkBOTableExists ['.($tableExists ? 'true' : 'false').']');
1554: return $tableExists;
1555: }else{
1556: throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
1557: self::$logger->debug('<<checkBOTableExists [false]');
1558: return false;
1559: }
1560: }
1561:
1562: 1563: 1564: 1565:
1566: public function checkTableNeedsUpdate() {
1567: self::$logger->debug('>>checkTableNeedsUpdate()');
1568:
1569: $updateRequired = false;
1570:
1571: $matchCount = 0;
1572:
1573: $query = 'SHOW COLUMNS FROM '.$this->BO->getTableName();
1574: $result = self::getConnection()->query($query);
1575: $this->BO->setLastQuery($query);
1576:
1577:
1578: $reflection = new ReflectionClass(get_class($this->BO));
1579: $properties = $reflection->getProperties();
1580:
1581: foreach($properties as $propObj) {
1582: $propName = $propObj->name;
1583: if (!in_array($propName, $this->BO->getTransientAttributes())) {
1584:
1585: $foundMatch = false;
1586:
1587: while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1588: if ($propName == $row['Field']) {
1589: $foundMatch = true;
1590: break;
1591: }
1592: }
1593:
1594: if(!$foundMatch)
1595: $matchCount--;
1596:
1597: $result->data_seek(0);
1598: }
1599: }
1600:
1601:
1602: if($this->BO->isTableOverloaded()) {
1603: $foundMatch = false;
1604:
1605: while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1606: if ('classname' == $row['Field']) {
1607: $foundMatch = true;
1608: break;
1609: }
1610: }
1611: if(!$foundMatch)
1612: $matchCount--;
1613: }
1614:
1615: if ($matchCount != 0)
1616: $updateRequired = true;
1617:
1618: if ($result) {
1619:
1620: try {
1621: $this->checkIndexes();
1622: }catch (AlphaException $ae) {
1623: self::$logger->warn("Error while checking database indexes:\n\n".$ae->getMessage());
1624: }
1625:
1626: self::$logger->debug('<<checkTableNeedsUpdate ['.$updateRequired.']');
1627: return $updateRequired;
1628: }else{
1629: throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
1630: self::$logger->debug('<<checkTableNeedsUpdate [false]');
1631: return false;
1632: }
1633: }
1634:
1635: 1636: 1637: 1638:
1639: public function findMissingFields() {
1640: self::$logger->debug('>>findMissingFields()');
1641:
1642: $missingFields = array();
1643: $matchCount = 0;
1644:
1645: $sqlQuery = 'SHOW COLUMNS FROM '.$this->BO->getTableName();
1646:
1647: $result = self::getConnection()->query($sqlQuery);
1648:
1649: $this->BO->setLastQuery($sqlQuery);
1650:
1651:
1652: $reflection = new ReflectionClass(get_class($this->BO));
1653: $properties = $reflection->getProperties();
1654:
1655: foreach($properties as $propObj) {
1656: $propName = $propObj->name;
1657: if (!in_array($propName, $this->BO->getTransientAttributes())) {
1658: while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1659: if ($propName == $row['Field']) {
1660: $matchCount++;
1661: break;
1662: }
1663: }
1664: $result->data_seek(0);
1665: }else{
1666: $matchCount++;
1667: }
1668:
1669: if($matchCount==0) {
1670: array_push($missingFields, $propName);
1671: }else{
1672: $matchCount = 0;
1673: }
1674: }
1675:
1676:
1677: if($this->BO->isTableOverloaded()) {
1678: $foundMatch = false;
1679:
1680: while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1681: if ('classname' == $row['Field']) {
1682: $foundMatch = true;
1683: break;
1684: }
1685: }
1686: if(!$foundMatch)
1687: array_push($missingFields, 'classname');
1688: }
1689:
1690: if (!$result) {
1691: throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
1692: }
1693:
1694: self::$logger->debug('<<findMissingFields ['.var_export($missingFields, true).']');
1695: return $missingFields;
1696: }
1697:
1698: 1699: 1700: 1701:
1702: public function getIndexes() {
1703: self::$logger->debug('>>getIndexes()');
1704:
1705: $query = 'SHOW INDEX FROM '.$this->BO->getTableName();
1706:
1707: $result = self::getConnection()->query($query);
1708:
1709: $this->BO->setLastQuery($query);
1710:
1711: $indexNames = array();
1712:
1713: if (!$result) {
1714: throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
1715: }else{
1716: while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1717: array_push($indexNames, $row['Key_name']);
1718: }
1719: }
1720:
1721: self::$logger->debug('<<getIndexes');
1722: return $indexNames;
1723: }
1724:
1725: 1726: 1727: 1728: 1729:
1730: private function checkIndexes() {
1731: self::$logger->debug('>>checkIndexes()');
1732:
1733: $indexNames = $this->getIndexes();
1734:
1735:
1736: foreach($this->BO->getUniqueAttributes() as $prop) {
1737:
1738: if(mb_strpos($prop, '+')) {
1739: $attributes = explode('+', $prop);
1740:
1741: $index_exists = false;
1742: foreach ($indexNames as $index) {
1743: if ($attributes[0].'_'.$attributes[1].'_unq_idx' == $index) {
1744: $index_exists = true;
1745: }
1746: if(count($attributes) == 3) {
1747: if ($attributes[0].'_'.$attributes[1].'_'.$attributes[2].'_unq_idx' == $index) {
1748: $index_exists = true;
1749: }
1750: }
1751: }
1752:
1753: if(!$index_exists) {
1754: if(count($attributes) == 3)
1755: $this->BO->createUniqueIndex($attributes[0], $attributes[1], $attributes[2]);
1756: else
1757: $this->BO->createUniqueIndex($attributes[0], $attributes[1]);
1758: }
1759: }else{
1760: $index_exists = false;
1761: foreach ($indexNames as $index) {
1762: if ($prop.'_unq_idx' == $index) {
1763: $index_exists = true;
1764: }
1765: }
1766:
1767: if(!$index_exists)
1768: $this->createUniqueIndex($prop);
1769: }
1770: }
1771:
1772:
1773:
1774: $reflection = new ReflectionClass(get_class($this->BO));
1775: $properties = $reflection->getProperties();
1776:
1777: foreach($properties as $propObj) {
1778: $propName = $propObj->name;
1779: $prop = $this->BO->getPropObject($propName);
1780: if($prop instanceof Relation) {
1781:
1782: if($prop->getRelationType() == 'MANY-TO-ONE') {
1783: $indexExists = false;
1784: foreach ($indexNames as $index) {
1785: if ($propName.'_fk_idx' == $index) {
1786: $indexExists = true;
1787: }
1788: }
1789:
1790: if(!$indexExists) {
1791: $this->createForeignIndex($propName, $prop->getRelatedClass(), $prop->getRelatedClassField());
1792: }
1793: }
1794:
1795: if($prop->getRelationType() == 'MANY-TO-MANY') {
1796: $lookup = $prop->getLookup();
1797:
1798: if($lookup != null) {
1799: try {
1800: $lookupIndexNames = $lookup->getIndexes();
1801:
1802:
1803: $indexExists = false;
1804: foreach ($lookupIndexNames as $index) {
1805: if ('leftID_fk_idx' == $index) {
1806: $indexExists = true;
1807: }
1808: }
1809:
1810: if(!$indexExists) {
1811: $lookup->createForeignIndex('leftID', $prop->getRelatedClass('left'), 'OID');
1812: }
1813:
1814:
1815: $indexExists = false;
1816: foreach ($lookupIndexNames as $index) {
1817: if ('rightID_fk_idx' == $index) {
1818: $indexExists = true;
1819: }
1820: }
1821:
1822: if(!$indexExists) {
1823: $lookup->createForeignIndex('rightID', $prop->getRelatedClass('right'), 'OID');
1824: }
1825: }catch(AlphaException $e) {
1826: self::$logger->error($e->getMessage());
1827: }
1828: }
1829: }
1830:
1831: }
1832: }
1833:
1834: self::$logger->debug('<<checkIndexes');
1835: }
1836:
1837: 1838: 1839: 1840:
1841: public function createForeignIndex($attributeName, $relatedClass, $relatedClassAttribute) {
1842: self::$logger->debug('>>createForeignIndex(attributeName=['.$attributeName.'], relatedClass=['.$relatedClass.'], relatedClassAttribute=['.$relatedClassAttribute.']');
1843:
1844: AlphaDAO::loadClassDef($relatedClass);
1845: $relatedBO = new $relatedClass;
1846: $tableName = $relatedBO->getTableName();
1847:
1848: $result = false;
1849:
1850: if(self::checkBOTableExists(ucfirst($tableName).'Object')) {
1851: $sqlQuery = '';
1852:
1853: if($attributeName == 'leftID')
1854: $sqlQuery = 'ALTER TABLE '.$this->BO->getTableName().' ADD INDEX leftID_fk_idx (leftID);';
1855: if($attributeName == 'rightID')
1856: $sqlQuery = 'ALTER TABLE '.$this->BO->getTableName().' ADD INDEX rightID_fk_idx (rightID);';
1857:
1858: if(!empty($sqlQuery)) {
1859: $this->BO->setLastQuery($sqlQuery);
1860:
1861: $result = self::getConnection()->query($sqlQuery);
1862:
1863: if (!$result) {
1864: throw new FailedIndexCreateException('Failed to create an index on ['.$this->BO->getTableName().'], error is ['.self::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
1865: }
1866: }
1867:
1868: $sqlQuery = 'ALTER TABLE '.$this->BO->getTableName().' ADD FOREIGN KEY '.$attributeName.'_fk_idx ('.$attributeName.') REFERENCES '.$tableName.' ('.$relatedClassAttribute.') ON DELETE SET NULL;';
1869:
1870: $this->BO->setLastQuery($sqlQuery);
1871: $result = self::getConnection()->query($sqlQuery);
1872: }
1873:
1874: if ($result) {
1875: self::$logger->debug('Successfully created the foreign key index ['.$attributeName.'_fk_idx]');
1876: }else{
1877: throw new FailedIndexCreateException('Failed to create the index ['.$attributeName.'_fk_idx] on ['.$this->BO->getTableName().'], error is ['.self::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
1878: }
1879:
1880: self::$logger->debug('<<createForeignIndex');
1881: }
1882:
1883: 1884: 1885: 1886:
1887: public function createUniqueIndex($attribute1Name, $attribute2Name = '', $attribute3Name = '') {
1888: self::$logger->debug('>>createUniqueIndex(attribute1Name=['.$attribute1Name.'], attribute2Name=['.$attribute2Name.'], attribute3Name=['.$attribute3Name.'])');
1889:
1890: if($attribute2Name != '' && $attribute3Name != '')
1891: $sqlQuery = 'CREATE UNIQUE INDEX '.$attribute1Name.'_'.$attribute2Name.'_'.$attribute3Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.','.$attribute2Name.','.$attribute3Name.');';
1892:
1893: if($attribute2Name != '' && $attribute3Name == '')
1894: $sqlQuery = 'CREATE UNIQUE INDEX '.$attribute1Name.'_'.$attribute2Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.','.$attribute2Name.');';
1895:
1896: if($attribute2Name == '' && $attribute3Name == '')
1897: $sqlQuery = 'CREATE UNIQUE INDEX '.$attribute1Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.');';
1898:
1899: $this->BO->setLastQuery($sqlQuery);
1900:
1901: $result = self::getConnection()->query($sqlQuery);
1902:
1903: if ($result) {
1904: self::$logger->debug('Successfully created the unique index on ['.$this->BO->getTableName().']');
1905: }else{
1906: throw new FailedIndexCreateException('Failed to create the unique index on ['.$this->BO->getTableName().'], error is ['.self::getConnection()->error.']');
1907: }
1908:
1909: self::$logger->debug('<<createUniqueIndex');
1910: }
1911:
1912: 1913: 1914: 1915:
1916: public function reload() {
1917: self::$logger->debug('>>reload()');
1918:
1919: if(!$this->isTransient()) {
1920: $this->load($this->getOID());
1921: }else{
1922: throw new AlphaException('Cannot reload transient object from database!');
1923: }
1924: self::$logger->debug('<<reload');
1925: }
1926:
1927: 1928: 1929: 1930:
1931: public function checkRecordExists($OID) {
1932: self::$logger->debug('>>checkRecordExists(OID=['.$OID.'])');
1933:
1934: $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' WHERE OID = ?;';
1935:
1936: $this->BO->setLastQuery($sqlQuery);
1937:
1938: $stmt = self::getConnection()->stmt_init();
1939:
1940: if($stmt->prepare($sqlQuery)) {
1941: $stmt->bind_param('i', $OID);
1942:
1943: $stmt->execute();
1944:
1945: $result = $this->bindResult($stmt);
1946:
1947: $stmt->close();
1948:
1949: if ($result) {
1950: if(count($result) > 0) {
1951: self::$logger->debug('<<checkRecordExists [true]');
1952: return true;
1953: }else{
1954: self::$logger->debug('<<checkRecordExists [false]');
1955: return false;
1956: }
1957: }else{
1958: throw new AlphaException('Failed to check for the record ['.$OID.'] on the class ['.get_class($this->BO).'] from the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
1959: self::$logger->debug('<<checkRecordExists [false]');
1960: return false;
1961: }
1962: }else{
1963: throw new AlphaException('Failed to check for the record ['.$OID.'] on the class ['.get_class($this->BO).'] from the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
1964: self::$logger->debug('<<checkRecordExists [false]');
1965: return false;
1966: }
1967: }
1968:
1969: 1970: 1971: 1972:
1973: public function isTableOverloaded() {
1974: self::$logger->debug('>>isTableOverloaded()');
1975:
1976: $classname = get_class($this->BO);
1977: $tablename = ucfirst($this->BO->getTableName()).'Object';
1978:
1979:
1980: $reflection = new ReflectionClass($classname);
1981: $implementedInterfaces = $reflection->getInterfaces();
1982:
1983: foreach ($implementedInterfaces as $interface) {
1984: if ($interface->name == 'AlphaTypeInterface') {
1985: self::$logger->debug('<<isTableOverloaded [false]');
1986: return false;
1987: }
1988: }
1989:
1990: if($classname != $tablename) {
1991:
1992:
1993: $BOclasses = AlphaDAO::getBOClassNames();
1994:
1995: foreach($BOclasses as $BOclassName) {
1996: if($tablename == $BOclassName) {
1997: self::$logger->debug('<<isTableOverloaded [true]');
1998: return true;
1999: }
2000: }
2001: throw new BadBOTableNameException('The table name ['.$tablename.'] for the class ['.$classname.'] is invalid as it does not match a BO definition in the system!');
2002: self::$logger->debug('<<isTableOverloaded [false]');
2003: return false;
2004: }else{
2005:
2006:
2007: $query = 'SHOW COLUMNS FROM '.$this->BO->getTableName();
2008:
2009: $result = self::getConnection()->query($query);
2010:
2011: if($result) {
2012: while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
2013: if ('classname' == $row['Field']) {
2014: self::$logger->debug('<<isTableOverloaded [true]');
2015: return true;
2016: }
2017: }
2018: }else{
2019: self::$logger->warn('Error during show columns ['.self::getConnection()->error.']');
2020: }
2021:
2022: self::$logger->debug('<<isTableOverloaded [false]');
2023: return false;
2024: }
2025: }
2026:
2027: 2028: 2029: 2030:
2031: public static function begin() {
2032: if(self::$logger == null)
2033: self::$logger = new Logger('AlphaDAOProviderMySQL');
2034: self::$logger->debug('>>begin()');
2035:
2036: if (!self::getConnection()->autocommit(false))
2037: throw new AlphaException('Error beginning a new transaction, error is ['.self::getConnection()->error.']');
2038:
2039: self::$logger->debug('<<begin');
2040: }
2041:
2042: 2043: 2044: 2045:
2046: public static function commit() {
2047: if(self::$logger == null)
2048: self::$logger = new Logger('AlphaDAOProviderMySQL');
2049: self::$logger->debug('>>commit()');
2050:
2051: if (!self::getConnection()->commit())
2052: throw new FailedSaveException('Error commiting a transaction, error is ['.self::getConnection()->error.']');
2053:
2054: self::$logger->debug('<<commit');
2055: }
2056:
2057: 2058: 2059: 2060:
2061: public static function rollback() {
2062: if(self::$logger == null)
2063: self::$logger = new Logger('AlphaDAOProviderMySQL');
2064: self::$logger->debug('>>rollback()');
2065:
2066: if (!self::getConnection()->rollback())
2067: throw new AlphaException('Error rolling back a transaction, error is ['.self::getConnection()->error.']');
2068:
2069: self::$logger->debug('<<rollback');
2070: }
2071:
2072: 2073: 2074: 2075:
2076: public function setBO($BO) {
2077: $this->BO = $BO;
2078: }
2079:
2080: 2081: 2082: 2083: 2084: 2085: 2086: 2087: 2088: 2089: 2090:
2091: private function bindParams($stmt, $attributes=array(), $values=array()) {
2092: self::$logger->debug('>>bindParams(stmt=['.var_export($stmt, true).'])');
2093:
2094: $bindingsTypes = '';
2095: $params = array();
2096:
2097:
2098: if(count($attributes) > 0 && count($attributes) == count($values)) {
2099:
2100: $count = count($values);
2101:
2102: for($i = 0; $i < $count; $i++) {
2103: if (AlphaValidator::isInteger($values[$i]))
2104: $bindingsTypes .= 'i';
2105: else
2106: $bindingsTypes .= 's';
2107: array_push($params, $values[$i]);
2108: }
2109:
2110: if($this->BO->isTableOverloaded()) {
2111: if(isset($this->classname)) {
2112: $bindingsTypes .= 's';
2113: array_push($params, $this->classname);
2114: }else{
2115: $bindingsTypes .= 's';
2116: array_push($params, get_class($this->BO));
2117: }
2118: }
2119: }else{
2120:
2121:
2122: $reflection = new ReflectionClass(get_class($this->BO));
2123: $properties = $reflection->getProperties();
2124:
2125: foreach($properties as $propObj) {
2126: $propName = $propObj->name;
2127: if (!in_array($propName, $this->BO->getTransientAttributes())) {
2128:
2129: if($propName != 'OID' && $propName != 'version_num') {
2130: if($this->BO->getPropObject($propName) instanceof Integer)
2131: $bindingsTypes .= 'i';
2132: else
2133: $bindingsTypes .= 's';
2134: array_push($params, $this->BO->get($propName));
2135: }
2136:
2137: if($propName == 'version_num') {
2138: $temp = $this->BO->getVersionNumber()->getValue();
2139: $this->BO->set('version_num', $temp+1);
2140: $bindingsTypes .= 'i';
2141: array_push($params, $this->BO->getVersionNumber()->getValue());
2142: }
2143: }
2144: }
2145:
2146: if($this->BO->isTableOverloaded()) {
2147: if(isset($this->classname)) {
2148: $bindingsTypes .= 's';
2149: array_push($params, $this->classname);
2150: }else{
2151: $bindingsTypes .= 's';
2152: array_push($params, get_class($this->BO));
2153: }
2154: }
2155:
2156:
2157: if(!$this->BO->isTransient()) {
2158: $bindingsTypes .= 'i';
2159: array_push($params, $this->BO->getOID());
2160: }
2161: }
2162:
2163: self::$logger->debug('bindingsTypes=['.$bindingsTypes.'], count: ['.mb_strlen($bindingsTypes).']');
2164: self::$logger->debug('params ['.var_export($params, true).']');
2165:
2166: if ($params != null) {
2167: $bind_names[] = $bindingsTypes;
2168:
2169: $count = count($params);
2170:
2171: for ($i = 0; $i < $count; $i++) {
2172: $bind_name = 'bind'.$i;
2173: $$bind_name = $params[$i];
2174: $bind_names[] = &$$bind_name;
2175: }
2176:
2177: call_user_func_array(array($stmt,'bind_param'), $bind_names);
2178: }
2179:
2180: self::$logger->debug('<<bindParams ['.var_export($stmt, true).']');
2181: return $stmt;
2182: }
2183:
2184: 2185: 2186: 2187: 2188: 2189: 2190: 2191:
2192: private function bindResult($stmt) {
2193: $result = array();
2194:
2195: $metadata = $stmt->result_metadata();
2196: $fields = $metadata->fetch_fields();
2197:
2198: while(true) {
2199: $pointers = array();
2200: $row = array();
2201:
2202: $pointers[] = $stmt;
2203: foreach ($fields as $field) {
2204: $fieldname = $field->name;
2205: $pointers[] = &$row[$fieldname];
2206: }
2207:
2208: call_user_func_array('mysqli_stmt_bind_result', $pointers);
2209:
2210: if (!$stmt->fetch())
2211: break;
2212:
2213: $result[] = $row;
2214: }
2215:
2216: $metadata->free();
2217:
2218: return $result;
2219: }
2220:
2221: 2222: 2223: 2224: 2225: 2226:
2227: private function findOffendingValue($error) {
2228: self::$logger->debug('>>findOffendingValue(error=['.$error.'])');
2229:
2230: $singleQuote1 = mb_strpos($error,"'");
2231: $singleQuote2 = mb_strrpos($error,"'");
2232:
2233: $value = mb_substr($error, $singleQuote1, ($singleQuote2-$singleQuote1)+1);
2234: self::$logger->debug('<<findOffendingValue ['.$value.'])');
2235: return $value;
2236: }
2237: }
2238:
2239: ?>