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) {
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.']');
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: $obj = new $BO_Class();
577: $obj->load($row['OID']);
578: $objects[$count] = $obj;
579: $count++;
580: }catch(ResourceNotAllowedException $e) {
581:
582: }
583: }
584:
585: self::$logger->debug('<<loadAllByAttributes ['.count($objects).']');
586: return $objects;
587: }
588:
589: 590: 591: 592:
593: public function loadAllByDayUpdated($date, $start=0, $limit=0, $orderBy="OID", $order="ASC", $ignoreClassType=false) {
594: self::$logger->debug('>>loadAllByDayUpdated(date=['.$date.'], start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
595:
596: if ($start != 0 && $limit != 0)
597: $limit = ' LIMIT '.$start.', '.$limit.';';
598: else
599: $limit = ';';
600:
601: if(!$ignoreClassType && $this->BO->isTableOverloaded())
602: $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;
603: else
604: $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;
605:
606: $this->BO->setLastQuery($sqlQuery);
607:
608: if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
609: throw new BONotFoundException('Failed to load object OIDs, MySql error is ['.self::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
610: self::$logger->debug('<<loadAllByDayUpdated []');
611: return array();
612: }
613:
614:
615: $objects = array();
616: $count = 0;
617: $BO_Class = get_class($this->BO);
618:
619: while($row = $result->fetch_array(MYSQLI_ASSOC)) {
620: $obj = new $BO_Class();
621: $obj->load($row['OID']);
622: $objects[$count] = $obj;
623: $count++;
624: }
625:
626: self::$logger->debug('<<loadAllByDayUpdated ['.count($objects).']');
627: return $objects;
628: }
629:
630: 631: 632: 633:
634: public function loadAllFieldValuesByAttribute($attribute, $value, $returnAttribute, $order='ASC', $ignoreClassType=false) {
635: self::$logger->debug('>>loadAllFieldValuesByAttribute(attribute=['.$attribute.'], value=['.$value.'], returnAttribute=['.$returnAttribute.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
636:
637: if(!$ignoreClassType && $this->BO->isTableOverloaded())
638: $sqlQuery = "SELECT ".$returnAttribute." FROM ".$this->BO->getTableName()." WHERE $attribute = '$value' AND classname='".get_class($this->BO)."' ORDER BY OID ".$order.";";
639: else
640: $sqlQuery = "SELECT ".$returnAttribute." FROM ".$this->BO->getTableName()." WHERE $attribute = '$value' ORDER BY OID ".$order.";";
641:
642: $this->BO->setLastQuery($sqlQuery);
643:
644: self::$logger->debug('lastQuery ['.$sqlQuery.']');
645:
646: if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
647: throw new BONotFoundException('Failed to load field ['.$returnAttribute.'] values, MySql error is ['.AlphaDAOProviderMySQL::getConnection()->error.'], query ['.$this->getLastQuery().']');
648: self::$logger->debug('<<loadAllFieldValuesByAttribute []');
649: return array();
650: }
651:
652:
653: $values = array();
654: $count = 0;
655: $BO_Class = get_class($this->BO);
656:
657: while($row = $result->fetch_array(MYSQLI_ASSOC)) {
658: $values[$count] = $row[$returnAttribute];
659: $count++;
660: }
661:
662: self::$logger->debug('<<loadAllFieldValuesByAttribute ['.count($values).']');
663: return $values;
664: }
665:
666: 667: 668: 669:
670: public function save() {
671: self::$logger->debug('>>save()');
672:
673:
674: $reflection = new ReflectionClass(get_class($this->BO));
675: $properties = $reflection->getProperties();
676: $sqlQuery = '';
677: $stmt = null;
678:
679: if($this->BO->getVersion() != $this->BO->getVersionNumber()->getValue()){
680: throw new LockingException('Could not save the object as it has been updated by another user. Please try saving again.');
681: return;
682: }
683:
684:
685: if(isset($_SESSION['currentUser']))
686: $this->BO->set('updated_by', $_SESSION['currentUser']->getOID());
687:
688: $this->BO->set('updated_ts', new Timestamp(date("Y-m-d H:i:s")));
689:
690:
691: if($this->BO->isTransient()) {
692: $savedFieldsCount = 0;
693: $sqlQuery = 'INSERT INTO '.$this->BO->getTableName().' (';
694:
695: foreach($properties as $propObj) {
696: $propName = $propObj->name;
697: if (!in_array($propName, $this->BO->getTransientAttributes())) {
698:
699: if($propName != 'OID' && $propName != 'version_num') {
700: $sqlQuery .= "$propName,";
701: $savedFieldsCount++;
702: }
703:
704: if($propName == 'version_num') {
705: $sqlQuery .= 'version_num,';
706: $savedFieldsCount++;
707: }
708: }
709: }
710: if($this->BO->isTableOverloaded())
711: $sqlQuery .= 'classname,';
712:
713: $sqlQuery = rtrim($sqlQuery, ",");
714:
715: $sqlQuery .= ') VALUES (';
716:
717: for($i = 0; $i < $savedFieldsCount; $i++)
718: $sqlQuery.= '?,';
719:
720: if($this->BO->isTableOverloaded())
721: $sqlQuery.= '?,';
722:
723: $sqlQuery = rtrim($sqlQuery, ',').')';
724:
725: $this->BO->setLastQuery($sqlQuery);
726: self::$logger->debug('Query ['.$sqlQuery.']');
727:
728: $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
729:
730: if($stmt->prepare($sqlQuery)) {
731: $stmt = $this->bindParams($stmt);
732: $stmt->execute();
733: }else{
734: throw new FailedSaveException('Failed to save object, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
735: }
736: }else{
737:
738: $savedFieldsCount = 0;
739: $sqlQuery = 'UPDATE '.$this->BO->getTableName().' SET ';
740:
741: foreach($properties as $propObj) {
742: $propName = $propObj->name;
743: if (!in_array($propName, $this->BO->getTransientAttributes())) {
744:
745: if($propName != 'OID' && $propName != 'version_num') {
746: $sqlQuery .= "$propName = ?,";
747: $savedFieldsCount++;
748: }
749:
750: if($propName == 'version_num') {
751: $sqlQuery .= 'version_num = ?,';
752: $savedFieldsCount++;
753: }
754: }
755: }
756:
757: if($this->BO->isTableOverloaded())
758: $sqlQuery .= 'classname = ?,';
759:
760: $sqlQuery = rtrim($sqlQuery, ",");
761:
762: $sqlQuery .= " WHERE OID=?;";
763:
764: $this->BO->setLastQuery($sqlQuery);
765: $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
766:
767: if($stmt->prepare($sqlQuery)) {
768: $this->bindParams($stmt);
769: $stmt->execute();
770: }else{
771: throw new FailedSaveException('Failed to save object, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
772: }
773: }
774:
775: if ($stmt != null && $stmt->error == '') {
776:
777: if($this->BO->isTransient())
778: $this->BO->setOID(AlphaDAOProviderMySQL::getConnection()->insert_id);
779:
780: try {
781: foreach($properties as $propObj) {
782: $propName = $propObj->name;
783:
784: if($this->BO->getPropObject($propName) instanceof Relation) {
785: $prop = $this->BO->getPropObject($propName);
786:
787:
788: if($prop->getRelationType() == 'MANY-TO-MANY') {
789: try {
790: try{
791:
792: $side = $prop->getSide(get_class($this->BO));
793: }catch (IllegalArguementException $iae) {
794: $side = $prop->getSide(ucfirst($this->BO->getTableName()).'Object');
795: }
796:
797: $lookUp = $prop->getLookup();
798:
799:
800: try {
801: if($side == 'left')
802: $lookUp->deleteAllByAttribute('leftID', $this->BO->getOID());
803: else
804: $lookUp->deleteAllByAttribute('rightID', $this->BO->getOID());
805: }catch (Exception $e) {
806: throw new FailedSaveException('Failed to delete old RelationLookup objects on the table ['.$prop->getLookup()->getTableName().'], error is ['.$e->getMessage().']');
807: }
808:
809: if(isset($_POST[$propName]) && $_POST[$propName] != '00000000000')
810: $OIDs = explode(',', $_POST[$propName]);
811:
812: if(isset($OIDs) && !empty($OIDs[0])) {
813:
814: foreach ($OIDs as $oid) {
815: $newLookUp = new RelationLookup($lookUp->get('leftClassName'), $lookUp->get('rightClassName'));
816: if($side == 'left') {
817: $newLookUp->set('leftID', $this->BO->getOID());
818: $newLookUp->set('rightID', $oid);
819: }else{
820: $newLookUp->set('rightID', $this->BO->getOID());
821: $newLookUp->set('leftID', $oid);
822: }
823: $newLookUp->save();
824: }
825: }
826: }catch (Exception $e) {
827: throw new FailedSaveException('Failed to update a MANY-TO-MANY relation on the object, error is ['.$e->getMessage().']');
828: return;
829: }
830: }
831:
832:
833: if($prop->getRelationType() == 'ONE-TO-MANY') {
834: $prop->setValue($this->BO->getOID());
835: }
836: }
837: }
838: }catch (Exception $e) {
839: throw new FailedSaveException('Failed to save object, error is ['.$e->getMessage().']');
840: return;
841: }
842:
843: $stmt->close();
844: }else{
845:
846: $temp = $this->BO->getVersionNumber()->getValue();
847: $this->BO->set('version_num', $temp-1);
848:
849:
850: if(AlphaDAOProviderMySQL::getConnection()->errno == '1062') {
851: throw new ValidationException('Failed to save, the value '.$this->findOffendingValue(AlphaDAOProviderMySQL::getConnection()->error).' is already in use!');
852: return;
853: }else{
854: throw new FailedSaveException('Failed to save object, MySql error is ['.AlphaDAOProviderMySQL::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
855: }
856: }
857:
858: if($this->BO->getMaintainHistory())
859: $this->BO->saveHistory();
860: }
861:
862: 863: 864: 865:
866: public function saveAttribute($attribute, $value) {
867: self::$logger->debug('>>saveAttribute(attribute=['.$attribute.'], value=['.$value.'])');
868:
869:
870: $sqlQuery = 'UPDATE '.$this->BO->getTableName().' SET '.$attribute.'=?, version_num = ? WHERE OID=?;';
871:
872: $this->BO->setLastQuery($sqlQuery);
873: $stmt = self::getConnection()->stmt_init();
874:
875: $newVersionNumber = $this->BO->getVersionNumber()->getValue()+1;
876:
877: if($stmt->prepare($sqlQuery)) {
878: if($this->BO->getPropObject($attribute) instanceof Integer)
879: $bindingsType = 'i';
880: else
881: $bindingsType = 's';
882: $stmt->bind_param($bindingsType.'ii', $value, $newVersionNumber, $this->BO->getOID());
883: self::$logger->debug('Binding params ['.$bindingsType.'i, '.$value.', '.$this->BO->getOID().']');
884: $stmt->execute();
885: }else{
886: throw new FailedSaveException('Failed to save attribute, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
887: }
888:
889: $stmt->close();
890:
891: $this->BO->set($attribute, $value);
892: $this->BO->set('version_num', $newVersionNumber);
893:
894: if($this->BO->getMaintainHistory())
895: $this->BO->saveHistory();
896:
897: self::$logger->debug('<<saveAttribute');
898: }
899:
900: 901: 902: 903:
904: public function saveHistory() {
905: self::$logger->debug('>>saveHistory()');
906:
907:
908: $reflection = new ReflectionClass(get_class($this->BO));
909: $properties = $reflection->getProperties();
910: $sqlQuery = '';
911: $stmt = null;
912:
913: $savedFieldsCount = 0;
914: $attributeNames = array();
915: $attributeValues = array();
916:
917: $sqlQuery = 'INSERT INTO '.$this->BO->getTableName().'_history (';
918:
919: foreach($properties as $propObj) {
920: $propName = $propObj->name;
921: if (!in_array($propName, $this->BO->getTransientAttributes())) {
922: $sqlQuery .= "$propName,";
923: $attributeNames[] = $propName;
924: $attributeValues[] = $this->BO->get($propName);
925: $savedFieldsCount++;
926: }
927: }
928:
929: if($this->BO->isTableOverloaded())
930: $sqlQuery .= 'classname,';
931:
932: $sqlQuery = rtrim($sqlQuery, ",");
933:
934: $sqlQuery .= ') VALUES (';
935:
936: for($i = 0; $i < $savedFieldsCount; $i++)
937: $sqlQuery.= '?,';
938:
939: if($this->BO->isTableOverloaded())
940: $sqlQuery.= '?,';
941:
942: $sqlQuery = rtrim($sqlQuery, ',').')';
943:
944: $this->BO->setLastQuery($sqlQuery);
945: self::$logger->debug('Query ['.$sqlQuery.']');
946:
947: $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
948:
949: if($stmt->prepare($sqlQuery)) {
950: $stmt = $this->bindParams($stmt, $attributeNames, $attributeValues);
951: $stmt->execute();
952: }else{
953: throw new FailedSaveException('Failed to save object history, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
954: }
955: }
956:
957: 958: 959: 960:
961: public function delete() {
962: self::$logger->debug('>>delete()');
963:
964: $sqlQuery = "DELETE FROM ".$this->BO->getTableName()." WHERE OID = ?;";
965:
966: $this->BO->setLastQuery($sqlQuery);
967:
968: $stmt = self::getConnection()->stmt_init();
969:
970: if($stmt->prepare($sqlQuery)) {
971: $stmt->bind_param('i', $this->BO->getOID());
972: $stmt->execute();
973: self::$logger->debug('Deleted the object ['.$this->BO->getOID().'] of class ['.get_class($this->BO).']');
974: }else{
975: throw new FailedDeleteException('Failed to delete object ['.$this->BO->getOID().'], error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
976: }
977:
978: $stmt->close();
979:
980: self::$logger->debug('<<delete');
981: }
982:
983: 984: 985: 986:
987: public function getVersion() {
988: self::$logger->debug('>>getVersion()');
989:
990: $sqlQuery = 'SELECT version_num FROM '.$this->BO->getTableName().' WHERE OID = ?;';
991: $this->BO->setLastQuery($sqlQuery);
992:
993: $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
994:
995: if($stmt->prepare($sqlQuery)) {
996: $stmt->bind_param('i', $this->BO->getOID());
997:
998: $stmt->execute();
999:
1000: $result = $this->bindResult($stmt);
1001: if(isset($result[0]))
1002: $row = $result[0];
1003:
1004: $stmt->close();
1005: }else{
1006: self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
1007: if(!$this->BO->checkTableExists()) {
1008: $this->BO->makeTable();
1009:
1010: throw new BONotFoundException('Failed to get the version number, table did not exist so had to create!');
1011: }
1012: return;
1013: }
1014:
1015: if(!isset($row['version_num']) || $row['version_num'] < 1) {
1016: self::$logger->debug('<<getVersion [0]');
1017: return 0;
1018: }else{
1019: $version_num = $row['version_num'];
1020:
1021: self::$logger->debug('<<getVersion ['.$version_num.']');
1022: return $version_num;
1023: }
1024: }
1025:
1026: 1027: 1028: 1029:
1030: public function makeTable() {
1031: self::$logger->debug('>>makeTable()');
1032:
1033: $sqlQuery = "CREATE TABLE ".$this->BO->getTableName()." (OID INT(11) ZEROFILL NOT NULL AUTO_INCREMENT,";
1034:
1035:
1036: $reflection = new ReflectionClass(get_class($this->BO));
1037: $properties = $reflection->getProperties();
1038:
1039: foreach($properties as $propObj) {
1040: $propName = $propObj->name;
1041:
1042: if(!in_array($propName, $this->BO->getTransientAttributes()) && $propName != "OID") {
1043: $propClass = get_class($this->BO->getPropObject($propName));
1044:
1045: switch (mb_strtoupper($propClass)) {
1046: case "INTEGER":
1047:
1048: if($this->BO instanceof RelationLookup && ($propName == 'leftID' || $propName == 'rightID'))
1049: $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize().") ZEROFILL NOT NULL,";
1050: else
1051: $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize()."),";
1052: break;
1053: case "DOUBLE":
1054: $sqlQuery .= "$propName DOUBLE(".$this->BO->getPropObject($propName)->getSize(true)."),";
1055: break;
1056: case "STRING":
1057: $sqlQuery .= "$propName VARCHAR(".$this->BO->getPropObject($propName)->getSize().") CHARACTER SET utf8,";
1058: break;
1059: case "TEXT":
1060: $sqlQuery .= "$propName TEXT CHARACTER SET utf8,";
1061: break;
1062: case "BOOLEAN":
1063: $sqlQuery .= "$propName CHAR(1) DEFAULT '0',";
1064: break;
1065: case "DATE":
1066: $sqlQuery .= "$propName DATE,";
1067: break;
1068: case "TIMESTAMP":
1069: $sqlQuery .= "$propName DATETIME,";
1070: break;
1071: case "ENUM":
1072: $sqlQuery .= "$propName ENUM(";
1073: $enumVals = $this->BO->getPropObject($propName)->getOptions();
1074: foreach($enumVals as $val) {
1075: $sqlQuery .= "'".$val."',";
1076: }
1077: $sqlQuery = rtrim($sqlQuery, ",");
1078: $sqlQuery .= ") CHARACTER SET utf8,";
1079: break;
1080: case "DENUM":
1081: $tmp = new DEnum(get_class($this->BO).'::'.$propName);
1082: $sqlQuery .= "$propName INT(11) ZEROFILL,";
1083: break;
1084: case "RELATION":
1085: $sqlQuery .= "$propName INT(11) ZEROFILL UNSIGNED,";
1086: break;
1087: default:
1088: $sqlQuery .= "";
1089: break;
1090: }
1091: }
1092: }
1093: if($this->BO->isTableOverloaded())
1094: $sqlQuery .= "classname VARCHAR(100),";
1095:
1096: $sqlQuery .= "PRIMARY KEY (OID)) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
1097:
1098: $this->BO->setLastQuery($sqlQuery);
1099:
1100: if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
1101: throw new AlphaException('Failed to create the table ['.$this->BO->getTableName().'] for the class ['.get_class($this->BO).'], database error is ['.AlphaDAOProviderMySQL::getConnection()->error.']');
1102: self::$logger->debug('<<makeTable');
1103: }
1104:
1105:
1106: $this->checkIndexes();
1107:
1108: if($this->BO->getMaintainHistory())
1109: $this->BO->makeHistoryTable();
1110:
1111: self::$logger->debug('<<makeTable');
1112: }
1113:
1114: 1115: 1116: 1117:
1118: public function makeHistoryTable() {
1119: self::$logger->debug('>>makeHistoryTable()');
1120:
1121: $sqlQuery = "CREATE TABLE ".$this->BO->getTableName()."_history (OID INT(11) ZEROFILL NOT NULL,";
1122:
1123:
1124: $reflection = new ReflectionClass(get_class($this->BO));
1125: $properties = $reflection->getProperties();
1126:
1127: foreach($properties as $propObj) {
1128: $propName = $propObj->name;
1129:
1130: if(!in_array($propName, $this->BO->getTransientAttributes()) && $propName != "OID") {
1131: $propClass = get_class($this->BO->getPropObject($propName));
1132:
1133: switch (mb_strtoupper($propClass)) {
1134: case "INTEGER":
1135:
1136: if($this->BO instanceof RelationLookup && ($propName == 'leftID' || $propName == 'rightID'))
1137: $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize().") ZEROFILL NOT NULL,";
1138: else
1139: $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize()."),";
1140: break;
1141: case "DOUBLE":
1142: $sqlQuery .= "$propName DOUBLE(".$this->BO->getPropObject($propName)->getSize(true)."),";
1143: break;
1144: case "STRING":
1145: $sqlQuery .= "$propName VARCHAR(".$this->BO->getPropObject($propName)->getSize()."),";
1146: break;
1147: case "TEXT":
1148: $sqlQuery .= "$propName TEXT,";
1149: break;
1150: case "BOOLEAN":
1151: $sqlQuery .= "$propName CHAR(1) DEFAULT '0',";
1152: break;
1153: case "DATE":
1154: $sqlQuery .= "$propName DATE,";
1155: break;
1156: case "TIMESTAMP":
1157: $sqlQuery .= "$propName DATETIME,";
1158: break;
1159: case "ENUM":
1160: $sqlQuery .= "$propName ENUM(";
1161:
1162: $enumVals = $this->BO->getPropObject($propName)->getOptions();
1163:
1164: foreach($enumVals as $val) {
1165: $sqlQuery .= "'".$val."',";
1166: }
1167:
1168: $sqlQuery = rtrim($sqlQuery, ",");
1169: $sqlQuery .= "),";
1170: break;
1171: case "DENUM":
1172: $tmp = new DEnum(get_class($this->BO).'::'.$propName);
1173: $sqlQuery .= "$propName INT(11) ZEROFILL,";
1174: break;
1175: case "RELATION":
1176: $sqlQuery .= "$propName INT(11) ZEROFILL UNSIGNED,";
1177: break;
1178: default:
1179: $sqlQuery .= "";
1180: break;
1181: }
1182: }
1183: }
1184:
1185: if($this->BO->isTableOverloaded())
1186: $sqlQuery .= "classname VARCHAR(100),";
1187:
1188: $sqlQuery .= "PRIMARY KEY (OID, version_num)) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
1189:
1190: $this->BO->setLastQuery($sqlQuery);
1191:
1192: if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
1193: 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.']');
1194: self::$logger->debug('<<makeHistoryTable');
1195: }
1196:
1197: self::$logger->debug('<<makeHistoryTable');
1198: }
1199:
1200: 1201: 1202: 1203:
1204: public function rebuildTable() {
1205: self::$logger->debug('>>rebuildTable()');
1206:
1207: $sqlQuery = 'DROP TABLE IF EXISTS '.$this->BO->getTableName().';';
1208:
1209: $this->BO->setLastQuery($sqlQuery);
1210:
1211: if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
1212: throw new AlphaException('Failed to drop the table ['.$this->BO->getTableName().'] for the class ['.get_class($this->BO).'], database error is ['.AlphaDAOProviderMySQL::getConnection()->error.']');
1213: self::$logger->debug('<<rebuildTable');
1214: }
1215:
1216: $this->BO->makeTable();
1217:
1218: self::$logger->debug('<<rebuildTable');
1219: }
1220:
1221: 1222: 1223: 1224:
1225: public function dropTable($tableName=null) {
1226: self::$logger->debug('>>dropTable()');
1227:
1228: if($tableName == null)
1229: $tableName = $this->BO->getTableName();
1230:
1231: $sqlQuery = 'DROP TABLE IF EXISTS '.$tableName.';';
1232:
1233: $this->BO->setLastQuery($sqlQuery);
1234:
1235: if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
1236: throw new AlphaException('Failed to drop the table ['.$tableName.'] for the class ['.get_class($this->BO).'], query is ['.$this->BO->getLastQuery().']');
1237: self::$logger->debug('<<dropTable');
1238: }
1239:
1240: self::$logger->debug('<<dropTable');
1241: }
1242:
1243: 1244: 1245: 1246:
1247: public function addProperty($propName) {
1248: self::$logger->debug('>>addProperty(propName=['.$propName.'])');
1249:
1250: $sqlQuery = 'ALTER TABLE '.$this->BO->getTableName().' ADD ';
1251:
1252: if($this->isTableOverloaded() && $propName == 'classname') {
1253: $sqlQuery .= 'classname VARCHAR(100)';
1254: }else{
1255: if(!in_array($propName, $this->BO->getDefaultAttributes()) && !in_array($propName, $this->BO->getTransientAttributes())) {
1256: $propClass = get_class($this->BO->getPropObject($propName));
1257:
1258: switch (mb_strtoupper($propClass)) {
1259: case 'INTEGER':
1260: $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize().")";
1261: break;
1262: case 'DOUBLE':
1263: $sqlQuery .= "$propName DOUBLE(".$this->BO->getPropObject($propName)->getSize(true).")";
1264: break;
1265: case 'STRING':
1266: $sqlQuery .= "$propName VARCHAR(".$this->BO->getPropObject($propName)->getSize().")";
1267: break;
1268: case 'SEQUENCE':
1269: $sqlQuery .= "$propName VARCHAR(".$this->BO->getPropObject($propName)->getSize().")";
1270: break;
1271: case 'TEXT':
1272: $sqlQuery .= "$propName TEXT";
1273: break;
1274: case 'BOOLEAN':
1275: $sqlQuery .= "$propName CHAR(1) DEFAULT '0'";
1276: break;
1277: case 'DATE':
1278: $sqlQuery .= "$propName DATE";
1279: break;
1280: case 'TIMESTAMP':
1281: $sqlQuery .= "$propName DATETIME";
1282: break;
1283: case 'ENUM':
1284: $sqlQuery .= "$propName ENUM(";
1285: $enumVals = $this->BO->getPropObject($propName)->getOptions();
1286: foreach($enumVals as $val) {
1287: $sqlQuery .= "'".$val."',";
1288: }
1289: $sqlQuery = rtrim($sqlQuery, ",");
1290: $sqlQuery .= ')';
1291: break;
1292: case 'DENUM':
1293: $tmp = new DEnum(get_class($this->BO).'::'.$propName);
1294: $tmp->save();
1295: $sqlQuery .= "$propName INT(11) ZEROFILL";
1296: break;
1297: case 'RELATION':
1298: $sqlQuery .= "$propName INT(11) ZEROFILL UNSIGNED";
1299: break;
1300: default:
1301: $sqlQuery .= '';
1302: break;
1303: }
1304: }
1305: }
1306:
1307: $this->BO->setLastQuery($sqlQuery);
1308:
1309: if(!$result = self::getConnection()->query($sqlQuery)) {
1310: throw new AlphaException('Failed to add the new attribute ['.$propName.'] to the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
1311: self::$logger->debug('<<addProperty');
1312: }else{
1313: self::$logger->info('Successfully added the ['.$propName.'] column onto the ['.$this->BO->getTableName().'] table for the class ['.get_class($this->BO).']');
1314: }
1315:
1316: self::$logger->debug('<<addProperty');
1317: }
1318:
1319: 1320: 1321: 1322:
1323: public function getMAX() {
1324: self::$logger->debug('>>getMAX()');
1325:
1326: $sqlQuery = 'SELECT MAX(OID) AS max_OID FROM '.$this->BO->getTableName();
1327:
1328: $this->BO->setLastQuery($sqlQuery);
1329:
1330: try {
1331: $result = $this->BO->query($sqlQuery);
1332:
1333: $row = $result[0];
1334:
1335: if (isset($row['max_OID'])) {
1336: self::$logger->debug('<<getMAX ['.$row['max_OID'].']');
1337: return $row['max_OID'];
1338: }else{
1339: 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().']');
1340: }
1341: }catch (Exception $e) {
1342: throw new AlphaException($e->getMessage());
1343: self::$logger->debug('<<getMAX [0]');
1344: return 0;
1345: }
1346: }
1347:
1348: 1349: 1350: 1351:
1352: public function getCount($attributes=array(), $values=array()) {
1353: self::$logger->debug('>>getCount(attributes=['.var_export($attributes, true).'], values=['.var_export($values, true).'])');
1354:
1355: if($this->BO->isTableOverloaded())
1356: $whereClause = ' WHERE classname = \''.get_class($this->BO).'\' AND';
1357: else
1358: $whereClause = ' WHERE';
1359:
1360: $count = count($attributes);
1361:
1362: for($i = 0; $i < $count; $i++) {
1363: $whereClause .= ' '.$attributes[$i].' = \''.$values[$i].'\' AND';
1364: self::$logger->debug($whereClause);
1365: }
1366:
1367: $whereClause = mb_substr($whereClause, 0, -4);
1368:
1369: if($whereClause != ' WHERE')
1370: $sqlQuery = 'SELECT COUNT(OID) AS class_count FROM '.$this->BO->getTableName().$whereClause;
1371: else
1372: $sqlQuery = 'SELECT COUNT(OID) AS class_count FROM '.$this->BO->getTableName();
1373:
1374: $this->BO->setLastQuery($sqlQuery);
1375:
1376: $result = self::getConnection()->query($sqlQuery);
1377:
1378: if ($result) {
1379: $row = $result->fetch_array(MYSQLI_ASSOC);
1380:
1381: self::$logger->debug('<<getCount ['.$row['class_count'].']');
1382: return $row['class_count'];
1383: }else{
1384: 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().']');
1385: self::$logger->debug('<<getCount [0]');
1386: return 0;
1387: }
1388: }
1389:
1390: 1391: 1392: 1393:
1394: public function getHistoryCount() {
1395: self::$logger->debug('>>getHistoryCount()');
1396:
1397: if(!$this->BO->getMaintainHistory())
1398: throw new AlphaException('getHistoryCount method called on a DAO where no history is maintained!');
1399:
1400: $sqlQuery = 'SELECT COUNT(OID) AS object_count FROM '.$this->BO->getTableName().'_history WHERE OID='.$this->BO->getOID();
1401:
1402: $this->BO->setLastQuery($sqlQuery);
1403:
1404: $result = self::getConnection()->query($sqlQuery);
1405:
1406: if ($result) {
1407: $row = $result->fetch_array(MYSQLI_ASSOC);
1408:
1409: self::$logger->debug('<<getHistoryCount ['.$row['object_count'].']');
1410: return $row['object_count'];
1411: }else{
1412: 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().']');
1413: self::$logger->debug('<<getHistoryCount [0]');
1414: return 0;
1415: }
1416: }
1417:
1418: 1419: 1420: 1421: 1422:
1423: public function setEnumOptions() {
1424: self::$logger->debug('>>setEnumOptions()');
1425:
1426:
1427: $reflection = new ReflectionClass(get_class($this->BO));
1428: $properties = $reflection->getProperties();
1429:
1430:
1431: $dbError = false;
1432:
1433: foreach($properties as $propObj) {
1434: $propName = $propObj->name;
1435: if(!in_array($propName, $this->BO->getDefaultAttributes()) && !in_array($propName, $this->BO->getTransientAttributes())) {
1436: $propClass = get_class($this->BO->getPropObject($propName));
1437: if ($propClass == 'Enum') {
1438: $sqlQuery = "SHOW COLUMNS FROM ".$this->BO->getTableName()." LIKE '$propName'";
1439:
1440: $this->BO->setLastQuery($sqlQuery);
1441:
1442: $result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery);
1443:
1444: if ($result) {
1445: $row = $result->fetch_array(MYSQLI_NUM);
1446: $options = explode("','",preg_replace("/(enum|set)\('(.+?)'\)/","\\2",$row[1]));
1447:
1448: $this->BO->getPropObject($propName)->setOptions($options);
1449: }else{
1450: $dbError = true;
1451: break;
1452: }
1453: }
1454: }
1455: }
1456:
1457: if (!$dbError) {
1458: if(method_exists($this, 'after_setEnumOptions_callback'))
1459: $this->after_setEnumOptions_callback();
1460: }else{
1461: throw new AlphaException('Failed to load enum options correctly for object instance of class ['.get_class($this).']');
1462: }
1463: self::$logger->debug('<<setEnumOptions');
1464: }
1465:
1466: 1467: 1468: 1469:
1470: public function checkTableExists($checkHistoryTable = false) {
1471: self::$logger->debug('>>checkTableExists(checkHistoryTable=['.$checkHistoryTable.'])');
1472:
1473: global $config;
1474:
1475: $tableExists = false;
1476:
1477: $sqlQuery = 'SHOW TABLES;';
1478: $this->BO->setLastQuery($sqlQuery);
1479:
1480: $result = self::getConnection()->query($sqlQuery);
1481:
1482: if ($result) {
1483: $tableName = ($checkHistoryTable ? $this->BO->getTableName().'_history' : $this->BO->getTableName());
1484:
1485: while ($row = $result->fetch_array(MYSQLI_NUM)) {
1486: if (strtolower($row[0]) == mb_strtolower($tableName))
1487: $tableExists = true;
1488: }
1489:
1490: self::$logger->debug('<<checkTableExists ['.$tableExists.']');
1491: return $tableExists;
1492: }else{
1493: throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
1494: self::$logger->debug('<<checkTableExists [false]');
1495: return false;
1496: }
1497: }
1498:
1499: 1500: 1501: 1502:
1503: public static function checkBOTableExists($BOClassName, $checkHistoryTable = false) {
1504: if(self::$logger == null)
1505: self::$logger = new Logger('AlphaDAOProvidermySQL');
1506: self::$logger->debug('>>checkBOTableExists(BOClassName=['.$BOClassName.'], checkHistoryTable=['.$checkHistoryTable.'])');
1507:
1508: eval('$tableName = '.$BOClassName.'::TABLE_NAME;');
1509:
1510: if(empty($tableName))
1511: $tableName = mb_substr($BOClassName, 0, mb_strpos($BOClassName, '_'));
1512:
1513: if($checkHistoryTable)
1514: $tableName .= '_history';
1515:
1516: $tableExists = false;
1517:
1518: $sqlQuery = 'SHOW TABLES;';
1519:
1520: $result = self::getConnection()->query($sqlQuery);
1521:
1522: while ($row = $result->fetch_array(MYSQLI_NUM)) {
1523: if ($row[0] == $tableName)
1524: $tableExists = true;
1525: }
1526:
1527: if ($result) {
1528: self::$logger->debug('<<checkBOTableExists ['.($tableExists ? 'true' : 'false').']');
1529: return $tableExists;
1530: }else{
1531: throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
1532: self::$logger->debug('<<checkBOTableExists [false]');
1533: return false;
1534: }
1535: }
1536:
1537: 1538: 1539: 1540:
1541: public function checkTableNeedsUpdate() {
1542: self::$logger->debug('>>checkTableNeedsUpdate()');
1543:
1544: $updateRequired = false;
1545:
1546: $matchCount = 0;
1547:
1548: $query = 'SHOW COLUMNS FROM '.$this->BO->getTableName();
1549: $result = self::getConnection()->query($query);
1550: $this->BO->setLastQuery($query);
1551:
1552:
1553: $reflection = new ReflectionClass(get_class($this->BO));
1554: $properties = $reflection->getProperties();
1555:
1556: foreach($properties as $propObj) {
1557: $propName = $propObj->name;
1558: if (!in_array($propName, $this->BO->getTransientAttributes())) {
1559:
1560: $foundMatch = false;
1561:
1562: while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1563: if ($propName == $row['Field']) {
1564: $foundMatch = true;
1565: break;
1566: }
1567: }
1568:
1569: if(!$foundMatch)
1570: $matchCount--;
1571:
1572: $result->data_seek(0);
1573: }
1574: }
1575:
1576:
1577: if($this->BO->isTableOverloaded()) {
1578: $foundMatch = false;
1579:
1580: while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1581: if ('classname' == $row['Field']) {
1582: $foundMatch = true;
1583: break;
1584: }
1585: }
1586: if(!$foundMatch)
1587: $matchCount--;
1588: }
1589:
1590: if ($matchCount != 0)
1591: $updateRequired = true;
1592:
1593: if ($result) {
1594:
1595: try {
1596: $this->checkIndexes();
1597: }catch (AlphaException $ae) {
1598: self::$logger->warn("Error while checking database indexes:\n\n".$ae->getMessage());
1599: }
1600:
1601: self::$logger->debug('<<checkTableNeedsUpdate ['.$updateRequired.']');
1602: return $updateRequired;
1603: }else{
1604: throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
1605: self::$logger->debug('<<checkTableNeedsUpdate [false]');
1606: return false;
1607: }
1608: }
1609:
1610: 1611: 1612: 1613:
1614: public function findMissingFields() {
1615: self::$logger->debug('>>findMissingFields()');
1616:
1617: $missingFields = array();
1618: $matchCount = 0;
1619:
1620: $sqlQuery = 'SHOW COLUMNS FROM '.$this->BO->getTableName();
1621:
1622: $result = self::getConnection()->query($sqlQuery);
1623:
1624: $this->BO->setLastQuery($sqlQuery);
1625:
1626:
1627: $reflection = new ReflectionClass(get_class($this->BO));
1628: $properties = $reflection->getProperties();
1629:
1630: foreach($properties as $propObj) {
1631: $propName = $propObj->name;
1632: if (!in_array($propName, $this->BO->getTransientAttributes())) {
1633: while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1634: if ($propName == $row['Field']) {
1635: $matchCount++;
1636: break;
1637: }
1638: }
1639: $result->data_seek(0);
1640: }else{
1641: $matchCount++;
1642: }
1643:
1644: if($matchCount==0) {
1645: array_push($missingFields, $propName);
1646: }else{
1647: $matchCount = 0;
1648: }
1649: }
1650:
1651:
1652: if($this->BO->isTableOverloaded()) {
1653: $foundMatch = false;
1654:
1655: while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1656: if ('classname' == $row['Field']) {
1657: $foundMatch = true;
1658: break;
1659: }
1660: }
1661: if(!$foundMatch)
1662: array_push($missingFields, 'classname');
1663: }
1664:
1665: if (!$result) {
1666: throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
1667: }
1668:
1669: self::$logger->debug('<<findMissingFields ['.var_export($missingFields, true).']');
1670: return $missingFields;
1671: }
1672:
1673: 1674: 1675: 1676:
1677: public function getIndexes() {
1678: self::$logger->debug('>>getIndexes()');
1679:
1680: $query = 'SHOW INDEX FROM '.$this->BO->getTableName();
1681:
1682: $result = self::getConnection()->query($query);
1683:
1684: $this->BO->setLastQuery($query);
1685:
1686: $indexNames = array();
1687:
1688: if (!$result) {
1689: throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
1690: }else{
1691: while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1692: array_push($indexNames, $row['Key_name']);
1693: }
1694: }
1695:
1696: self::$logger->debug('<<getIndexes');
1697: return $indexNames;
1698: }
1699:
1700: 1701: 1702: 1703: 1704:
1705: private function checkIndexes() {
1706: self::$logger->debug('>>checkIndexes()');
1707:
1708: $indexNames = $this->getIndexes();
1709:
1710:
1711: foreach($this->BO->getUniqueAttributes() as $prop) {
1712:
1713: if(mb_strpos($prop, '+')) {
1714: $attributes = explode('+', $prop);
1715:
1716: $index_exists = false;
1717: foreach ($indexNames as $index) {
1718: if ($attributes[0].'_'.$attributes[1].'_unq_idx' == $index) {
1719: $index_exists = true;
1720: }
1721: if(count($attributes) == 3) {
1722: if ($attributes[0].'_'.$attributes[1].'_'.$attributes[2].'_unq_idx' == $index) {
1723: $index_exists = true;
1724: }
1725: }
1726: }
1727:
1728: if(!$index_exists) {
1729: if(count($attributes) == 3)
1730: $this->BO->createUniqueIndex($attributes[0], $attributes[1], $attributes[2]);
1731: else
1732: $this->BO->createUniqueIndex($attributes[0], $attributes[1]);
1733: }
1734: }else{
1735: $index_exists = false;
1736: foreach ($indexNames as $index) {
1737: if ($prop.'_unq_idx' == $index) {
1738: $index_exists = true;
1739: }
1740: }
1741:
1742: if(!$index_exists)
1743: $this->createUniqueIndex($prop);
1744: }
1745: }
1746:
1747:
1748:
1749: $reflection = new ReflectionClass(get_class($this->BO));
1750: $properties = $reflection->getProperties();
1751:
1752: foreach($properties as $propObj) {
1753: $propName = $propObj->name;
1754: $prop = $this->BO->getPropObject($propName);
1755: if($prop instanceof Relation) {
1756:
1757: if($prop->getRelationType() == 'MANY-TO-ONE') {
1758: $indexExists = false;
1759: foreach ($indexNames as $index) {
1760: if ($propName.'_fk_idx' == $index) {
1761: $indexExists = true;
1762: }
1763: }
1764:
1765: if(!$indexExists) {
1766: $this->createForeignIndex($propName, $prop->getRelatedClass(), $prop->getRelatedClassField());
1767: }
1768: }
1769:
1770: if($prop->getRelationType() == 'MANY-TO-MANY') {
1771: $lookup = $prop->getLookup();
1772:
1773: if($lookup != null) {
1774: try {
1775: $lookupIndexNames = $lookup->getIndexes();
1776:
1777:
1778: $indexExists = false;
1779: foreach ($lookupIndexNames as $index) {
1780: if ('leftID_fk_idx' == $index) {
1781: $indexExists = true;
1782: }
1783: }
1784:
1785: if(!$indexExists) {
1786: $lookup->createForeignIndex('leftID', $prop->getRelatedClass('left'), 'OID');
1787: }
1788:
1789:
1790: $indexExists = false;
1791: foreach ($lookupIndexNames as $index) {
1792: if ('rightID_fk_idx' == $index) {
1793: $indexExists = true;
1794: }
1795: }
1796:
1797: if(!$indexExists) {
1798: $lookup->createForeignIndex('rightID', $prop->getRelatedClass('right'), 'OID');
1799: }
1800: }catch(AlphaException $e) {
1801: self::$logger->error($e->getMessage());
1802: }
1803: }
1804: }
1805:
1806: }
1807: }
1808:
1809: self::$logger->debug('<<checkIndexes');
1810: }
1811:
1812: 1813: 1814: 1815:
1816: public function createForeignIndex($attributeName, $relatedClass, $relatedClassAttribute) {
1817: self::$logger->debug('>>createForeignIndex(attributeName=['.$attributeName.'], relatedClass=['.$relatedClass.'], relatedClassAttribute=['.$relatedClassAttribute.']');
1818:
1819: AlphaDAO::loadClassDef($relatedClass);
1820: $relatedBO = new $relatedClass;
1821: $tableName = $relatedBO->getTableName();
1822:
1823: $result = false;
1824:
1825: if(self::checkBOTableExists(ucfirst($tableName).'Object')) {
1826: $sqlQuery = '';
1827:
1828: if($attributeName == 'leftID')
1829: $sqlQuery = 'ALTER TABLE '.$this->BO->getTableName().' ADD INDEX leftID_fk_idx (leftID);';
1830: if($attributeName == 'rightID')
1831: $sqlQuery = 'ALTER TABLE '.$this->BO->getTableName().' ADD INDEX rightID_fk_idx (rightID);';
1832:
1833: if(!empty($sqlQuery)) {
1834: $this->BO->setLastQuery($sqlQuery);
1835:
1836: $result = self::getConnection()->query($sqlQuery);
1837:
1838: if (!$result) {
1839: throw new FailedIndexCreateException('Failed to create an index on ['.$this->BO->getTableName().'], error is ['.self::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
1840: }
1841: }
1842:
1843: $sqlQuery = 'ALTER TABLE '.$this->BO->getTableName().' ADD FOREIGN KEY '.$attributeName.'_fk_idx ('.$attributeName.') REFERENCES '.$tableName.' ('.$relatedClassAttribute.') ON DELETE SET NULL;';
1844:
1845: $this->BO->setLastQuery($sqlQuery);
1846: $result = self::getConnection()->query($sqlQuery);
1847: }
1848:
1849: if ($result) {
1850: self::$logger->debug('Successfully created the foreign key index ['.$attributeName.'_fk_idx]');
1851: }else{
1852: throw new FailedIndexCreateException('Failed to create the index ['.$attributeName.'_fk_idx] on ['.$this->BO->getTableName().'], error is ['.self::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
1853: }
1854:
1855: self::$logger->debug('<<createForeignIndex');
1856: }
1857:
1858: 1859: 1860: 1861:
1862: public function createUniqueIndex($attribute1Name, $attribute2Name = '', $attribute3Name = '') {
1863: self::$logger->debug('>>createUniqueIndex(attribute1Name=['.$attribute1Name.'], attribute2Name=['.$attribute2Name.'], attribute3Name=['.$attribute3Name.'])');
1864:
1865: if($attribute2Name != '' && $attribute3Name != '')
1866: $sqlQuery = 'CREATE UNIQUE INDEX '.$attribute1Name.'_'.$attribute2Name.'_'.$attribute3Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.','.$attribute2Name.','.$attribute3Name.');';
1867:
1868: if($attribute2Name != '' && $attribute3Name == '')
1869: $sqlQuery = 'CREATE UNIQUE INDEX '.$attribute1Name.'_'.$attribute2Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.','.$attribute2Name.');';
1870:
1871: if($attribute2Name == '' && $attribute3Name == '')
1872: $sqlQuery = 'CREATE UNIQUE INDEX '.$attribute1Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.');';
1873:
1874: $this->BO->setLastQuery($sqlQuery);
1875:
1876: $result = self::getConnection()->query($sqlQuery);
1877:
1878: if ($result) {
1879: self::$logger->debug('Successfully created the unique index on ['.$this->BO->getTableName().']');
1880: }else{
1881: throw new FailedIndexCreateException('Failed to create the unique index on ['.$this->BO->getTableName().'], error is ['.self::getConnection()->error.']');
1882: }
1883:
1884: self::$logger->debug('<<createUniqueIndex');
1885: }
1886:
1887: 1888: 1889: 1890:
1891: public function reload() {
1892: self::$logger->debug('>>reload()');
1893:
1894: if(!$this->isTransient()) {
1895: $this->load($this->getOID());
1896: }else{
1897: throw new AlphaException('Cannot reload transient object from database!');
1898: }
1899: self::$logger->debug('<<reload');
1900: }
1901:
1902: 1903: 1904: 1905:
1906: public function checkRecordExists($OID) {
1907: self::$logger->debug('>>checkRecordExists(OID=['.$OID.'])');
1908:
1909: $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' WHERE OID = ?;';
1910:
1911: $this->BO->setLastQuery($sqlQuery);
1912:
1913: $stmt = self::getConnection()->stmt_init();
1914:
1915: if($stmt->prepare($sqlQuery)) {
1916: $stmt->bind_param('i', $OID);
1917:
1918: $stmt->execute();
1919:
1920: $result = $this->bindResult($stmt);
1921:
1922: $stmt->close();
1923:
1924: if ($result) {
1925: if(count($result) > 0) {
1926: self::$logger->debug('<<checkRecordExists [true]');
1927: return true;
1928: }else{
1929: self::$logger->debug('<<checkRecordExists [false]');
1930: return false;
1931: }
1932: }else{
1933: 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().']');
1934: self::$logger->debug('<<checkRecordExists [false]');
1935: return false;
1936: }
1937: }else{
1938: 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().']');
1939: self::$logger->debug('<<checkRecordExists [false]');
1940: return false;
1941: }
1942: }
1943:
1944: 1945: 1946: 1947:
1948: public function isTableOverloaded() {
1949: self::$logger->debug('>>isTableOverloaded()');
1950:
1951: $classname = get_class($this->BO);
1952: $tablename = ucfirst($this->BO->getTableName()).'Object';
1953:
1954:
1955: $reflection = new ReflectionClass($classname);
1956: $implementedInterfaces = $reflection->getInterfaces();
1957:
1958: foreach ($implementedInterfaces as $interface) {
1959: if ($interface->name == 'AlphaTypeInterface') {
1960: self::$logger->debug('<<isTableOverloaded [false]');
1961: return false;
1962: }
1963: }
1964:
1965: if($classname != $tablename) {
1966:
1967:
1968: $BOclasses = AlphaDAO::getBOClassNames();
1969:
1970: foreach($BOclasses as $BOclassName) {
1971: if($tablename == $BOclassName) {
1972: self::$logger->debug('<<isTableOverloaded [true]');
1973: return true;
1974: }
1975: }
1976: throw new BadBOTableNameException('The table name ['.$tablename.'] for the class ['.$classname.'] is invalid as it does not match a BO definition in the system!');
1977: self::$logger->debug('<<isTableOverloaded [false]');
1978: return false;
1979: }else{
1980:
1981:
1982: $query = 'SHOW COLUMNS FROM '.$this->BO->getTableName();
1983:
1984: $result = self::getConnection()->query($query);
1985:
1986: if($result) {
1987: while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1988: if ('classname' == $row['Field']) {
1989: self::$logger->debug('<<isTableOverloaded [true]');
1990: return true;
1991: }
1992: }
1993: }else{
1994: self::$logger->warn('Error during show columns ['.self::getConnection()->error.']');
1995: }
1996:
1997: self::$logger->debug('<<isTableOverloaded [false]');
1998: return false;
1999: }
2000: }
2001:
2002: 2003: 2004: 2005:
2006: public static function begin() {
2007: if(self::$logger == null)
2008: self::$logger = new Logger('AlphaDAOProviderMySQL');
2009: self::$logger->debug('>>begin()');
2010:
2011: if (!self::getConnection()->autocommit(false))
2012: throw new AlphaException('Error beginning a new transaction, error is ['.self::getConnection()->error.']');
2013:
2014: self::$logger->debug('<<begin');
2015: }
2016:
2017: 2018: 2019: 2020:
2021: public static function commit() {
2022: if(self::$logger == null)
2023: self::$logger = new Logger('AlphaDAOProviderMySQL');
2024: self::$logger->debug('>>commit()');
2025:
2026: if (!self::getConnection()->commit())
2027: throw new FailedSaveException('Error commiting a transaction, error is ['.self::getConnection()->error.']');
2028:
2029: self::$logger->debug('<<commit');
2030: }
2031:
2032: 2033: 2034: 2035:
2036: public static function rollback() {
2037: if(self::$logger == null)
2038: self::$logger = new Logger('AlphaDAOProviderMySQL');
2039: self::$logger->debug('>>rollback()');
2040:
2041: if (!self::getConnection()->rollback())
2042: throw new AlphaException('Error rolling back a transaction, error is ['.self::getConnection()->error.']');
2043:
2044: self::$logger->debug('<<rollback');
2045: }
2046:
2047: 2048: 2049: 2050:
2051: public function setBO($BO) {
2052: $this->BO = $BO;
2053: }
2054:
2055: 2056: 2057: 2058: 2059: 2060: 2061: 2062: 2063: 2064: 2065:
2066: private function bindParams($stmt, $attributes=array(), $values=array()) {
2067: self::$logger->debug('>>bindParams(stmt=['.var_export($stmt, true).'])');
2068:
2069: $bindingsTypes = '';
2070: $params = array();
2071:
2072:
2073: if(count($attributes) > 0 && count($attributes) == count($values)) {
2074:
2075: $count = count($values);
2076:
2077: for($i = 0; $i < $count; $i++) {
2078: if (AlphaValidator::isInteger($values[$i]))
2079: $bindingsTypes .= 'i';
2080: else
2081: $bindingsTypes .= 's';
2082: array_push($params, $values[$i]);
2083: }
2084:
2085: if($this->BO->isTableOverloaded()) {
2086: if(isset($this->classname)) {
2087: $bindingsTypes .= 's';
2088: array_push($params, $this->classname);
2089: }else{
2090: $bindingsTypes .= 's';
2091: array_push($params, get_class($this->BO));
2092: }
2093: }
2094: }else{
2095:
2096:
2097: $reflection = new ReflectionClass(get_class($this->BO));
2098: $properties = $reflection->getProperties();
2099:
2100: foreach($properties as $propObj) {
2101: $propName = $propObj->name;
2102: if (!in_array($propName, $this->BO->getTransientAttributes())) {
2103:
2104: if($propName != 'OID' && $propName != 'version_num') {
2105: if($this->BO->getPropObject($propName) instanceof Integer)
2106: $bindingsTypes .= 'i';
2107: else
2108: $bindingsTypes .= 's';
2109: array_push($params, $this->BO->get($propName));
2110: }
2111:
2112: if($propName == 'version_num') {
2113: $temp = $this->BO->getVersionNumber()->getValue();
2114: $this->BO->set('version_num', $temp+1);
2115: $bindingsTypes .= 'i';
2116: array_push($params, $this->BO->getVersionNumber()->getValue());
2117: }
2118: }
2119: }
2120:
2121: if($this->BO->isTableOverloaded()) {
2122: if(isset($this->classname)) {
2123: $bindingsTypes .= 's';
2124: array_push($params, $this->classname);
2125: }else{
2126: $bindingsTypes .= 's';
2127: array_push($params, get_class($this->BO));
2128: }
2129: }
2130:
2131:
2132: if(!$this->BO->isTransient()) {
2133: $bindingsTypes .= 'i';
2134: array_push($params, $this->BO->getOID());
2135: }
2136: }
2137:
2138: self::$logger->debug('bindingsTypes=['.$bindingsTypes.'], count: ['.mb_strlen($bindingsTypes).']');
2139: self::$logger->debug('params ['.var_export($params, true).']');
2140:
2141: if ($params != null) {
2142: $bind_names[] = $bindingsTypes;
2143:
2144: $count = count($params);
2145:
2146: for ($i = 0; $i < $count; $i++) {
2147: $bind_name = 'bind'.$i;
2148: $$bind_name = $params[$i];
2149: $bind_names[] = &$$bind_name;
2150: }
2151:
2152: call_user_func_array(array($stmt,'bind_param'), $bind_names);
2153: }
2154:
2155: self::$logger->debug('<<bindParams ['.var_export($stmt, true).']');
2156: return $stmt;
2157: }
2158:
2159: 2160: 2161: 2162: 2163: 2164: 2165: 2166:
2167: private function bindResult($stmt) {
2168: $result = array();
2169:
2170: $metadata = $stmt->result_metadata();
2171: $fields = $metadata->fetch_fields();
2172:
2173: while(true) {
2174: $pointers = array();
2175: $row = array();
2176:
2177: $pointers[] = $stmt;
2178: foreach ($fields as $field) {
2179: $fieldname = $field->name;
2180: $pointers[] = &$row[$fieldname];
2181: }
2182:
2183: call_user_func_array('mysqli_stmt_bind_result', $pointers);
2184:
2185: if (!$stmt->fetch())
2186: break;
2187:
2188: $result[] = $row;
2189: }
2190:
2191: $metadata->free();
2192:
2193: return $result;
2194: }
2195:
2196: 2197: 2198: 2199: 2200: 2201:
2202: private function findOffendingValue($error) {
2203: self::$logger->debug('>>findOffendingValue(error=['.$error.'])');
2204:
2205: $singleQuote1 = mb_strpos($error,"'");
2206: $singleQuote2 = mb_strrpos($error,"'");
2207:
2208: $value = mb_substr($error, $singleQuote1, ($singleQuote2-$singleQuote1)+1);
2209: self::$logger->debug('<<findOffendingValue ['.$value.'])');
2210: return $value;
2211: }
2212: }
2213:
2214: ?>