00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 require_once 'creole/CreoleTypes.php';
00027
00028
00029
00030 include_once 'jargon/QueryDataSet.php';
00031
00041 class Record {
00042
00043
00044 const ZOMBIE = -1;
00045 const UNKNOWN = 0;
00046 const INSERT = 1;
00047 const UPDATE = 2;
00048 const DELETE = 3;
00049 const BEFOREINSERT = 4;
00050 const AFTERINSERT = 5;
00051 const BEFOREUPDATE = 6;
00052 const AFTERUPDATE = 7;
00053 const BEFOREDELETE = 8;
00054 const AFTERDELETE = 9;
00055
00057 private $values = array();
00058
00060 private $dirtyCols = array();
00061
00063 private $ds;
00064
00066 private $saveType = 0;
00067
00076 function __construct(DataSet $ds, $addRecord = false)
00077 {
00078 $this->setParentDataSet($ds);
00079 if (!$addRecord) {
00080 $this->createValues($this->ds->resultSet());
00081 }
00082 }
00083
00087 private function initializeRecord()
00088 {
00089 $this->values = array();
00090 $this->dirtyCols = array();
00091 $this->setSaveType(Record::UNKNOWN);
00092 }
00093
00099 private function createValues(ResultSet $rs)
00100 {
00101 $this->values = $rs->getRow();
00102 }
00103
00108 public function delete(Connection $conn = null)
00109 {
00110 $this->setSaveType(DELETE);
00111 $this->save($conn);
00112 }
00113
00120 public function save(Connection $conn = null)
00121 {
00122 $returnValue = false;
00123
00124 if ($this->ds instanceof QueryDataSet) {
00125 throw new DataSetException("You cannot save a QueryDataSet. Please use a TableDataSet instead.");
00126 }
00127
00128 if (!$this->needsToBeSaved()) {
00129 return $returnValue;
00130 }
00131
00132 switch($this->saveType) {
00133 case Record::INSERT:
00134 $returnValue = $this->doInsert($conn);
00135 break;
00136 case Record::UPDATE:
00137 $returnValue = $this->doUpdate($conn);
00138 break;
00139 case Record::DELETE:
00140 $returnValue = $this->doDelete($conn);
00141 break;
00142 default:
00143 throw new DataSetException("Invalid or no-action saveType for Record.");
00144 }
00145
00146 return (boolean) $returnValue;
00147 }
00148
00154 private function doDelete(Connection $conn = null)
00155 {
00156 if ($conn === null) {
00157 $conn = $this->ds->connection();
00158 }
00159
00160 $table = $this->ds->tableInfo();
00161 $stmt = null;
00162 try {
00163 $stmt = $conn->prepareStatement($this->getDeleteSql());
00164 $ps = 1;
00165 $kd = $this->ds->keydef();
00166 for ($i = 1, $kdsize = $kd->size(); $i <= $kdsize; $i++) {
00167 $col = $kd->getAttrib($i);
00168 $val = $this->getValue($col);
00169 $setter = 'set' . CreoleTypes::getAffix( $table->getColumn($col)->getType() );
00170 $stmt->$setter($ps++, $val);
00171 }
00172
00173 $ret = $stmt->executeUpdate();
00174
00175
00176
00177 $this->setSaveType(Record::ZOMBIE);
00178
00179 $stmt->close();
00180
00181 if ($ret > 1) {
00182 throw new SQLException("There were " . $ret . " rows deleted with this records key value.");
00183 }
00184
00185 return $ret;
00186
00187 } catch (SQLException $e) {
00188 if ($stmt) $stmt->close();
00189 throw $e;
00190 }
00191 }
00192
00198 private function doUpdate(Connection $conn = null)
00199 {
00200 if ($conn === null) {
00201 $conn = $this->ds->connection();
00202 }
00203
00204 $table = $this->ds->tableInfo();
00205
00206 $stmt = null;
00207 try {
00208 $stmt = $conn->prepareStatement($this->getUpdateSql());
00209
00210 $ps = 1;
00211 foreach($this->dirtyColumns() as $col) {
00212 $setter = 'set' . CreoleTypes::getAffix( $table->getColumn($col)->getType() );
00213 $stmt->$setter($ps++, $this->getValue($col));
00214 }
00215
00216 $kd = $this->ds->keydef();
00217 for ($i = 1, $kdsize = $kd->size(); $i <= $kdsize; $i++) {
00218 $attrib = $kd->getAttrib($i);
00219 $setter = 'set' . CreoleTypes::getAffix( $table->getColumn($attrib)->getType() );
00220 $stmt->$setter($ps++, $this->getValue($attrib));
00221 }
00222
00223 $ret = $stmt->executeUpdate();
00224
00225 if ($this->ds->refreshOnSave()) {
00226 $this->refresh();
00227 } else {
00228
00229 $this->markRecordClean();
00230 }
00231
00232 $this->setSaveType(Record::AFTERUPDATE);
00233
00234 if ($ret > 1) {
00235 throw new SQLException ("There were " . $ret . " rows updated with this records key value.");
00236 }
00237 return $ret;
00238 } catch (SQLException $e) {
00239 if ($stmt) $stmt->close();
00240 throw $e;
00241 }
00242
00243 }
00244
00250 private function doInsert(Connection $conn = null)
00251 {
00252 $stmt = null;
00253
00254 try {
00255 $stmt = $conn->prepareStatement($this->getInsertSql());
00256 $ps = 1;
00257 foreach($this->dirtyColumns() as $col) {
00258 $val = $this->getValue($col);
00259 $setter = 'set' . CreoleTypes::getAffix( $table->getColumn($col)->getType() );
00260 $stmt->$setter($ps++, $val);
00261 }
00262
00263 $ret = $stmt->executeUpdate();
00264
00265 if ($this->ds->refreshOnSave()) {
00266 $this->refresh();
00267 } else {
00268
00269 $this->markRecordClean();
00270 }
00271
00272 $this->setSaveType(Record::AFTERINSERT);
00273
00274 if ($ret > 1) {
00275
00276 throw new SQLException ("There were " . $ret . " rows inserted with this records key value.");
00277 }
00278
00279 return $ret;
00280 } catch (SQLException $e) {
00281 if ($stmt) $stmt->close();
00282 throw $e;
00283 }
00284 }
00285
00291 private function getUpdateSql()
00292 {
00293 $kd = $this->ds->keydef();
00294 if ($kd === null || $kd->size() === 0) {
00295 throw new DataSetException("You must specify KeyDef attributes for this TableDataSet in order to create a Record for update.");
00296 } elseif ($this->recordIsClean()) {
00297 throw new DataSetException ("You must Record->setValue() on a column before doing an update.");
00298 }
00299
00300 $set_sql = "";
00301 $where_sql = "";
00302
00303 $comma = false;
00304
00305 foreach($this->dirtyColumns() as $col) {
00306 if (!$comma) {
00307 $set_sql .= $col . " = ?";
00308 $comma = true;
00309 } else {
00310 $set_sql .= ", " . $col . " = ?";
00311 }
00312 }
00313
00314 $comma = false;
00315 for ($i = 1, $kdsize = $kd->size(); $i <= $kdsize; $i++) {
00316 $attrib = $kd->getAttrib($i);
00317 if (! $this->valueIsClean ($attrib)) {
00318 throw new DataSetException ("The value for column '" . $attrib . "' is a key value and cannot be updated.");
00319 }
00320 if (!$comma) {
00321 $where_sql .= $attrib . " = ?";
00322 $comma = true;
00323 } else {
00324 $where_sql .= " AND " . $attrib . " = ?";
00325 }
00326 }
00327 return "UPDATE " . $this->ds->tableName() . " SET " . $set_sql . " WHERE " . $where_sql;
00328 }
00329
00330
00336 private function getDeleteSql()
00337 {
00338 $kd = $this->ds->keydef();
00339
00340 if ($kd === null || $kd->size() === 0) {
00341 throw new DataSetException("You must specify KeyDef attributes for this TableDataSet in order to delete a Record.");
00342 }
00343
00344 $where_sql = "";
00345 $comma = false;
00346 for ($i = 1, $kdsize = $kd->size(); $i <= $kdsize; $i++) {
00347 if (!$comma) {
00348 $where_sql .= $kd->getAttrib($i) . " = ?";
00349 $comma = true;
00350 } else {
00351 $where_sql .= " AND " . $kd->getAttrib($i) . " = ?";
00352 }
00353 }
00354
00355 return "DELETE FROM " . $this->ds->tableName() . " WHERE " . $where_sql;
00356 }
00357
00362 private function getInsertSql()
00363 {
00364 $fields_sql = "";
00365 $values_sql = "";
00366 $comma = false;
00367 foreach($this->dirtyColumns() as $col) {
00368 if (!$comma) {
00369 $fields_sql .= $col;
00370 $values_sql .= "?";
00371 $comma = true;
00372 } else {
00373 $fields_sql .= ", " . $col;
00374 $values_sql .= ", ?";
00375 }
00376 }
00377 return "INSERT INTO " . $this->ds->tableName() . " ( " . $fields_sql . " ) VALUES ( " . $values_sql . " )";
00378 }
00379
00386 public function getValue($col)
00387 {
00388 if (!isset($this->values[$col])) {
00389 throw new DataSetException("Undefined column in Record: " . $col);
00390 }
00391 return $this->values[$col];
00392 }
00393
00398 public function columns()
00399 {
00400 return array_keys($this->values);
00401 }
00402
00409 private function dirtyColumns()
00410 {
00411 return array_keys($this->dirtyCols);
00412 }
00413
00418 public function size()
00419 {
00420 return count($this->values);
00421 }
00422
00427 public function toBeSavedWithInsert()
00428 {
00429 return ($this->saveType === Record::INSERT);
00430 }
00431
00436 public function toBeSavedWithUpdate()
00437 {
00438 return ($this->saveType === Record::UPDATE);
00439 }
00440
00445 public function toBeSavedWithDelete()
00446 {
00447 return ($this->saveType === Record::DELETE);
00448 }
00449
00454 public function markRecordClean()
00455 {
00456 $this->dirtyCols = array();
00457 }
00458
00464 public function markForInsert()
00465 {
00466 if ($this->ds instanceof QueryDataSet) {
00467 throw new DataSetException ("You cannot mark a record in a QueryDataSet for insert");
00468 }
00469 $this->setSaveType(Record::INSERT);
00470 }
00471
00477 public function markForUpdate()
00478 {
00479 if ($this->ds instanceof QueryDataSet) {
00480 throw new DataSetException ("You cannot mark a record in a QueryDataSet for update");
00481 }
00482 $this->setSaveType(Record::UPDATE);
00483 }
00489 public function markToBeDeleted()
00490 {
00491 if ($this->ds instanceof QueryDataSet) {
00492 throw new DataSetException ("You cannot mark a record in a QueryDataSet for deletion");
00493 }
00494 $this->setSaveType(Record::DELETE);
00495
00496 }
00497
00508 public function unmarkToBeDeleted()
00509 {
00510 if ($this->saveType === Record::ZOMBIE) {
00511 throw new DataSetException ("This record has already been deleted!");
00512 }
00513 $this->setSaveType(UNKNOWN);
00514
00515 }
00516
00522 public function markValueClean($col)
00523 {
00524 unset($this->dirtyCols[$col]);
00525 }
00526
00532 public function markValueDirty($col)
00533 {
00534 $this->dirtyCols[$col] = true;
00535 }
00536
00542 public function setSaveType($type)
00543 {
00544 $this->saveType = $type;
00545 }
00546
00551 public function getSaveType()
00552 {
00553 return $this->saveType;
00554 }
00555
00561 public function setValue ($col, $value)
00562 {
00563 $this->values[$col] = $value;
00564 $this->markValueDirty($col);
00565 return $this;
00566 }
00567
00574 public function isAZombie()
00575 {
00576 return ($this->saveType === Record::ZOMBIE);
00577 }
00578
00583 public function needsToBeSaved()
00584 {
00585 return (!$this->isAZombie() || !$this->recordIsClean() || $this->toBeSavedWithUpdate() ||
00586 $this->toBeSavedWithDelete() || $this->toBeSavedWithInsert());
00587 }
00588
00594 public function valueIsClean($column)
00595 {
00596 if (!isset($this->values[$column])) {
00597 throw new DataSetException("Undefined column: ".$column);
00598 }
00599 return !isset($this->dirtyCols[$column]);
00600 }
00601
00606 public function recordIsClean()
00607 {
00608 return empty($this->dirtyCols);
00609 }
00610
00620 public function refresh(Connection $conn = null)
00621 {
00622 if ($conn === null) {
00623 $conn = $this->ds->connection();
00624 }
00625
00626 if ($this->toBeSavedWithDelete()) {
00627 return;
00628 } elseif ($this->toBeSavedWithInsert()) {
00629 throw new DataSetException("There is no way to refresh a record which has been created with addRecord().");
00630 } elseif ($this->ds instanceof QueryDataSet) {
00631 throw new DataSetException ("You can only perform a refresh on Records created with a TableDataSet.");
00632 }
00633
00634 $stmt = null;
00635 try {
00636 $stmt = $conn->prepareStatement ($this->getRefreshSql());
00637 $ps = 1;
00638 $kd = $this->ds->keydef();
00639 for ($i = 1, $kdsize = $kd->size(); $i <= $kdsize; $i++)
00640 {
00641 $val = $this->getValue($kd->getAttrib($i));
00642 if ($val == null) {
00643 throw new DataSetException ("You cannot execute an update with a null value for a KeyDef.");
00644 }
00645 $setter = 'set' . CreoleTypes::getAffix( $table->getColumn($col)->getType() );
00646 $stmt->$setter($ps++, $val);
00647 }
00648 $rs = $stmt->executeQuery();
00649 $rs->next();
00650 $this->initializeRecord();
00651 $this->createValues($rs);
00652 } catch (SQLException $e) {
00653 if ($stmt) $stmt->close();
00654 throw $e;
00655 }
00656 }
00657
00666 public function getRefreshSql()
00667 {
00668 if ($this->ds->keydef() === null || $this->ds->keydef()->size() === 0) {
00669 throw new DataSetException("You can only perform a getRefreshQueryString on a TableDataSet that was created with a KeyDef.");
00670 } elseif ($this->ds instanceof QueryDataSet) {
00671 throw new DataSetException("You can only perform a getRefreshQueryString on Records created with a TableDataSet.");
00672 }
00673
00674 $sql1 = "";
00675 $sql2 = "";
00676 $comma = false;
00677
00678 foreach($this->columns() as $col) {
00679 if (!$comma) {
00680 $attribs_sql .= $col;
00681 $comma = true;
00682 } else {
00683 $attribs_sql .= ", " . $col;
00684 }
00685 }
00686
00687 $comma = false;
00688
00689 for ($i = 1, $kdsize = $kd->size(); $i <= $kdsize; $i++) {
00690 $attrib = $kd->getAttrib($i);
00691
00692 if (!$this->valueIsClean($attrib)) {
00693 throw new DataSetException (
00694 "You cannot do a refresh from the database if the value " .
00695 "for a KeyDef column has been changed with a Record.setValue().");
00696 }
00697
00698 if (!$comma) {
00699 $where_sql .= $attrib . " = ?";
00700 $comma = true;
00701 } else {
00702 $where_sql .= " AND " . $attrib . " = ?";
00703 }
00704 }
00705
00706 return "SELECT " . $attribs_sql . " FROM " . $this->ds->tableName() . " WHERE " . $where_sql;
00707 }
00708
00714 public function dataset()
00715 {
00716 return $this->ds;
00717 }
00718
00723 public function setParentDataSet(DataSet $ds)
00724 {
00725 $this->ds = $ds;
00726 }
00727
00732 public function __toString()
00733 {
00734 $sb = "{";
00735 foreach($this->columns() as $col) {
00736 $sb .= "'" . $this->getValue($col) . "',";
00737 }
00738 $sb = substr($sb, 0, -1);
00739 $sb .= "}";
00740 return $sb;
00741 }
00742
00743 }