Created
March 21, 2016 16:27
-
-
Save deathlyfrantic/8c93addb6ed3b46597ce to your computer and use it in GitHub Desktop.
the start of a mini-framework for object<->db abstraction that i will not finish because it's reinvention of the wheel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
function coerceToCamelCase($string) | |
{ | |
$camel = ""; | |
$i = 0; | |
$upper = false; | |
while($i < mb_strlen($string)) { | |
$char = $string[$i]; | |
if($char === "_") { | |
$upper = true; | |
} elseif($upper) { | |
$camel .= mb_strtoupper($char); | |
$upper = false; | |
} else { | |
$camel .= mb_strtolower($char); | |
} | |
$i++; | |
} | |
return $camel; | |
} | |
function coerceToSnakeCase($string) | |
{ | |
$snake = ""; | |
$i = 0; | |
while($i < mb_strlen($string)) { | |
$char = $string[$i]; | |
if(mb_strtolower($char) === $char) { | |
$snake .= $char; | |
} elseif($i === 0) { | |
$snake .= mb_strtolower($char); | |
} else { | |
$snake .= "_".mb_strtolower($char); | |
} | |
$i++; | |
} | |
return $snake; | |
} | |
abstract class Base | |
{ | |
protected $properties = []; | |
protected function table() | |
{ | |
return coerceToSnakeCase(get_class($this)); | |
} | |
public function __construct($id = null) | |
{ | |
foreach($this->properties as $name => $fieldType) { | |
$prop = new $fieldType(); | |
$prop->setName($name); | |
$this->properties[$name] = $prop; | |
} | |
$idField = new IntegerField(); | |
$idField->setName("id"); | |
$this->properties["id"] = $idField; | |
if(is_numeric($id)) { | |
$this->id = $id; | |
$this->load(); | |
} | |
return $this; | |
} | |
public function __call($name, $args) | |
{ | |
if(mb_stripos($name, "get") === 0) { | |
$prop = mb_substr($name, 3); | |
$prop[0] = mb_strtolower($prop[0]); | |
return $this->getter($prop); | |
} | |
if(mb_stripos($name, "set") === 0) { | |
$prop = mb_substr($name, 3); | |
$prop[0] = mb_strtolower($prop[0]); | |
return $this->setter($prop, $args[0]); | |
} | |
} | |
public function __get($name) | |
{ | |
return $this->getter($name); | |
} | |
public function __set($name, $value) | |
{ | |
$this->setter($name, $value); | |
return $this; | |
} | |
protected function setter($name, $value) | |
{ | |
if(array_key_exists($name, $this->properties)) { | |
$this->properties[$name]->setValue($value); | |
} else { | |
$this->$name = $value; | |
} | |
return $this; | |
} | |
protected function getter($name) | |
{ | |
if(array_key_exists($name, $this->properties)) { | |
return $this->properties[$name]->getValue(); | |
} elseif(property_exists($this, $name)) { | |
return $this->$name; | |
} else { | |
$thisObject = get_class($this); | |
throw new InvalidPropertyException("`$name` is not a valid property of this object ($thisObject)"); | |
} | |
} | |
public function save() | |
{ | |
$params = []; | |
foreach($this->properties as $prop) { | |
if($prop->changed && $prop->getName() !== "id") { | |
$params[$prop->getSnakeCaseName()] = $prop->getValue(); | |
} | |
} | |
if(count($params) > 0) { | |
if(is_numeric($this->id)) { | |
// update query | |
$cols = array_map( | |
function ($col) { | |
return "$col = ?"; | |
}, | |
array_keys($params) | |
); | |
$colsString = implode(", ", $cols); | |
$params["id"] = $this->id; | |
$query = "UPDATE `{$this->table()}` SET $colsString WHERE id = ?;"; | |
} else { | |
// insert query | |
$colsString = implode(", ", array_keys($params)); | |
$marks = implode(", ", array_fill(0, count($params), "?")); | |
$query = "INSERT INTO `{$this->table()}` ($colsString) VALUES ($marks)"; | |
} | |
} else { | |
$query = "no changed properties, nothing to do"; | |
} | |
$this->resetPropertyChangedFlags(); | |
return [$query, $params]; | |
} | |
public function resetPropertyChangedFlags() | |
{ | |
foreach($this->properties as $prop) { | |
$prop->changed = false; | |
} | |
return $this; | |
} | |
public function load() | |
{ | |
$query = "SELECT * FROM `{$this->table()}` WHERE id = ?"; | |
$param = $this->id; | |
// $result = someFetchRowFunction(); | |
$result = [ | |
// for testing | |
"first_name" => "Jimmy", | |
"last_name" => "Smith", | |
"age" => 20 | |
]; | |
$this->setProperties($result); | |
return $this; | |
} | |
public function delete() | |
{ | |
$query = "DELETE FROM `{$this->table()}` WHERE id = ?"; | |
$param = $this->id; | |
// $success = someDeleteRowFunction(); | |
return $success; | |
} | |
public function setProperties(array $data) | |
{ | |
foreach($data as $field => $value) { | |
$prop = $this->properties[coerceToCamelCase($field)]; | |
$prop->setValue($value); | |
} | |
$this->resetPropertyChangedFlags(); | |
return $this; | |
} | |
} | |
class Person extends Base | |
{ | |
protected $properties = [ | |
"firstName" => "StringField", | |
"lastName" => "StringField", | |
"age" => "IntegerField", | |
]; | |
public function getFullName() | |
{ | |
$fullName = []; | |
foreach([$this->firstName, $this->lastName] as $name) { | |
if(mb_strlen($name) > 0) { | |
$fullName[] = $name; | |
} | |
} | |
return implode(" ", $fullName); | |
} | |
} | |
abstract class Field | |
{ | |
protected $name; | |
protected $value; | |
public $changed = false; | |
public function getName() | |
{ | |
return $this->name; | |
} | |
public function getSnakeCaseName() | |
{ | |
return coerceToSnakeCase($this->getName()); | |
} | |
public function setName($name) | |
{ | |
$this->name = $name; | |
return $this; | |
} | |
public function getValue() | |
{ | |
return $this->value; | |
} | |
public function setValue($value) | |
{ | |
if($this->validate($value)) { | |
$this->value = $this->coerce($value); | |
} elseif(is_null($value)) { | |
$this->value = null; | |
} else { | |
$thisType = get_class($this); | |
throw new InvalidValueException("`$value` is not a valid value for a property of this type ($thisType)"); | |
} | |
$this->changed = true; | |
return $this; | |
} | |
public abstract function coerce($value); | |
public abstract function validate($value); | |
} | |
class StringField extends Field | |
{ | |
public function coerce($value) | |
{ | |
return $value; | |
} | |
public function validate($value) | |
{ | |
return true; | |
} | |
} | |
class IntegerField extends Field | |
{ | |
public function validate($value) | |
{ | |
return is_int($value); | |
} | |
public function coerce($value) | |
{ | |
return (int)$value; | |
} | |
} | |
class FloatField extends Field | |
{ | |
public function validate($value) | |
{ | |
return is_float($value); | |
} | |
public function coerce($value) | |
{ | |
return (float)$value; | |
} | |
} | |
class BooleanField extends Field | |
{ | |
public function validate($value) | |
{ | |
return is_bool($value) || (is_int($value) && in_array($value, [0, 1])); | |
} | |
public function coerce($value) | |
{ | |
return (bool)$value; | |
} | |
} | |
class InvalidPropertyException extends \Exception {} | |
class InvalidValueException extends \Exception {} | |
$p = new Person(); | |
$p->firstName = "John"; | |
$p->lastName = "Doe"; | |
$p->age = 30; | |
print_r($p); | |
echo "{$p->firstName}'s age is {$p->age}\n"; | |
print_r($p->save()); | |
$p->id = 1; | |
$p->age = 31; | |
print_r($p->save()); | |
$p->firstName = "Johnathan"; | |
$p->lastName = "Doeson"; | |
print_r($p->save()); | |
$p2 = new Person(1); | |
print_r($p2); | |
echo print_r($p2->getFullName(), true)."\n"; | |
print_r($p2->save()); | |
try { | |
echo $p2->flibberty; | |
} catch(InvalidPropertyException $e) { | |
echo "exception caught: {$e->getMessage()}\n"; | |
} | |
try { | |
$p2->age = "spam"; | |
} catch(InvalidValueException $e) { | |
echo "exception caught: {$e->getMessage()}\n"; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment