src/DataAccessObject.php
<?php
namespace Parm;
use Parm\Exception\ErrorException;
use Parm\Exception\GetFieldValueException;
use Parm\Exception\RecordNotFoundException;
abstract class DataAccessObject extends Row implements TableInterface
{
/**
* Get the ID (Primary Key) of the object
* @return mixed ID of the record in the database. null if a new record
*/
abstract public function getId();
abstract public function isAutoIncremented();
private $modifiedColumns = array();
/**
* @var DataAccessObjectFactory
*/
private $factory;
private $isNewObject = false;
/**
* Constructor
* @param array $row Array of data
* @param DataAccessObjectFactory $factory
*/
public function __construct(array $row = null, DataAccessObjectFactory $factory = null)
{
if ($row == null) {
$row = static::getDefaultRow();
$this->isNewObject = true;
} elseif (!array_key_exists(static::getIdField(), $row)) {
$row[static::getIdField()] = null;
$this->isNewObject = true;
}
if ($this->isNewObject()) {
foreach (array_keys($row) as $field) {
if (in_array($field, static::getFields())) {
$this->addModifiedColumn($field);
}
}
}
$this->factory = static::ifNullReturnNewFactory($factory);
parent::__construct($row);
}
/**
* @param string $column the name of the column
*/
protected function addModifiedColumn($column)
{
$this->modifiedColumns[$column] = 1;
}
/**
* clear the list of modified columns so none are saved to the database
*/
protected function clearModifiedColumns()
{
$this->modifiedColumns = array();
}
protected function setupAsNewObject()
{
$this->clearModifiedColumns();
$this->isNewObject = true;
foreach (static::getFields() as $field) {
$this->addModifiedColumn($field);
}
}
/**
* Find an object by ID
* @param integer $id ID of the row in the database
* @param DataAccessObjectFactory $factory
* @return null|object The record from the database
*/
public static function findId($id, DataAccessObjectFactory $factory = null)
{
$factory = self::ifNullReturnNewFactory($factory);
return $factory->findId($id);
}
/**
* Save the object to the database.
* @return object
*/
public function save()
{
if (count($this->modifiedColumns) > 0) {
$sql = array();
foreach ($this->modifiedColumns as $field => $j) {
if ($field == $this->getIdField() && !$this->isAutoIncremented()) {
if ($this->getId() == null) {
throw new ErrorException($this->getIdField() . " is required for tables without an autoincrement id.");
}
$sql[] = $this->getTableName() . "." . $field . ' = ' . $this->factory->escapeString($this->getId()) . '';
} elseif ( $field != $this->getIdField() && in_array($field, static::getFields())) {
if ($this[$field] !== null) {
$sql[] = $this->getTableName() . "." . $field . ' = ' . $this->factory->escapeString($this[$field]) . '';
} else {
$sql[] = $this->getTableName() . "." . $field . ' = NULL';
}
}
}
if ($this->isNewObject()) {
$this->factory->update('INSERT INTO ' . $this->getTableName() . " SET " . implode(",", $sql));
if($this->isAutoIncremented()){
$this[$this->getIdField()] = $this->factory->getLastInsertId();
}
} else {
$this->factory->update('UPDATE ' . $this->getTableName() . " SET " . implode(",", $sql) . " WHERE " . $this->getTableName() . "." . $this->getIdField() . ' = ' . $this->factory->escapeString($this->getId()));
}
}
$this->clearModifiedColumns();
$this->isNewObject = false;
return $this;
}
/**
* Delete the object from the database.
* @return TRUE if successful, False if failed or ID is not greater than
*
* @throws Exception\RecordNotFoundException
* @throws Exception\UpdateFailedException
*/
public function delete()
{
if (!$this->isNewObject()) {
$this->factory->update("DELETE FROM " . $this->getTableName() . " WHERE " . $this->getIdField() . " = " . $this->factory->escapeString($this->getId()));
} else {
throw new RecordNotFoundException("delete() failed: You can't delete this object from the database as it hasn't been saved yet.");
}
}
/**
* Test if the object has been saved to the database. The primary key will be null
* @return boolean
*/
public function isNewObject()
{
return $this->isNewObject;
}
/**
* Clone the object as a new object. This will cause the save() to save a new row to the database.
* @return DataAccessObject
*/
public function duplicateAsNewObject()
{
$data = (array) $this;
if (static::getIdField()) {
unset($data[static::getIdField()]);
}
return new static($data);
}
/**
* Convert to JSON ready array. Camel case fields and the primary key is always mapped to 'id'
* @return array
*/
public function toJSON()
{
$json = parent::toJSON();
$json['id'] = $this->getId();
return $json;
}
/**
* Used by the generated classes
*/
public function getFieldValue($columnName)
{
if (array_key_exists($columnName, $this)) {
return $this[$columnName];
} else {
throw new GetFieldValueException($columnName . ' not initialized for get method in ' . get_class($this));
}
}
/**
* @param $columnName
* @param $val
* @return $this
*/
protected function setFieldValue($columnName, $val)
{
if ($val === NULL || strcmp($this[$columnName], $val) !== 0) {
$this->addModifiedColumn($columnName);
$this[$columnName] = $val;
}
return $this;
}
/**
* @param $columnName
* @param $val
* @return DataAccessObject
*/
protected function setIntFieldValue($columnName, $val)
{
if ($val === null) {
return $this->setFieldValue($columnName, NULL);
} else {
return $this->setFieldValue($columnName, (int) $val);
}
}
/**
* @param $columnName
* @return int|null
* @throws GetFieldValueException
*/
protected function getIntFieldValue($columnName)
{
$val = $this->getFieldValue($columnName);
if ($val === null) {
return null;
} else {
return (int) $val;
}
}
/**
* @param $columnName
* @param $mixed
* @return $this|DataAccessObject
*/
protected function setDateFieldValue($columnName, $mixed)
{
if ($mixed instanceof \DateTime) {
return $this->setFieldValue($columnName, $mixed->format($this->factory->getDateStorageFormat()));
} elseif (is_int($mixed)) {
$date = new \DateTime();
$date->setTimestamp($mixed);
return $this->setFieldValue($columnName, $date->format($this->factory->getDateStorageFormat()));
} else {
return $this->setFieldValue($columnName, $mixed);
}
}
/**
* @param $columnName
* @param null $format
* @return mixed|string
* @throws GetFieldValueException
*/
protected function getDateFieldValue($columnName, $format = null)
{
if ($format != null && $this->getFieldValue($columnName) != null) {
// \Datetime::createFromFormat parses a date value format and sets the time of day to the current system time
// see http://php.net/manual/en/datetime.createfromformat.php for explanation
$dateTime = \DateTime::createFromFormat($this->factory->getDateStorageFormat(), $this->getFieldValue($columnName));
if ($dateTime) { // $dateTime will be a new DateTime instance or FALSE on failure.
// setting the time to midnight as the expected value when pulling from a database
$dateTime->setTime(0, 0, 0);
return $dateTime->format($format);
}
}
return $this->getFieldValue($columnName);
}
/**
* @param $columnName
* @param $format
* @return \DateTime|null
* @throws GetFieldValueException
*/
protected function getDatetimeObjectFromField($columnName, $format)
{
$val = $this->getFieldValue($columnName);
if ($val != null && $format != null) {
return \DateTime::createFromFormat($format, $val);
} else {
return null;
}
}
/**
* @param $columnName
* @param $mixed
* @return $this|DataAccessObject
*/
protected function setDatetimeFieldValue($columnName, $mixed)
{
if ($mixed instanceof \DateTime) {
return $this->setFieldValue($columnName, $mixed->format($this->factory->getDatetimeStorageFormat()));
} elseif (is_int($mixed)) {
$date = new \DateTime();
$date->setTimestamp($mixed);
return $this->setFieldValue($columnName, $date->format($this->factory->getDatetimeStorageFormat()));
} else {
return $this->setFieldValue($columnName, $mixed);
}
}
/**
* @param $columnName
* @param null $format
* @return mixed|string
* @throws GetFieldValueException
*/
protected function getDatetimeFieldValue($columnName, $format = null)
{
if ($format != null && $this->getFieldValue($columnName) != null) {
$dateTime = \DateTime::createFromFormat($this->factory->getDatetimeStorageFormat(), $this->getFieldValue($columnName));
if ($dateTime) { // $dateTime will be a new DateTime instance or FALSE on failure.
return $dateTime->format($format);
}
}
return $this->getFieldValue($columnName);
}
/**
* @param $columnName
* @param $val
* @return $this|DataAccessObject
*/
protected function setBooleanFieldValue($columnName, $val)
{
if ($val === null) {
return $this->setFieldValue($columnName, NULL);
} else {
return $this->setFieldValue($columnName, $val ? 1 : 0);
}
}
/**
* @param $columnName
* @return bool|null
* @throws GetFieldValueException
*/
protected function getBooleanFieldValue($columnName)
{
$val = $this->getFieldValue($columnName);
if ($val === null) {
return null;
} else {
return (bool) $val;
}
}
/**
* @param $columnName
* @param $val
* @return DataAccessObject
*/
protected function setNumericalFieldValue($columnName, $val)
{
if ($val === null) {
return $this->setFieldValue($columnName, NULL);
} else {
return $this->setFieldValue($columnName, (float) $val);
}
}
/**
* @param $columnName
* @return float|null
* @throws GetFieldValueException
*/
protected function getNumericalFieldValue($columnName)
{
$val = $this->getFieldValue($columnName);
if ($val === null) {
return null;
} else {
return (float) $val;
}
}
/**
* @param DataAccessObjectFactory|null $factory
* @return DataAccessObjectFactory
*/
private static function ifNullReturnNewFactory(DataAccessObjectFactory $factory = null)
{
if ($factory == null) {
return static::getFactory();
} else {
return $factory;
}
}
}