Record.php

Go to the documentation of this file.
00001 <?php 00002 00003 /* 00004 * $Id: Record.php,v 1.7 2004/05/23 09:33:49 micha Exp $ 00005 * 00006 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00007 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00008 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 00009 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 00010 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00011 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 00012 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 00013 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 00014 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00015 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00016 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00017 * 00018 * This software consists of voluntary contributions made by many individuals 00019 * and is licensed under the LGPL. For more information please see 00020 * <http://creole.phpdb.org>. 00021 * 00022 * This product includes software based on the Village framework, 00023 * http://share.whichever.com/index.php?SCREEN=village. 00024 */ 00025 00026 include_once 'creole/CreoleTypes.php'; 00027 00037 class Record { 00038 00039 // saveType constants 00040 const ZOMBIE = -1; 00041 const UNKNOWN = 0; 00042 const INSERT = 1; 00043 const UPDATE = 2; 00044 const DELETE = 3; 00045 const BEFOREINSERT = 4; 00046 const AFTERINSERT = 5; 00047 const BEFOREUPDATE = 6; 00048 const AFTERUPDATE = 7; 00049 const BEFOREDELETE = 8; 00050 const AFTERDELETE = 9; 00051 00053 private $values = array(); 00054 00056 private $dirtyCols = array(); 00057 00059 private $ds; 00060 00062 private $saveType = 0; 00063 00072 function __construct(DataSet $ds, $addRecord = false) 00073 { 00074 $this->setParentDataSet($ds); 00075 if (!$addRecord) { 00076 $this->createValues($this->ds->resultSet()); 00077 } 00078 } 00079 00083 private function initializeRecord() 00084 { 00085 $this->values = array(); 00086 $this->dirtyCols = array(); 00087 $this->setSaveType(UNKNOWN); 00088 } 00089 00095 private function createValues(ResultSet $rs) 00096 { 00097 $this->values = $rs->getRow(); 00098 } 00099 00104 public function delete(Connection $conn = null) 00105 { 00106 $this->setSaveType(DELETE); 00107 $this->save($conn); 00108 } 00109 00116 public function save(Connection $conn = null) 00117 { 00118 $returnValue = false; 00119 00120 if ($this->ds instanceof QueryDataSet) { 00121 throw new DataSetException("You cannot save a QueryDataSet. Please use a TableDataSet instead."); 00122 } 00123 00124 if (!$this->needsToBeSaved()) { 00125 return $returnValue; 00126 } 00127 00128 switch($this->saveType) { 00129 case INSERT: 00130 $returnValue = $this->doInsert($conn); 00131 break; 00132 case UPDATE: 00133 $returnValue = $this->doUpdate($conn); 00134 break; 00135 case DELETE: 00136 $returnValue = $this->doDelete($conn); 00137 break; 00138 default: 00139 throw new DataSetException("Invalid or no-action saveType for Record."); 00140 } 00141 00142 return (boolean) $returnValue; 00143 } 00144 00150 private function doDelete(Connection $conn = null) 00151 { 00152 if ($conn === null) { 00153 $conn = $this->ds->connection(); 00154 } 00155 00156 $table = $this->ds->tableInfo(); 00157 $stmt = null; 00158 try { 00159 $stmt = $conn->prepareStatement($this->getDeleteSql()); 00160 $ps = 1; 00161 $kd = $this->ds->keydef(); 00162 for ($i = 1, $kdsize = $kd->size(); $i <= $kdsize; $i++) { 00163 $col = $kd->getAttrib($i); 00164 $val = $this->getValue($col); 00165 $setter = 'set' . CreoleTypes::getAffix( $table->getColumn($col)->getType() ); 00166 $stmt->$setter($ps++, $val); 00167 } 00168 00169 $ret = $stmt->executeUpdate(); 00170 00171 // note that the actual removal of the Record objects 00172 // from the DataSet is done by the TDS::save() method. 00173 $this->setSaveType(ZOMBIE); 00174 00175 $stmt->close(); 00176 00177 if ($ret > 1) { 00178 throw new SQLException("There were " . $ret . " rows deleted with this records key value."); 00179 } 00180 00181 return $ret; 00182 00183 } catch (SQLException $e) { 00184 if ($stmt) $stmt->close(); 00185 throw $e; 00186 } 00187 } 00188 00194 private function doUpdate(Connection $conn = null) 00195 { 00196 if ($conn === null) { 00197 $conn = $this->ds->connection(); 00198 } 00199 00200 $table = $this->ds->tableInfo(); 00201 00202 $stmt = null; 00203 try { 00204 $stmt = $conn->prepareStatement($this->getUpdateSql()); 00205 00206 $ps = 1; 00207 foreach($this->dirtyColumns() as $col) { 00208 $setter = 'set' . CreoleTypes::getAffix( $table->getColumn($col)->getType() ); 00209 $stmt->$setter($ps++, $this->getValue($col)); 00210 } 00211 00212 $kd = $this->ds->keydef(); 00213 for ($i = 1, $kdsize = $kd->size(); $i <= $kdsize; $i++) { 00214 $attrib = $kd->getAttrib($i); 00215 $setter = 'set' . CreoleTypes::getAffix( $table->getColumn($attrib)->getType() ); 00216 $stmt->$setter($ps++, $this->getValue($attrib)); 00217 } 00218 00219 $ret = $stmt->executeUpdate(); 00220 00221 if ($this->ds->refreshOnSave()) { 00222 $this->refresh(); 00223 } else { 00224 // Marks all of the values clean since they have now been saved 00225 $this->markRecordClean(); 00226 } 00227 00228 $this->setSaveType(AFTERUPDATE); 00229 00230 if ($ret > 1) { 00231 throw new SQLException ("There were " . $ret . " rows updated with this records key value."); 00232 } 00233 return $ret; 00234 } catch (SQLException $e) { 00235 if ($stmt) $stmt->close(); 00236 throw $e; 00237 } 00238 00239 } 00240 00246 private function doInsert(Connection $conn = null) 00247 { 00248 $stmt = null; 00249 00250 try { 00251 $stmt = $conn->prepareStatement($this->getInsertSql()); 00252 $ps = 1; 00253 foreach($this->dirtyColumns() as $col) { 00254 $val = $this->getValue($col); 00255 $setter = 'set' . CreoleTypes::getAffix( $table->getColumn($col)->getType() ); 00256 $stmt->$setter($ps++, $val); 00257 } 00258 00259 $ret = $stmt->executeUpdate(); 00260 00261 if ($this->ds->refreshOnSave()) { 00262 $this->refresh(); 00263 } else { 00264 // Marks all of the values clean since they have now been saved 00265 $this->markRecordClean(); 00266 } 00267 00268 $this->setSaveType(AFTERINSERT); 00269 00270 if ($ret > 1) { 00271 // a little late again... 00272 throw new SQLException ("There were " . $ret . " rows inserted with this records key value."); 00273 } 00274 00275 return $ret; 00276 } catch (SQLException $e) { 00277 if ($stmt) $stmt->close(); 00278 throw $e; 00279 } 00280 } 00281 00287 private function getUpdateSql() 00288 { 00289 $kd = $this->ds->keydef(); 00290 if ($kd === null || $kd->size() === 0) { 00291 throw new DataSetException("You must specify KeyDef attributes for this TableDataSet in order to create a Record for update."); 00292 } elseif ($this->recordIsClean()) { 00293 throw new DataSetException ("You must Record->setValue() on a column before doing an update."); 00294 } 00295 00296 $set_sql = ""; 00297 $where_sql = ""; 00298 00299 $comma = false; 00300 00301 foreach($this->dirtyColumns() as $col) { 00302 if (!$comma) { 00303 $set_sql .= $col . " = ?"; 00304 $comma = true; 00305 } else { 00306 $set_sql .= ", " . $col . " = ?"; 00307 } 00308 } 00309 00310 $comma = false; 00311 for ($i = 1, $kdsize = $kd->size(); $i <= $kdsize; $i++) { 00312 $attrib = $kd->getAttrib($i); 00313 if (! $this->valueIsClean ($attrib)) { 00314 throw new DataSetException ("The value for column '" . $attrib . "' is a key value and cannot be updated."); 00315 } 00316 if (!$comma) { 00317 $where_sql .= $attrib . " = ?"; 00318 $comma = true; 00319 } else { 00320 $where_sql .= " AND " . $attrib . " = ?"; 00321 } 00322 } 00323 return "UPDATE " . $this->ds->tableName() . " SET " . $set_sql . " WHERE " . $where_sql; 00324 } 00325 00326 00332 private function getDeleteSql() 00333 { 00334 $kd = $this->ds->keydef(); 00335 00336 if ($kd === null || $kd->size() === 0) { 00337 throw new DataSetException("You must specify KeyDef attributes for this TableDataSet in order to delete a Record."); 00338 } 00339 00340 $where_sql = ""; 00341 $comma = false; 00342 for ($i = 1, $kdsize = $kd->size(); $i <= $kdsize; $i++) { 00343 if (!$comma) { 00344 $where_sql .= $kd->getAttrib($i) . " = ?"; 00345 $comma = true; 00346 } else { 00347 $where_sql .= " AND " . $kd->getAttrib($i) . " = ?"; 00348 } 00349 } 00350 00351 return "DELETE FROM " . $this->ds->tableName() . " WHERE " . $where_sql; 00352 } 00353 00358 private function getInsertSql() 00359 { 00360 $fields_sql = ""; 00361 $values_sql = ""; 00362 $comma = false; 00363 foreach($this->dirtyColumns() as $col) { 00364 if (!$comma) { 00365 $fields_sql .= $col; 00366 $values_sql .= "?"; 00367 $comma = true; 00368 } else { 00369 $fields_sql .= ", " . $col; 00370 $values_sql .= ", ?"; 00371 } 00372 } 00373 return "INSERT INTO " . $this->ds->tableName() . " ( " . $fields_sql . " ) VALUES ( " . $values_sql . " )"; 00374 } 00375 00382 public function getValue($col) 00383 { 00384 if (!isset($this->values[$col])) { 00385 throw new DataSetException("Undefined column in Record: " . $col); 00386 } 00387 return $this->values[$col]; 00388 } 00389 00394 public function columns() 00395 { 00396 return array_keys($this->values); 00397 } 00398 00405 private function dirtyColumns() 00406 { 00407 return array_keys($this->dirtyCols); 00408 } 00409 00414 public function size() 00415 { 00416 return count($this->values); 00417 } 00418 00423 public function toBeSavedWithInsert() 00424 { 00425 return ($this->saveType === INSERT); 00426 } 00427 00432 public function toBeSavedWithUpdate() 00433 { 00434 return ($this->saveType === UPDATE); 00435 } 00436 00441 public function toBeSavedWithDelete() 00442 { 00443 return ($this->saveType === DELETE); 00444 } 00445 00450 public function markRecordClean() 00451 { 00452 $this->dirtyCols = array(); 00453 } 00454 00460 public function markForInsert() 00461 { 00462 if ($this->ds instanceof QueryDataSet) { 00463 throw new DataSetException ("You cannot mark a record in a QueryDataSet for insert"); 00464 } 00465 $this->setSaveType(INSERT); 00466 } 00467 00473 public function markForUpdate() 00474 { 00475 if ($this->ds instanceof QueryDataSet) { 00476 throw new DataSetException ("You cannot mark a record in a QueryDataSet for update"); 00477 } 00478 $this->setSaveType(UPDATE); 00479 } 00485 public function markToBeDeleted() 00486 { 00487 if ($this->ds instanceof QueryDataSet) { 00488 throw new DataSetException ("You cannot mark a record in a QueryDataSet for deletion"); 00489 } 00490 $this->setSaveType(DELETE); 00491 // return this; 00492 } 00493 00504 public function unmarkToBeDeleted() 00505 { 00506 if ($this->saveType === ZOMBIE) { 00507 throw new DataSetException ("This record has already been deleted!"); 00508 } 00509 $this->setSaveType(UNKNOWN); 00510 //return $this; 00511 } 00512 00518 public function markValueClean($col) 00519 { 00520 unset($this->dirtyCols[$col]); 00521 } 00522 00528 public function markValueDirty($col) 00529 { 00530 $this->dirtyCols[$col] = true; 00531 } 00532 00538 public function setSaveType($type) 00539 { 00540 $this->saveType = $type; 00541 } 00542 00547 public function getSaveType() 00548 { 00549 return $this->saveType; 00550 } 00551 00557 public function setValue ($col, $value) 00558 { 00559 $this->values[$col] = $value; 00560 $this->markValueDirty($col); 00561 return $this; 00562 } 00563 00570 public function isAZombie() 00571 { 00572 return ($this->saveType === ZOMBIE); 00573 } 00574 00579 public function needsToBeSaved() 00580 { 00581 return (!$this->isAZombie() || !$this->recordIsClean() || $this->toBeSavedWithUpdate() || 00582 $this->toBeSavedWithDelete() || $this->toBeSavedWithInsert()); 00583 } 00584 00590 public function valueIsClean($column) 00591 { 00592 if (!isset($this->values[$column])) { 00593 throw new DataSetException("Undefined column: ".$column); 00594 } 00595 return !isset($this->dirtyCols[$column]); 00596 } 00597 00602 public function recordIsClean() 00603 { 00604 return empty($this->dirtyCols); 00605 } 00606 00616 public function refresh(Connection $conn = null) 00617 { 00618 if ($conn === null) { 00619 $conn = $this->ds->connection(); 00620 } 00621 00622 if ($this->toBeSavedWithDelete()) { 00623 return; 00624 } elseif ($this->toBeSavedWithInsert()) { 00625 throw new DataSetException("There is no way to refresh a record which has been created with addRecord()."); 00626 } elseif ($this->ds instanceof QueryDataSet) { 00627 throw new DataSetException ("You can only perform a refresh on Records created with a TableDataSet."); 00628 } 00629 00630 $stmt = null; 00631 try { 00632 $stmt = $conn->prepareStatement ($this->getRefreshSql()); 00633 $ps = 1; 00634 $kd = $this->ds->keydef(); 00635 for ($i = 1, $kdsize = $kd->size(); $i <= $kdsize; $i++) 00636 { 00637 $val = $this->getValue($kd->getAttrib($i)); 00638 if ($val == null) { 00639 throw new DataSetException ("You cannot execute an update with a null value for a KeyDef."); 00640 } 00641 $setter = 'set' . CreoleTypes::getAffix( $table->getColumn($col)->getType() ); 00642 $stmt->$setter($ps++, $val); 00643 } 00644 $rs = $stmt->executeQuery(); 00645 $rs->next(); 00646 $this->initializeRecord(); 00647 $this->createValues($rs); 00648 } catch (SQLException $e) { 00649 if ($stmt) $stmt->close(); 00650 throw $e; 00651 } 00652 } 00653 00662 public function getRefreshSql() 00663 { 00664 if ($this->ds->keydef() === null || $this->ds->keydef()->size() === 0) { 00665 throw new DataSetException("You can only perform a getRefreshQueryString on a TableDataSet that was created with a KeyDef."); 00666 } elseif ($this->ds instanceof QueryDataSet) { 00667 throw new DataSetException("You can only perform a getRefreshQueryString on Records created with a TableDataSet."); 00668 } 00669 00670 $sql1 = ""; 00671 $sql2 = ""; 00672 $comma = false; 00673 00674 foreach($this->columns() as $col) { 00675 if (!$comma) { 00676 $attribs_sql .= $col; 00677 $comma = true; 00678 } else { 00679 $attribs_sql .= ", " . $col; 00680 } 00681 } 00682 00683 $comma = false; 00684 00685 for ($i = 1, $kdsize = $kd->size(); $i <= $kdsize; $i++) { 00686 $attrib = $kd->getAttrib($i); 00687 00688 if (!$this->valueIsClean($attrib)) { 00689 throw new DataSetException ( 00690 "You cannot do a refresh from the database if the value " . 00691 "for a KeyDef column has been changed with a Record.setValue()."); 00692 } 00693 00694 if (!$comma) { 00695 $where_sql .= $attrib . " = ?"; 00696 $comma = true; 00697 } else { 00698 $where_sql .= " AND " . $attrib . " = ?"; 00699 } 00700 } 00701 00702 return "SELECT " . $attribs_sql . " FROM " . $this->ds->tableName() . " WHERE " . $where_sql; 00703 } 00704 00710 public function dataset() 00711 { 00712 return $this->ds; 00713 } 00714 00719 public function setParentDataSet(DataSet $ds) 00720 { 00721 $this->ds = $ds; 00722 } 00723 00728 public function __toString() 00729 { 00730 $sb = "{"; 00731 foreach($this->columns() as $col) { 00732 $sb .= "'" . $this->getValue($col) . "',"; 00733 } 00734 $sb = substr($sb, 0, -1); 00735 $sb .= "}"; 00736 return $sb; 00737 } 00738 00739 }

This file is part of the Creole[php5] library.


Copyright © 2004 Hans Lellelid  
Creole[php5] CVS