* @version 1.0 */ /** * A namespace for exceptions thrown in the CRUD * @version 1.0 * @author Josh Team * @package Exception */ class CRUDException extends Exception{} /** * Creation, Read, Update, Delete, FindID, Validate Model Objects * * The CRUD class is the backbone to all DataStores. DataStores are generated for you * based on your database. All of your DataStores extend CRUD which handles the core * functionality of each Store. You shall not instantiate the CRUD itself however extend it * and then change whatever functionality you need. For instance, the CRUD only supports * grabbing one row of a table per ID passed into the read method. If you need to grab other * records as per a join, or a subselect you would need to make your own read method in the * corresponding DataStore to do so: * * //will over ride the CURD::read() method * class MyDataStore extends CRUD { * public function read($id=0) { * //Anything custom you need * } * } * * * All creation and updates are passed through Validation that matches it per Database * Meta information. * * @abstract * @package Model */ abstract class CRUD { /** * Stores an array of all fields to ignore when doing queries. You can extend * per DataStore to add or take away as need, per DataStore: * * class MyDataStore extends CRUD { * $this->ignoreFields = array('propCount','anotherNonSelectableField'); * } * * * Warning: If you decide to add more fields in your implemintation class * YOU MUST ADD propCount or it will start selecing, validating, etc. the propCount * which would cause unforseen errors. * * @var array a protected default only has propCount, can add to via DataStore. */ protected $ignoreFields = array('propCount'); /** * Stores the return type of the read method. If not defined in the controller * then it will return php types (Arrays/Strings), otherwise it looks for * XML or Soap. * * @var string The return type you expect. PHP by default (xml/soap) */ public $returnType = 'php'; /** * Will return an entire record from the DataStore corresponding DB table, it * pulls all values that are stored in the DataStore's props array, except for fields * that are stored in the ignoreFields array. * * * class MyDataStore extends CRUD { * //Will store an array of client with the id of 5 if exist * $client5 = $this->read(5); * } * * * @todo I need to build logic at some point that allows you to ignore items in the DataStore based off a variable. * * @param mixed $id Can be an array or an ID * @return array - Will return an array, or a multi-dimensional array. */ public function read( $id = null ) { if(is_numeric($id) || $id == null) { $fields = array_keys($this->props); if( is_array( $fields ) ) { foreach($fields as $field) { if(!in_array($field, $this->ignoreFields) && substr($field,0,8) != "link_ID_" ) { $select .= "$field, "; } } if( $id > 0 ) { $whereClause = " WHERE ".$fields[0]." = $id"; } $select = substr($select, 0, -2); $query = "SELECT $select FROM ". substr(get_class( $this ),0,-5) ."$whereClause"; if($id > 0) { try { return $this->dbObj->getRow($query); } catch (Exception $e) { $this->_handleError( $e ); } } else { try { return $this->returnData($this->dbObj->getAll($query)); } catch (Exception $e) { $this->_handleError( $e ); } } } } else if(is_array($id)) { $this->read($id); } else { throw new CRUDException('You must pass in an ID to '.get_class($this).'::read()'); } } /** * Will return the value of ID in the table mapped by the DataStore where the * field you pass in is equal to the value you also passed in. * * * class MyDataStore extends CRUD { * //Will store the ID of the record where the field email has 'myemail@something.com' * $ID = $this->findID('email','myemail@something.com'); * } * * * @param string $field Field Searching * @param string $value Value Searching * @return integer */ public function findID( $field, $value ) { $query = " SELECT ID FROM ". substr(get_class( $this ),0,-5) ." WHERE ". $field ." = '". $value ."' LIMIT 1 "; try { return ((int)$this->dbObj->getField( $query )); } catch (Exception $e) { $this->_handleError($e); } } /** * Takes an array of values and validates the values based on the properties * of the DataStore which holds all meta data then calls the protected method * save which simply loops through the array and puts the value of the index * in the field of the index. * * * $client = array('first_name'=>'Joe','last_name'=>'Bob'); * * //Will create a record in the client table, and set fields accordingly * //Assumes that no other undefined fields are required * $c = new ClientStore(); * $c->create($client); * * * @todo Will need to go back and add logic to match the values to the table properties and such. * * @param array $values */ public function create( $values ) { try { $this->_validate( $values, $this->props ); $this->save('INSERT into',$values); } catch (Exception $e) { $this->_handleError($e); } } /** * Takes an array of values and validates the values based on the properties * of the DataStore which holds all meta data then calls the protected method * save which simply loops through the array and puts the value of the index * in the field of the index where the ID matches the ID passed in. * * * class MyDataStore extends CRUD { * //Gets client1 * $client1 = $this->read(1); * * //Changes first name * $client1['first_name'] = 'Bob'; * * //Save Changes * $this->update($client1); * } * * * @todo Will need to go back and add logic to match the values to the table properties and such. * * @param int $id The ID of the record you want to save * @param array $values */ public function update($id, $values) { try { $this->_validate( $values, $this->props ); $this->save('UPDATE',$values); } catch (Exception $e) { $this->_handleError($e); } } protected function save($type, $values) { /** * Extract the ID and store it elsewhere so that we can't change the * Id of the row that we are changing. I will need to write a method later * that allows you to change the ID. */ if(isset($values['ID'])) { $id = $values['ID']; unset($values['ID']); } $i = 1; //Loops through the values and builds out the SQL foreach($values as $index=>$value) { $v .= $index ." = '$value'"; if(count($values) > $i) { $v .= ", \n"; } $i++; } //If we are updating, add the where clause if($type == 'UPDATE') { $v .= ' WHERE ID = '. $id ; } //Puts the entire query togather. $query = " $type ". substr(get_class( $this ),0,-5) ." SET ".$v." "; try { $this->dbObj->query( $query ); } catch (Exception $e) { $this->_handleError($e); } } /** * Removes the entire record from the table mapped by the DataStore * * For now, it simply deletes the record from the table where the ID is the ID that * you pass in. * * * class MyDataStore extends CRUD { * //Grabs ID of row * $id = $this->findID('email','something@someone.com'); * * //Removes the record * $this->delete($id); * } * * * @todo I need to go back and write logic in this that allows you to flag rather to delete the record or to mark the record as Active=0. I also need to write some logic that will allow you to store more information when a delete happens and also be able to flag rows as not deletable. * @param int $id The ID of the record you want to delete */ public function delete( $id ) { $query = " DELETE FROM ". substr(get_class( $this ),0,-5) ." WHERE ID = ". $id ." LIMIT 1 "; try { $this->dbObj->query( $query ); } catch (Exception $e) { $this->_handleError( $e ); } } private function _validate($values, $properties) { $v = new Validation(); try { $v->validate($values, $properties); } catch (Exception $e) { throw new CRUDException($e->getMessage()); } } private function _handleError(Exception $e) { $l = new Log(); $l->saveException( $e ); throw new CRUDException($e->getMessage()); } /** * Returns the last ID entered */ public function getLastID() { return $this->dbObj->getLastID(); } /** * A factory on what type of data to return */ private function returnData($data) { switch($this->returnType) { case "xml": $class = 'XMLReturn'; break; case "soap": $class = 'SOAPReturn'; break; default: return $data; break; $r = new $class(); return $r->read($data); } } } ?>