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