123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691 |
- <?php
- abstract class AbstractModel
- {
- /**
- * Db table for find methods
- *
- * @var string
- */
- public static $table;
- /**
- * Db id attribute for find methods
- *
- * @var string
- */
- public static $idAttribute;
- /**
- * Mapping model attributes and database attributes for saving
- *
- * @var array
- */
- protected $attributeDbAttributeMapping = array();
- /**
- * Setup db attribute mapping
- *
- * @param array $childMapping
- *
- * @return array
- *
- * @throws Exception
- */
- protected function setupDbMapping($childMapping = array())
- {
- return array_replace(
- array(
- 'id' => static::$idAttribute,
- ),
- $childMapping
- );
- }
- /**
- * Format or do other things before saving
- *
- * @param array $data
- *
- * @return array
- */
- protected function preSave($data)
- {
- return $data;
- }
- /**
- * Hold all data from a model
- *
- * @var mixed
- */
- protected $data = array();
- /**
- * Constructor.
- *
- * @param array $data
- */
- protected function __construct($data)
- {
- $this->attributeDbAttributeMapping = $this->setupDbMapping();
- if(isset($data[static::$idAttribute])){
- $id = is_numeric($data[static::$idAttribute]) && strpos($data[static::$idAttribute], ',') === false
- ? intval($data[static::$idAttribute])
- : $data[static::$idAttribute];
- $this->setId($id);
- }
- }
- /**
- * Create a model from data
- *
- * @param array $data
- *
- * @return static|null The Model
- */
- public static function create($data)
- {
- if(count($data) > 0){
- return new static($data);
- }
- return null;
- }
- /**
- * Create a model collection from data
- *
- * @param array $multiData
- *
- * @return ModelCollection|static[]
- */
- public static function createMultiple($multiData = array())
- {
- $collection = new ModelCollection();
- foreach($multiData as $data){
- $model = static::create($data);
- if(!is_null($model)){
- if(is_null($model->getId())){
- $collection->add($model);
- }
- else{
- $collection->add($model, $model->getId());
- }
- }
- }
- return $collection;
- }
- /**
- * @see create
- *
- * @param array $data
- *
- * @return AbstractModel|null
- */
- public static function createAndSave($data)
- {
- $model = static::create($data);
- if(!is_null($model) && $model->save()){
- return $model;
- }
- return null;
- }
- /**
- * @see createMultiple
- *
- * @param array $multiData
- *
- * @return ModelCollection|static[]
- */
- public static function createMultipleAndSave($multiData = array())
- {
- $collection = new ModelCollection();
- foreach($multiData as $data){
- $model = static::createAndSave($data);
- if(!is_null($model)){
- $collection->add($model);
- }
- }
- return $collection;
- }
- /**
- * Create a model from mysqli result
- *
- * @param mysqli_result $result
- *
- * @return static|null
- */
- public static function createFromDbResult($result)
- {
- if($result->num_rows === 0){
- return null;
- }
- return static::create($result->fetch_assoc());
- }
- /**
- * Create a model collection from mysqli result
- *
- * @param mysqli_result $result
- *
- * @return ModelCollection|static[]
- */
- public static function createMultipleFromDbResult($result)
- {
- return static::createMultiple($result->fetch_all(MYSQLI_ASSOC));
- }
- /**
- * @param string $attribute
- * @param mixed $value
- */
- public function setAttribute($attribute, $value)
- {
- $this->data[$attribute] = $value;
- }
- /**
- * @param string $attribute
- *
- * @return mixed|null
- */
- public function getAttribute($attribute)
- {
- if(isset($this->data[$attribute])){
- if(is_array($this->data[$attribute])){
- return array_map('strip_tags', $this->data[$attribute]);
- }
- else{
- return strip_tags($this->data[$attribute]);
- }
- }
- return null;
- }
- /**
- * @return mixed
- */
- public function getId()
- {
- return $this->getAttribute('id');
- }
- /**
- * @param mixed $value
- */
- protected function setId($value)
- {
- $this->setAttribute('id', $value);
- }
- /**
- * @param array $attributes
- *
- * @return string
- */
- protected static function sqlHelperAttributeList($attributes)
- {
- $sql = "%s";
- $values = array();
- $keywords = array('AS', 'ASC', 'DESC');
- foreach($attributes as $val){
- if(!is_array($val)){
- // raw
- $values[] = $val;
- continue;
- }
- switch(count($val)){
- case 1:
- $values[] = "`{$val[0]}`";
- break;
- case 2:
- if(in_array(strtoupper($val[1]), $keywords)){
- $values[] = "`{$val[0]}` {$val[1]}";
- }
- else{
- $values[] = "`{$val[0]}`.`{$val[1]}`";
- }
- break;
- case 3:
- if(in_array(strtoupper($val[1]), $keywords)){
- $values[] = "`{$val[0]}` {$val[1]} `{$val[2]}`";
- }
- elseif(in_array(strtoupper($val[2]), $keywords)){
- $values[] = "`{$val[0]}`.`{$val[1]}` {$val[2]}";
- }
- break;
- case 4:
- if(in_array(strtoupper($val[1]), $keywords)){
- $values[] = "`{$val[0]}` {$val[1]} `{$val[2]}`.`{$val[3]}`";
- }
- elseif(in_array(strtoupper($val[2]), $keywords)){
- $values[] = "`{$val[0]}`.`{$val[1]}` {$val[2]} `{$val[3]}`";
- }
- else{
- $values[] = "`{$val[0]}`.`{$val[1]}` `{$val[2]}`.`{$val[3]}`";
- }
- break;
- case 5:
- if(in_array(strtoupper($val[2]), $keywords)){
- $values[] = "`{$val[0]}`.`{$val[1]}` {$val[2]} `{$val[3]}`.`{$val[4]}`";
- }
- break;
- }
- }
- return sprintf($sql, implode(', ', $values));
- }
- /**
- * @param mixed $value
- *
- * @return string
- */
- protected static function sqlHelperValue($value)
- {
- global $db;
- if(is_null($value) || (is_string($value) && strtoupper($value) === 'NULL')){
- return "NULL";
- }
- elseif(is_array($value)){
- return static::sqlHelperValueList($value);
- }
- return "'{$db->escape_string($value)}'";
- }
- /**
- * @param array $values
- *
- * @return string
- */
- protected static function sqlHelperValueList($values)
- {
- $sql = "(%s)";
- $sqlValues = array();
- foreach($values as $val){
- $sqlValues[] = static::sqlHelperValue($val);
- }
- return sprintf($sql, implode(', ', $sqlValues));
- }
- /**
- * @param array $conditions
- * array('attr', '=', '3') => "`attr` = '3'"
- * array(
- * array('`attr` = '3') (raw SQL) => `attr` = '3'
- * array('attr', 3) => `attr` = '3'
- * array('attr', '=', '3') => `attr` = '3'
- * array('attr', '<=', 3) => `attr` <= '3'
- * array('attr', 'LIKE', '%asd') => `attr` LIKE '%asd'
- * array('attr', 'IS', null) => `attr` IS NULL
- * array('attr', 'IS NOT', null) => `attr` IS NOT NULL
- * )
- * @param string $conditionConnector AND, OR
- *
- * @return string
- */
- protected static function sqlHelperConditionList($conditions, $conditionConnector = 'AND')
- {
- $values = array();
- // detect non nested array
- if(count($conditions) > 0 && !is_array($conditions[0])){
- $conditions = array($conditions);
- }
- $conditionConnector = strtoupper($conditionConnector);
- if(in_array($conditionConnector, array('AND', 'OR'))){
- $conditionConnector = " ".$conditionConnector;
- }
- $sql = "`%s` %s %s";
- foreach($conditions as $val){
- switch(count($val)){
- case 1:
- // raw
- $values[] = $val;
- break;
- case 2:
- $v = static::sqlHelperValue($val[1]);
- $values[] = sprintf($sql, $val[0], "=", $v);
- break;
- case 3:
- $v = static::sqlHelperValue($val[2]);
- $values[] = sprintf($sql, $val[0], strtoupper($val[1]), $v);
- break;
- }
- }
- return implode($conditionConnector." ", $values);
- }
- /**
- * @param array $conditions
- * @param string $conditionConnector AND, OR
- *
- * @return string
- */
- protected static function sqlHelperWhere($conditions, $conditionConnector = 'AND')
- {
- if(count($conditions) > 0){
- $sql = " WHERE %s";
- return sprintf($sql, static::sqlHelperConditionList($conditions, $conditionConnector));
- }
- return "";
- }
- /**
- * @param array|null $orderBy Examples below:
- * null => ""
- * array() => ""
- * array('attr1' => 'asc', 'attr2' => 'desc') => " ORDER BY `attr1` ASC, `attr2` DESC "
- * array('attr1') => " ORDER BY `attr1` ASC "
- *
- * @return string
- */
- protected static function sqlHelperOrderBy($orderBy = null)
- {
- if(!is_null($orderBy) && count($orderBy) > 0){
- $sql = " ORDER BY %s";
- $values = array();
- foreach($orderBy as $key => $val){
- if(is_int($key)){
- $values[] = array($val);
- }
- else{
- $values[] = array($key, strtoupper($val));
- }
- }
- return sprintf($sql, static::sqlHelperAttributeList($values));
- }
- return "";
- }
- /**
- * @param int|array $limit
- * 0 => ""
- * 3 => " LIMIT 3 "
- * array(3, 4) => " LIMIT 3,4 "
- *
- * @return string
- */
- protected static function sqlHelperLimit($limit = 0)
- {
- $sql = " LIMIT %s";
- if(is_string($limit) || (is_int($limit) && $limit > 0)){
- return sprintf($sql, $limit);
- }
- elseif(is_array($limit) && count($limit) == 2){
- return sprintf($sql, $limit[0].",".$limit[1]);
- }
- return "";
- }
- /**
- * Find all models by raw sql
- *
- * @param $sql
- * @param null|string $useSpecificModel
- *
- * @return ModelCollection|static[]
- */
- public static function findAllRaw($sql, $useSpecificModel = null)
- {
- global $db;
- if(!$result = $db->query($sql)){
- dbError($db->error, $sql);
- }
- if(is_null($useSpecificModel)){
- return static::createMultipleFromDbResult($result);
- }
- elseif(class_exists($useSpecificModel)){
- return call_user_func_array(array($useSpecificModel, 'createMultipleFromDbResult'), array($result));
- }
- return new ModelCollection();
- }
- /**
- * Find a model by raw sql
- *
- * @param $sql
- * @param null|string $useSpecificModel
- *
- * @return AbstractModel
- */
- public static function findRaw($sql, $useSpecificModel = null)
- {
- global $db;
- if(!$result = $db->query($sql)){
- dbError($db->error, $sql);
- }
- if(is_null($useSpecificModel)){
- return static::createFromDbResult($result);
- }
- elseif(class_exists($useSpecificModel)){
- return call_user_func_array(array($useSpecificModel, 'createFromDbResult'), array($result));
- }
- return null;
- }
- /**
- * Find all models
- *
- * @param array|null $orderBy see sqlHelperOrderBy
- *
- * @return ModelCollection|static[]
- */
- public static function findAll($orderBy = null)
- {
- $sql = "SELECT * FROM `".static::$table."`"
- .static::sqlHelperOrderBy($orderBy);
- return static::findAllRaw($sql);
- }
- /**
- * Find models by a condition
- *
- * @param array $conditions see sqlHelperConditionArray
- * @param string $conditionConnector see sqlHelperConditionArray
- * @param array|null $orderBy
- * @param int $limit see sqlHelperLimit
- *
- * @return ModelCollection|static[]|AbstractModel|null
- */
- public static function findWhere($conditions = array(), $conditionConnector = 'AND', $orderBy = null, $limit = 0)
- {
- $sql = "SELECT * FROM `".static::$table."`"
- .static::sqlHelperWhere($conditions, $conditionConnector)
- .static::sqlHelperOrderBy($orderBy)
- .static::sqlHelperLimit($limit);
- if($limit === 1){
- return static::findRaw($sql);
- }
- return static::findAllRaw($sql);
- }
- /**
- * Find first model matching a condition
- *
- * @param array $conditions see sqlHelperConditionArray
- * @param string $conditionConnector see sqlHelperConditionArray
- * @param array|null $orderBy
- *
- * @return AbstractModel|null
- */
- public static function findWhereFirst($conditions = array(), $conditionConnector = 'AND', $orderBy = null)
- {
- return static::findWhere($conditions, $conditionConnector, $orderBy, 1);
- }
- /**
- * Find a model by id
- *
- * @param mixed $id
- *
- * @return AbstractModel|null
- */
- public static function find($id)
- {
- return static::findWhereFirst(array(static::$idAttribute, $id));
- }
- /**
- * Save model data to database
- *
- * @return bool
- */
- public function save()
- {
- global $db;
- $data = $this->preSave($this->data);
- if(is_null($this->getId())){
- // insert
- $attributes = array();
- $values = array();
- foreach($this->attributeDbAttributeMapping as $attribute => $sqlAttribute){
- if($sqlAttribute === static::$idAttribute){
- continue;
- }
- $attributes[] = array($sqlAttribute);
- $values[] = $data[$attribute];
- }
- $sql = "INSERT INTO `".static::$table."`"
- ." (".static::sqlHelperAttributeList($attributes).")"
- ." VALUES ".static::sqlHelperValueList($values);
- }
- else{
- // update
- $values = array();
- foreach($this->attributeDbAttributeMapping as $attribute => $sqlAttribute){
- if($sqlAttribute === static::$idAttribute){
- continue;
- }
- $values[] = array($sqlAttribute, '=', $data[$attribute]);
- }
- $sql = "UPDATE `".static::$table."`"
- ." SET ".static::sqlHelperConditionList($values, ',')
- .static::sqlHelperWhere(array(static::$idAttribute, $this->getId()));
- }
- if($stmt = $db->prepare($sql)){
- if($stmt->execute()){
- if(is_null($this->getId())){
- $this->setId(intval($db->insert_id));
- }
- return true;
- }
- else{
- dbError($db->error, $sql);
- }
- }
- return false;
- }
- /**
- * Delete model from database
- *
- * @return bool
- */
- public function delete()
- {
- global $db;
- if(!is_null($this->getId())){
- $sql = "DELETE FROM `".static::$table."`"
- .static::sqlHelperWhere(array(static::$idAttribute, $this->getId()));
- if($stmt = $db->prepare($sql)){
- if($stmt->execute()){
- return true;
- }
- else{
- dbError($db->error, $sql);
- }
- }
- }
- return false;
- }
- }
|