table = &$table;
$this->keyfieldname = $keyfieldname;
}
/**
* Change active key field
* @param $keyfieldname Optional name of primary key field, defaults to 'ID'
*/
function set_key_field($keyfieldname='ID')
{
$this->keyfieldname = $keyfieldname;
$this->key = isset($this->data[$keyfieldname]) ? $this->data[$keyfieldname] : '';
}
/**
* Select a set of records of this table according to an array of selection
* and sorting criteria.
* Use fetch_next() to iterate through the result set
* Example:
* $record->select(array('Type' => 'bar', 'Date >=' => '1999-01-01',
* '-Date <' => 'NOW()', 'User NI' => 'chris'), 'ORDER BY Date');
*
* @param $params optional array of fieldname => value tupels.
* These are ANDed to form a WHERE clause.
* fieldname can contain an operator (separated by space), the
* default operator is '='. The special operator 'NI' specifies
* that the argument must be contained in a comma-separated list.
*
* @param $sql Optional SQL addendum added after $params (ORDER BY etc.)
* @param $fields Optional field list to select (defaults to '*')
* @return Number of rows that matched this query
* @see it_db_record::fetch_next, it_db_table::construct_sql_clause
*
*/
function select($params='', $sql='', $fields='*')
{
$this->handle = $this->table->safe_sql_select($this->table->construct_sql_clause($params, $sql), $fields);
return $this->table->db->num_rows($this->handle);
}
/**
* Post-process data after reading a record.
* This is a stub-function that can be overloaded.
*/
function _read_post_process()
{
}
/**
* Pre-process data before writing a record.
* This is a stub-function that can be overloaded.
* @param $tags Reference to update/create tags, can be modified as needed
*/
function _write_pre_process(&$tags)
{
}
/**
* Fetch the next record of a select() query
* @return true if a record was successfully fetched, false otherwise
* @see it_db_record::select
*/
function fetch_next()
{
return $this->fetch($this->handle);
}
/**
* Fetch the next record from an SQL result into this object.
* If no more records are pending, clear the object's data.
* @param $sqlresult Result of table::sql_select() or similar
* @return 1 if a record was successfully fetched, 0 otherwise
*/
function fetch(&$sqlresult)
{
$result = 0;
if ($this->data = $this->table->db->fetch_assoc($sqlresult))
{
$this->key = $this->data[$this->keyfieldname];
$result = 1;
}
else
{
$this->key = ""; /* "No record is loaded" */
$this->data = array(); /* Clear local record data */
}
$this->_read_post_process();
return $result;
}
/**
* Read a record from table
* @param $key key field value of the desired row
* @return 1 on success, 0 otherwise
*/
function read($key)
{
$result = 0;
if ($key != "")
{
$result = $this->fetch($this->table->safe_sql_select("WHERE $this->keyfieldname='" . mysql_real_escape_string($key) . "'"));
}
else
{
$this->key = ""; /* "No record is loaded" */
$this->data = array(); /* Clear local record data */
}
$this->_read_post_process();
return $result;
}
/**
* Create a new record in the table
* @see it_db_record::record_create, it_db_record::safe_create
* @param $tags array of Fieldname/Value pairs
* @param $replace use REPLACE (1) instead of INSERT (otherwise)
* @return MySQL result (0 means failure)
*/
function create($tags, $replace=0)
{
/* Pre-processing, $tags is passed by reference and may be modified here */
$this->_write_pre_process($tags);
$sql = $replace ? "REPLACE" : "INSERT";
$sql .= " INTO " . $this->table->name . " (";
$sql2 = '';
unset($this->data);
for ($sep=""; $ti = each($tags); $sep=",")
{
/* If the field name starts with '-', the value is taken as raw, no escaping
is done and no quotes are put around it. */
if (substr($ti[0], 0, 1) == '-')
{
$ti[0] = substr($ti[0], 1); /* Strip that '-' sign */
$val = $ti[1];
}
else
$val = isset($ti[1]) ? "'".mysql_real_escape_string($ti[1])."'" : 'NULL';
$this->data[$ti[0]] = $ti[1]; /* Store data for later use */
$sql .= $sep . $ti[0];
$sql2 .= "$sep$val";
}
$sql .= ") VALUES ($sql2)";
if ($result = $this->table->db->sql_query($sql))
{
/* If the key element was not given, it means it's an AUTO_INCREMENT thingy. */
if (($this->key = $this->data[$this->keyfieldname]) == "")
{
$result = $this->table->db->safe_sql_query("SELECT LAST_INSERT_ID()");
$this->key = mysql_result($result, 0, 0);
}
/* We need all fields from record, not only those we initialised */
$this->read($this->key);
}
return $result;
}
/**
* Safe version of create (doesn't return in case of failure)
* @see it_db_record::create, it_db_record::record_create
* @param $tags array of Fieldname/Value pairs
* @param $replace use REPLACE (1) instead of INSERT (otherwise)
* @return MySQL result (0 means failure)
*/
function safe_create($tags, $replace=0)
{
if ($result = $this->create($tags, $replace))
return $result;
else
fail("it_db_record::safe_create() failed: " . $this->table->db->error());
}
/**
* Update a record in the table
* @param $tags array of Fieldname/Value pairs
* @return MySQL result (0 means failure)
*/
function update($tags)
{
/* Pre-processing, $tags is passed by reference and may be modified here */
$this->_write_pre_process($tags);
if ($this->key == "")
internal_error("it_db_record::update(): can't update undefined record");
/* If we have nothing to do, return instead of performing an invalid SQL query */
if (!count($tags))
return true;
$sql = "UPDATE " . $this->table->name . " SET ";
for ($sep="", $raw=0; $ti = each($tags); $sep=",")
{
/* If the field name starts with '-', the value is taken as raw, no escaping
is done and no quotes are put around it. */
if (substr($ti[0], 0, 1) == '-')
{
$ti[0] = substr($ti[0], 1); /* Strip that '-' sign */
$val = $ti[1];
$raw++;
}
else
$val = "'".mysql_real_escape_string($ti[1])."'";
$sql .= $sep.$ti[0].'='.$val;
if ($ti[0] == $this->key)
{
if ($this->data[$this->keyfieldname] != $ti[0])
internal_error("$sql: trying to change key from $this->key.");
}
$this->data[$ti[0]] = $ti[1];
}
$sql .= " WHERE " . $this->keyfieldname . "='" . mysql_real_escape_string($this->key) . "'";
/* debug("it_db_record::update(): $sql"); */
$ret = $this->table->db->safe_sql_query($sql);
/* Only re-read record if necessary (for performance), but always do post-processing. */
if ($raw)
$this->read($this->key);
else
$this->_read_post_process();
return $ret;
}
/**
* Delete the current record from the database
* @param $key keyfield value of the record to be deleted. If this is missing, the current record is deleted.
* @return MySQL result (0 means failure)
*/
function delete($key="")
{
if (!$key) $key = $this->key;
if (($this->table->name == "") || ($this->keyfieldname == "") || ($key == ""))
internal_error("it_db_record::delete(): no record\n");
$sql = "DELETE FROM " . $this->table->name . " WHERE ". $this->keyfieldname . "='" . mysql_real_escape_string($key) . "'";
if ($result = $this->table->db->sql_query($sql))
{
$this->key = "";
$this->data = array();
}
return $result;
}
/**
* Return a field of a record.
* @param $field field name
* @return field value. If the field does not exists, issue an
* error message and terminate program execution.
*/
function safe_get_field($field)
{
if ($field)
{
if (isset($this->data[$field]))
return $this->data[$field];
else internal_error("it_db_record::safe_get_field(): field \"$field\" not present in record.");
}
else internal_error("it_db_record::safe_get_field(): empty field name");
}
/**
* Output all fields of a record in (ugly) html format
*/
function dump_html()
{
echo "
Dump of table " . $this->table->name . ", record " . $this->keyfieldname . " = \"" . $this->key . "\":
\n"; reset($this->data); next($this->data); while(list($key, $value) = each($this->data)) { if ($key > 0) continue; /* $$$ ugly */ echo $key . " = \"" . htmlspecialchars($value) . "\"\n"; } echo "