├── .coveralls.yml
├── .travis.yml
├── ActiveRecord.php
├── README.md
├── README.zh-CN.md
├── composer.json
├── doc.sh
├── phpunit.xml
├── test.php
└── test
└── ActiveRecordTest.php
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | service_name: travis-ci
2 | coverage_clover: build/logs/clover.xml
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.4
5 | - 5.5
6 | - 5.6
7 | - 7.0
8 |
9 | before_script:
10 | - composer install --prefer-dist --dev
11 |
12 | script:
13 | - mkdir -p build/logs
14 | - ./vendor/bin/phpunit --coverage-clover build/logs/clover.xml
15 |
16 | after_script:
17 | - php vendor/bin/coveralls -v
18 | - if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.6" ]]; then ./doc.sh; fi
19 |
20 |
--------------------------------------------------------------------------------
/ActiveRecord.php:
--------------------------------------------------------------------------------
1 |
9 | * Using magic function to implement more smarty functions.
10 | * Can using chain method calls, to build concise and compactness program.
11 | */
12 | abstract class ActiveRecord extends Base {
13 | /**
14 | * @var PDO static property to connect database.
15 | */
16 | public static $db;
17 | /**
18 | * @var array maping the function name and the operator, to build Expressions in WHERE condition.
19 | *
user can call it like this: 20 | * $user->isnotnull()->eq('id', 1); 21 | * will create Expressions can explain to SQL: 22 | * WHERE user.id IS NOT NULL AND user.id = :ph123 | */ 24 | public static $operators = array( 25 | 'equal' => '=', 'eq' => '=', 26 | 'notequal' => '<>', 'ne' => '<>', 27 | 'greaterthan' => '>', 'gt' => '>', 28 | 'lessthan' => '<', 'lt' => '<', 29 | 'greaterthanorequal' => '>=', 'ge' => '>=','gte' => '>=', 30 | 'lessthanorequal' => '<=', 'le' => '<=','lte' => '<=', 31 | 'between' => 'BETWEEN', 32 | 'like' => 'LIKE', 33 | 'in' => 'IN', 34 | 'notin' => 'NOT IN', 35 | 'isnull' => 'IS NULL', 36 | 'isnotnull' => 'IS NOT NULL', 'notnull' => 'IS NOT NULL', 37 | ); 38 | /** 39 | * @var array Part of SQL, maping the function name and the operator to build SQL Part. 40 | *
call function like this: 41 | * $user->order('id desc', 'name asc')->limit(2,1); 42 | * can explain to SQL: 43 | * ORDER BY id desc, name asc limit 2,144 | */ 45 | public static $sqlParts = array( 46 | 'select' => 'SELECT', 47 | 'from' => 'FROM', 48 | 'set' => 'SET', 49 | 'where' => 'WHERE', 50 | 'group' => 'GROUP BY','groupby' => 'GROUP BY', 51 | 'having' => 'HAVING', 52 | 'order' => 'ORDER BY','orderby' => 'ORDER BY', 53 | 'limit' => 'limit', 54 | 'top' => 'TOP', 55 | ); 56 | /** 57 | * @var array Static property to stored the default Sql Expressions values. 58 | */ 59 | public static $defaultSqlExpressions = array('expressions' => array(), 'wrap' => false, 60 | 'select'=>null, 'insert'=>null, 'update'=>null, 'set' => null, 'delete'=>'DELETE ', 'join' => null, 61 | 'from'=>null, 'values' => null, 'where'=>null, 'having'=>null, 'limit'=>null, 'order'=>null, 'group' => null); 62 | /** 63 | * @var array Stored the Expressions of the SQL. 64 | */ 65 | protected $sqlExpressions = array(); 66 | /** 67 | * @var string The table name in database. 68 | */ 69 | public $table; 70 | /** 71 | * @var string The primary key of this ActiveRecord, just suport single primary key. 72 | */ 73 | public $primaryKey = 'id'; 74 | /** 75 | * @var array Stored the drity data of this object, when call "insert" or "update" function, will write this data into database. 76 | */ 77 | public $dirty = array(); 78 | /** 79 | * @var array Stored the params will bind to SQL when call PDOStatement::execute(), 80 | */ 81 | public $params = array(); 82 | const BELONGS_TO = 'belongs_to'; 83 | const HAS_MANY = 'has_many'; 84 | const HAS_ONE = 'has_one'; 85 | /** 86 | * @var array Stored the configure of the relation, or target of the relation. 87 | */ 88 | public $relations = array(); 89 | /** 90 | * @var int The count of bind params, using this count and const "PREFIX" (:ph) to generate place holder in SQL. 91 | */ 92 | public static $count = 0; 93 | const PREFIX = ':ph'; 94 | 95 | /** 96 | * function to reset the $params and $sqlExpressions. 97 | * @return ActiveRecord return $this, can using chain method calls. 98 | */ 99 | public function reset() { 100 | $this->params = array(); 101 | $this->sqlExpressions = array(); 102 | return $this; 103 | } 104 | /** 105 | * function to SET or RESET the dirty data. 106 | * @param array $dirty The dirty data will be set, or empty array to reset the dirty data. 107 | * @return ActiveRecord return $this, can using chain method calls. 108 | */ 109 | public function dirty($dirty = array()){ 110 | $this->data = array_merge($this->data, $this->dirty = $dirty); 111 | return $this; 112 | } 113 | /** 114 | * set the DB connection. 115 | * @param PDO $db 116 | */ 117 | public static function setDb($db) { 118 | self::$db = $db; 119 | } 120 | /** 121 | * function to find one record and assign in to current object. 122 | * @param int $id If call this function using this param, will find record by using this id. If not set, just find the first record in database. 123 | * @return bool|ActiveRecord if find record, assign in to current object and return it, other wise return "false". 124 | */ 125 | public function find($id = null) { 126 | if ($id) $this->reset()->eq($this->primaryKey, $id); 127 | return self::_query($this->limit(1)->_buildSql(array('select', 'from', 'join', 'where', 'group', 'having', 'order', 'limit')), $this->params, $this->reset(), true); 128 | } 129 | /** 130 | * function to find all records in database. 131 | * @return array return array of ActiveRecord 132 | */ 133 | public function findAll() { 134 | return self::_query($this->_buildSql(array('select', 'from', 'join', 'where', 'group', 'having', 'order', 'limit')), $this->params, $this->reset()); 135 | } 136 | /** 137 | * function to delete current record in database. 138 | * @return bool 139 | */ 140 | public function delete() { 141 | return self::execute($this->eq($this->primaryKey, $this->{$this->primaryKey})->_buildSql(array('delete', 'from', 'where')), $this->params); 142 | } 143 | /** 144 | * function to build update SQL, and update current record in database, just write the dirty data into database. 145 | * @return bool|ActiveRecord if update success return current object, other wise return false. 146 | */ 147 | public function update() { 148 | if (count($this->dirty) == 0) return true; 149 | foreach($this->dirty as $field => $value) $this->addCondition($field, '=', $value, ',' , 'set'); 150 | if(self::execute($this->eq($this->primaryKey, $this->{$this->primaryKey})->_buildSql(array('update', 'set', 'where')), $this->params)) 151 | return $this->dirty()->reset(); 152 | return false; 153 | } 154 | /** 155 | * function to build insert SQL, and insert current record into database. 156 | * @return bool|ActiveRecord if insert success return current object, other wise return false. 157 | */ 158 | public function insert() { 159 | if (count($this->dirty) == 0) return true; 160 | $value = $this->_filterParam($this->dirty); 161 | $this->insert = new Expressions(array('operator'=> 'INSERT INTO '. $this->table, 162 | 'target' => new WrapExpressions(array('target' => array_keys($this->dirty))))); 163 | $this->values = new Expressions(array('operator'=> 'VALUES', 'target' => new WrapExpressions(array('target' => $value)))); 164 | if (self::execute($this->_buildSql(array('insert', 'values')), $this->params)) { 165 | $this->{$this->primaryKey} = self::$db->lastInsertId(); 166 | return $this->dirty()->reset(); 167 | } 168 | return false; 169 | } 170 | /** 171 | * helper function to exec sql. 172 | * @param string $sql The SQL need to be execute. 173 | * @param array $param The param will be bind to PDOStatement. 174 | * @return bool 175 | */ 176 | public static function execute($sql, $param = array()) { 177 | $res = (($sth = self::$db->prepare($sql)) && $sth->execute($param)); 178 | if (!$res) throw new Exception($sth->errorInfo()[2]); 179 | return $res; 180 | } 181 | /** 182 | * helper function to query one record by sql and params. 183 | * @param string $sql The SQL to find record. 184 | * @param array $param The param will be bind to PDOStatement. 185 | * @param ActiveRecord $obj The object, if find record in database, will assign the attributes in to this object. 186 | * @param bool $single if set to true, will find record and fetch in current object, otherwise will find all records. 187 | * @return bool|ActiveRecord|array 188 | */ 189 | public static function _query($sql, $param = array(), $obj = null, $single=false) { 190 | if ($sth = self::$db->prepare($sql)) { 191 | $called_class = get_called_class(); 192 | $sth->setFetchMode( PDO::FETCH_INTO , ($obj ? $obj : new $called_class )); 193 | $sth->execute($param); 194 | if ($single) return $sth->fetch( PDO::FETCH_INTO ) ? $obj->dirty() : false; 195 | $result = array(); 196 | while ($obj = $sth->fetch( PDO::FETCH_INTO )) $result[] = clone $obj->dirty(); 197 | return $result; 198 | } 199 | return false; 200 | } 201 | /** 202 | * helper function to get relation of this object. 203 | * There was three types of relations: {BELONGS_TO, HAS_ONE, HAS_MANY} 204 | * @param string $name The name of the relation, the array key when defind the relation. 205 | * @return mixed 206 | */ 207 | protected function & getRelation($name) { 208 | $relation = $this->relations[$name]; 209 | if ($relation instanceof self || (is_array($relation) && $relation[0] instanceof self)) 210 | return $relation; 211 | $this->relations[$name] = $obj = new $relation[1]; 212 | if (isset($relation[3]) && is_array($relation[3])) 213 | foreach((array)$relation[3] as $func => $args) 214 | call_user_func_array(array($obj, $func), (array)$args); 215 | $backref = isset($relation[4]) ? $relation[4] : ''; 216 | if ((!$relation instanceof self) && self::HAS_ONE == $relation[0]) 217 | $obj->eq($relation[2], $this->{$this->primaryKey})->find() && $backref && $obj->__set($backref, $this); 218 | elseif (is_array($relation) && self::HAS_MANY == $relation[0]) { 219 | $this->relations[$name] = $obj->eq($relation[2], $this->{$this->primaryKey})->findAll(); 220 | if ($backref) 221 | foreach($this->relations[$name] as $o) 222 | $o->__set($backref, $this); 223 | } elseif ((!$relation instanceof self) && self::BELONGS_TO == $relation[0]) 224 | $obj->eq($obj->primaryKey, $this->{$relation[2]})->find() && $backref && $obj->__set($backref, $this); 225 | else throw new Exception("Relation $name not found."); 226 | return $this->relations[$name]; 227 | } 228 | /** 229 | * helper function to build SQL with sql parts. 230 | * @param string $n The SQL part will be build. 231 | * @param int $i The index of $n in $sqls array. 232 | * @param ActiveRecord $o The refrence to $this 233 | * @return string 234 | */ 235 | private function _buildSqlCallback(&$n, $i, $o){ 236 | if ('select' === $n && null == $o->$n) $n = strtoupper($n). ' '.$o->table.'.*'; 237 | elseif (('update' === $n||'from' === $n) && null == $o->$n) $n = strtoupper($n).' '. $o->table; 238 | elseif ('delete' === $n) $n = strtoupper($n). ' '; 239 | else $n = (null !== $o->$n) ? $o->$n. ' ' : ''; 240 | } 241 | /** 242 | * helper function to build SQL with sql parts. 243 | * @param array $sqls The SQL part will be build. 244 | * @return string 245 | */ 246 | protected function _buildSql($sqls = array()) { 247 | array_walk($sqls, array($this, '_buildSqlCallback'), $this); 248 | //this code to debug info. 249 | //echo 'SQL: ', implode(' ', $sqls), "\n", "PARAMS: ", implode(', ', $this->params), "\n"; 250 | return implode(' ', $sqls); 251 | } 252 | /** 253 | * magic function to make calls witch in function mapping stored in $operators and $sqlPart. 254 | * also can call function of PDO object. 255 | * @param string $name function name 256 | * @param array $args The arguments of the function. 257 | * @return mixed Return the result of callback or the current object to make chain method calls. 258 | */ 259 | public function __call($name, $args) { 260 | if (is_callable($callback = array(self::$db,$name))) 261 | return call_user_func_array($callback, $args); 262 | if (in_array($name = strtolower($name), array_keys(self::$operators))) 263 | $this->addCondition($args[0], self::$operators[$name], isset($args[1]) ? $args[1] : null, (is_string(end($args)) && 'or' === strtolower(end($args))) ? 'OR' : 'AND'); 264 | else if (in_array($name= str_replace('by', '', $name), array_keys(self::$sqlParts))) 265 | $this->$name = new Expressions(array('operator'=>self::$sqlParts[$name], 'target' => implode(', ', $args))); 266 | else throw new Exception("Method $name not exist."); 267 | return $this; 268 | } 269 | /** 270 | * make wrap when build the SQL expressions of WHWRE. 271 | * @param string $op If give this param will build one WrapExpressions include the stored expressions add into WHWRE. otherwise wil stored the expressions into array. 272 | * @return ActiveRecord return $this, can using chain method calls. 273 | */ 274 | public function wrap($op = null) { 275 | if (1 === func_num_args()){ 276 | $this->wrap = false; 277 | if (is_array($this->expressions) && count($this->expressions) > 0) 278 | $this->_addCondition(new WrapExpressions(array('delimiter' => ' ','target'=>$this->expressions)), 'or' === strtolower($op) ? 'OR' : 'AND'); 279 | $this->expressions = array(); 280 | } else $this->wrap = true; 281 | return $this; 282 | } 283 | /** 284 | * helper function to build place holder when make SQL expressions. 285 | * @param mixed $value the value will bind to SQL, just store it in $this->params. 286 | * @return mixed $value 287 | */ 288 | protected function _filterParam($value) { 289 | if (is_array($value)) foreach($value as $key => $val) $this->params[$value[$key] = self::PREFIX. ++self::$count] = $val; 290 | else if (is_string($value)){ 291 | $this->params[$ph = self::PREFIX. ++self::$count] = $value; 292 | $value = $ph; 293 | } 294 | return $value; 295 | } 296 | /** 297 | * helper function to add condition into WHERE. 298 | * create the SQL Expressions. 299 | * @param string $field The field name, the source of Expressions 300 | * @param string $operator 301 | * @param mixed $value the target of the Expressions 302 | * @param string $op the operator to concat this Expressions into WHERE or SET statment. 303 | * @param string $name The Expression will contact to. 304 | */ 305 | public function addCondition($field, $operator, $value, $op = 'AND', $name = 'where') { 306 | $value = $this->_filterParam($value); 307 | if ($exp = new Expressions(array('source'=>('where' == $name? $this->table.'.' : '' ) .$field, 'operator'=>$operator, 'target'=>(is_array($value) 308 | ? new WrapExpressions('between' === strtolower($operator) 309 | ? array('target' => $value, 'start' => ' ', 'end' => ' ', 'delimiter' => ' AND ') 310 | : array('target' => $value) 311 | ) : $value)))) { 312 | if (!$this->wrap) 313 | $this->_addCondition($exp, $op, $name); 314 | else 315 | $this->_addExpression($exp, $op); 316 | } 317 | } 318 | /** 319 | * helper function to add condition into JOIN. 320 | * create the SQL Expressions. 321 | * @param string $table The join table name 322 | * @param string $on The condition of ON 323 | * @param string $type The join type, like "LEFT", "INNER", "OUTER" 324 | */ 325 | public function join($table, $on, $type='LEFT'){ 326 | $this->join = new Expressions(array('source' => $this->join ?: '', 'operator' => $type. ' JOIN', 'target' => new Expressions( 327 | array('source' => $table, 'operator' => 'ON', 'target' => $on) 328 | ))); 329 | return $this; 330 | } 331 | /** 332 | * helper function to make wrapper. Stored the expression in to array. 333 | * @param Expressions $exp The expression will be stored. 334 | * @param string $operator The operator to concat this Expressions into WHERE statment. 335 | */ 336 | protected function _addExpression($exp, $operator) { 337 | if (!is_array($this->expressions) || count($this->expressions) == 0) 338 | $this->expressions = array($exp); 339 | else 340 | $this->expressions[] = new Expressions(array('operator'=>$operator, 'target'=>$exp)); 341 | } 342 | /** 343 | * helper function to add condition into WHERE. 344 | * @param Expressions $exp The expression will be concat into WHERE or SET statment. 345 | * @param string $operator the operator to concat this Expressions into WHERE or SET statment. 346 | * @param string $name The Expression will contact to. 347 | */ 348 | protected function _addCondition($exp, $operator, $name ='where' ) { 349 | if (!$this->$name) 350 | $this->$name = new Expressions(array('operator'=>strtoupper($name) , 'target'=>$exp)); 351 | else 352 | $this->$name->target = new Expressions(array('source'=>$this->$name->target, 'operator'=>$operator, 'target'=>$exp)); 353 | } 354 | /** 355 | * magic function to SET values of the current object. 356 | */ 357 | public function __set($var, $val) { 358 | if (array_key_exists($var, $this->sqlExpressions) || array_key_exists($var, self::$defaultSqlExpressions)) 359 | $this->sqlExpressions[$var] = $val; 360 | else if (array_key_exists($var, $this->relations) && $val instanceof self) 361 | $this->relations[$var] = $val; 362 | else $this->dirty[$var] = $this->data[$var] = $val; 363 | } 364 | /** 365 | * magic function to UNSET values of the current object. 366 | */ 367 | public function __unset($var) { 368 | if (array_key_exists($var, $this->sqlExpressions)) unset($this->sqlExpressions[$var]); 369 | if(isset($this->data[$var])) unset($this->data[$var]); 370 | if(isset($this->dirty[$var])) unset($this->dirty[$var]); 371 | } 372 | /** 373 | * magic function to GET the values of current object. 374 | */ 375 | public function & __get($var) { 376 | if (array_key_exists($var, $this->sqlExpressions)) return $this->sqlExpressions[$var]; 377 | else if (array_key_exists($var, $this->relations)) return $this->getRelation($var); 378 | else return parent::__get($var); 379 | } 380 | } 381 | /** 382 | * base class to stord attributes in one array. 383 | */ 384 | abstract class Base { 385 | /** 386 | * @var array Stored the attributes of the current object 387 | */ 388 | public $data = array(); 389 | public function __construct($config = array()) { 390 | foreach($config as $key => $val) $this->$key = $val; 391 | } 392 | public function __set($var, $val) { 393 | $this->data[$var] = $val; 394 | } 395 | public function & __get($var) { 396 | $result = isset($this->data[$var]) ? $this->data[$var] : null; 397 | return $result; 398 | } 399 | } 400 | /** 401 | * Class Expressions, part of SQL. 402 | * Every SQL can be split into multiple expressions. 403 | * Each expression contains three parts: 404 | * @property string|Expressions $source of this expression, (option) 405 | * @property string $operator (required) 406 | * @property string|Expressions $target of this expression (required) 407 | * Just implement one function __toString. 408 | */ 409 | class Expressions extends Base { 410 | public function __toString() { 411 | return $this->source. ' '. $this->operator. ' '. $this->target; 412 | } 413 | } 414 | /** 415 | * Class WrapExpressions 416 | */ 417 | class WrapExpressions extends Expressions { 418 | public function __toString() { 419 | return ($this->start ? $this->start: '('). implode(($this->delimiter ? $this->delimiter: ','), $this->target). ($this->end?$this->end:')'); 420 | } 421 | } 422 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # activerecord 2 | [](https://travis-ci.org/bephp/activerecord) 3 | [](https://coveralls.io/github/bephp/activerecord?branch=master) 4 | [](https://packagist.org/packages/bephp/activerecord) [](https://packagist.org/packages/bephp/activerecord) [](https://packagist.org/packages/bephp/activerecord) [](https://packagist.org/packages/bephp/activerecord) 5 | 6 | micro activerecord library in PHP(only 400 lines with comments), support chain calls and relations(HAS_ONE, HAS_MANY, BELONGS_TO). 7 | 8 | > [中文版](https://github.com/bephp/activerecord/blob/master/README.zh-CN.md). 9 | 10 | ## Documentation 11 | [Documentation](https://bephp.github.io/activerecord/) 12 | 13 | ## API Reference 14 | ### CRUD functions 15 | #### setDb(\PDO $db) 16 | set the DB connection. 17 | 18 | ActiveRecord::setDb(new PDO('sqlite:test.db')); 19 | 20 | #### insert() : boolean|\ActiveRecord 21 | function to build insert SQL, and insert current record into database. 22 | if insert success return current object, other wise return false. 23 | 24 | $user = new User(); 25 | $user->name = 'demo'; 26 | $user->password = md5('demo'); 27 | $user->insert(); 28 | 29 | #### find(integer $id = null) : boolean|\ActiveRecord 30 | function to find one record and assign in to current object. If call this function using $id param, will find record by using this id. If not set, just find the first record in database. if find record, assign in to current object and return it, other wise return "false". 31 | 32 | $user->notnull('id')->orderby('id desc')->find(); 33 | 34 | #### findAll() : array 35 | function to find all records in database. return array of ActiveRecord 36 | 37 | $user->findAll(); 38 | 39 | #### update() : boolean|\ActiveRecord 40 | function to build update SQL, and update current record in database, just write the dirty data into database. 41 | if update success return current object, other wise return false. 42 | 43 | $user->notnull('id')->orderby('id desc')->find(); 44 | $user->email = 'test@example.com'; 45 | $user->update(); 46 | 47 | #### delete() : boolean 48 | function to delete current record in database. 49 | 50 | #### reset() : \ActiveRecord 51 | function to reset the $params and $sqlExpressions. return $this, can using chain method calls. 52 | 53 | #### dirty(array $dirty = array()) : \ActiveRecord 54 | function to SET or RESET the dirty data. The dirty data will be set, or empty array to reset the dirty data. 55 | 56 | ### SQL part functions 57 | #### select() 58 | function to set the select columns. 59 | 60 | $user->select('id', 'name')->find(); 61 | 62 | #### from() 63 | function to set the table to find record 64 | 65 | $user->select('id', 'name')->from('user')->find(); 66 | 67 | #### join() 68 | function to set the table to find record 69 | 70 | $user->join('contact', 'contact.user_id = user.id')->find(); 71 | 72 | #### where() 73 | function to set where conditions 74 | 75 | $user->where('id=1 AND name="demo"')->find(); 76 | 77 | #### group()/groupby() 78 | 79 | $user->select('count(1) as count')->groupby('name')->findAll(); 80 | 81 | #### order()/orderby() 82 | 83 | $user->orderby('name DESC')->find(); 84 | 85 | #### limit() 86 | 87 | $user->orderby('name DESC')->limit(0, 1)->find(); 88 | 89 | ### WHERE conditions 90 | #### equal()/eq() 91 | 92 | $user->eq('id', 1)->find(); 93 | 94 | #### notequal()/ne() 95 | 96 | $user->ne('id', 1)->find(); 97 | 98 | #### greaterthan()/gt() 99 | 100 | $user->gt('id', 1)->find(); 101 | 102 | #### lessthan()/lt() 103 | 104 | $user->lt('id', 1)->find(); 105 | 106 | #### greaterthanorequal()/ge()/gte() 107 | 108 | $user->ge('id', 1)->find(); 109 | 110 | #### lessthanorequal()/le()/lte() 111 | 112 | $user->le('id', 1)->find(); 113 | 114 | #### like() 115 | 116 | $user->like('name', 'de')->find(); 117 | 118 | #### in() 119 | 120 | $user->in('id', [1, 2])->find(); 121 | 122 | #### notin() 123 | 124 | $user->notin('id', [1,3])->find(); 125 | 126 | #### isnull() 127 | 128 | $user->isnull('id')->find(); 129 | 130 | #### isnotnull()/notnull() 131 | 132 | $user->isnotnull('id')->find(); 133 | 134 | ## Install 135 | 136 | composer require bephp/activerecord 137 | 138 | There's one [Blog demo](https://github.com/bephp/blog), work with [Router](https://github.com/bephp/router) and [MicoTpl](https://github.com/bephp/microtpl). 139 | 140 | ## Demo 141 | ### Include base class ActiveRecord 142 | ```php 143 | include "ActiveRecord.php"; 144 | ``` 145 | ### Define Class 146 | ```php 147 | class User extends ActiveRecord{ 148 | public $table = 'user'; 149 | public $primaryKey = 'id'; 150 | public $relations = array( 151 | 'contacts' => array(self::HAS_MANY, 'Contact', 'user_id'), 152 | 'contact' => array(self::HAS_ONE, 'Contact', 'user_id'), 153 | ); 154 | } 155 | class Contact extends ActiveRecord{ 156 | public $table = 'contact'; 157 | public $primaryKey = 'id'; 158 | public $relations = array( 159 | 'user' => array(self::BELONGS_TO, 'User', 'user_id'), 160 | 'user_with_backref' => array(self::BELONGS_TO, 'User', 'user_id', array(), 'contact'), 161 | // using 5th param to define backref 162 | ); 163 | } 164 | ``` 165 | ### Init data 166 | ```php 167 | ActiveRecord::setDb(new PDO('sqlite:test.db')); 168 | ActiveRecord::execute("CREATE TABLE IF NOT EXISTS user ( 169 | id INTEGER PRIMARY KEY, 170 | name TEXT, 171 | password TEXT 172 | );"); 173 | ActiveRecord::execute("CREATE TABLE IF NOT EXISTS contact ( 174 | id INTEGER PRIMARY KEY, 175 | user_id INTEGER, 176 | email TEXT, 177 | address TEXT 178 | );"); 179 | ``` 180 | ### Insert one User into database. 181 | ```php 182 | $user = new User(); 183 | $user->name = 'demo'; 184 | $user->password = md5('demo'); 185 | var_dump($user->insert()); 186 | ``` 187 | ### Insert one Contact belongs the current user. 188 | ```php 189 | $contact = new Contact(); 190 | $contact->address = 'test'; 191 | $contact->email = 'test1234456@domain.com'; 192 | $contact->user_id = $user->id; 193 | var_dump($contact->insert()); 194 | ``` 195 | ### Example to using relations 196 | ```php 197 | $user = new User(); 198 | // find one user 199 | var_dump($user->notnull('id')->orderby('id desc')->find()); 200 | echo "\nContact of User # {$user->id}\n"; 201 | // get contacts by using relation: 202 | // 'contacts' => array(self::HAS_MANY, 'Contact', 'user_id'), 203 | var_dump($user->contacts); 204 | 205 | $contact = new Contact(); 206 | // find one contact 207 | var_dump($contact->find()); 208 | // get user by using relation: 209 | // 'user' => array(self::BELONGS_TO, 'User', 'user_id'), 210 | var_dump($contact->user); 211 | ``` 212 | 213 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # activerecord 2 | [](https://travis-ci.org/bephp/activerecord) 3 | [](https://coveralls.io/github/bephp/activerecord?branch=master) 4 | [](https://packagist.org/packages/bephp/activerecord) [](https://packagist.org/packages/bephp/activerecord) [](https://packagist.org/packages/bephp/activerecord) [](https://packagist.org/packages/bephp/activerecord) 5 | 6 | 一个微型的ActiveRecord库(包含注释才400行),支持链式调用以及(HAS_ONE, HAS_MANY, BELONGS_TO)三种关联关系 7 | 8 | ## 文档地址 9 | [文档](https://bephp.github.io/activerecord/) 10 | 11 | ## API 12 | ### CRUD 函数 13 | #### setDb(\PDO $db) 14 | 设置数据库连接 15 | 16 | ActiveRecord::setDb(new PDO('sqlite:test.db')); 17 | 18 | #### insert() : boolean|\ActiveRecord 19 | 插入函数,会使用当前对象的值生成插入SQL语句,如果插入成功,返回当前对象,否则返回false 20 | 21 | $user = new User(); 22 | $user->name = 'demo'; 23 | $user->password = md5('demo'); 24 | $user->insert(); 25 | 26 | #### find(integer $id = null) : boolean|\ActiveRecord 27 | 从数据库查找记录,并将记录赋值给当前对象 28 | 如果使用$id参数,则使用这个id来进行查找 29 | 如果查找到记录,则赋值给当前对象,否则返回false 30 | 31 | $user->notnull('id')->orderby('id desc')->find(); 32 | 33 | #### findAll() : array 34 | 查找一个列表数据,返回的数组里面,每一个都是一个ActiveRecord对象 35 | 36 | $user->findAll(); 37 | 38 | #### update() : boolean|\ActiveRecord 39 | 更新当前对象对应的数据库记录,每次更新的时候,只会将改变的值更新到数据库。 40 | 更新成功返回当前对象,否则返回false 41 | 42 | $user->notnull('id')->orderby('id desc')->find(); 43 | $user->email = 'test@example.com'; 44 | $user->update(); 45 | 46 | #### delete() : boolean 47 | 删除当前对象在数据库中对应的记录 48 | 49 | #### reset() : \ActiveRecord 50 | 将$params, $sqlExpressions数组重置 51 | 52 | #### dirty(array $dirty = array()) : \ActiveRecord 53 | 这个函数用来设置或者重置dirty数据 54 | 55 | ### SQL部分帮助函数 56 | #### select() 57 | 设置需要查找的字段 58 | 59 | $user->select('id', 'name')->find(); 60 | 61 | #### from() 62 | 设置查找的表 63 | 64 | $user->select('id', 'name')->from('user')->find(); 65 | 66 | #### join() 67 | 使用join函数设置连接表查询 68 | 69 | $user->join('contact', 'contact.user_id = user.id')->find(); 70 | 71 | #### where() 72 | 设置where条件 73 | 74 | $user->where('id=1 AND name="demo"')->find(); 75 | 76 | #### group()/groupby() 77 | 78 | $user->select('count(1) as count')->groupby('name')->findAll(); 79 | 80 | #### order()/orderby() 81 | 82 | $user->orderby('name DESC')->find(); 83 | 84 | #### limit() 85 | 86 | $user->orderby('name DESC')->limit(0, 1)->find(); 87 | 88 | ### WHERE 条件 89 | #### equal()/eq() 90 | 91 | $user->eq('id', 1)->find(); 92 | 93 | #### notequal()/ne() 94 | 95 | $user->ne('id', 1)->find(); 96 | 97 | #### greaterthan()/gt() 98 | 99 | $user->gt('id', 1)->find(); 100 | 101 | #### lessthan()/lt() 102 | 103 | $user->lt('id', 1)->find(); 104 | 105 | #### greaterthanorequal()/ge()/gte() 106 | 107 | $user->ge('id', 1)->find(); 108 | 109 | #### lessthanorequal()/le()/lte() 110 | 111 | $user->le('id', 1)->find(); 112 | 113 | #### like() 114 | 115 | $user->like('name', 'de')->find(); 116 | 117 | #### in() 118 | 119 | $user->in('id', [1, 2])->find(); 120 | 121 | #### notin() 122 | 123 | $user->notin('id', [1,3])->find(); 124 | 125 | #### isnull() 126 | 127 | $user->isnull('id')->find(); 128 | 129 | #### isnotnull()/notnull() 130 | 131 | $user->isnotnull('id')->find(); 132 | 133 | ## 安装 134 | 135 | composer require bephp/activerecord 136 | 137 | 138 | 这里有一个[博客的例子](https://github.com/bephp/blog), 与[Router](https://github.com/bephp/router)以及[MicoTpl](https://github.com/bephp/microtpl)一起组织起来使用。 139 | 140 | ## 例子 141 | ### 包含class ActiveRecord 142 | ```php 143 | include "ActiveRecord.php"; 144 | ``` 145 | ### 定义 Class 146 | ```php 147 | class User extends ActiveRecord{ 148 | public $table = 'user'; 149 | public $primaryKey = 'id'; 150 | public $relations = array( 151 | 'contacts' => array(self::HAS_MANY, 'Contact', 'user_id'), 152 | 'contact' => array(self::HAS_ONE, 'Contact', 'user_id'), 153 | ); 154 | } 155 | class Contact extends ActiveRecord{ 156 | public $table = 'contact'; 157 | public $primaryKey = 'id'; 158 | public $relations = array( 159 | 'user' => array(self::BELONGS_TO, 'User', 'user_id'), 160 | 'user_with_backref' => array(self::BELONGS_TO, 'User', 'user_id', null, 'contact'), 161 | // 使用第五个参数定义一个backref属性,可以在使用反向的关联关系的时候,不用重复查询数据库。 162 | ); 163 | } 164 | ``` 165 | ### 初始化数据 166 | ```php 167 | ActiveRecord::setDb(new PDO('sqlite:test.db')); 168 | ActiveRecord::execute("CREATE TABLE IF NOT EXISTS user ( 169 | id INTEGER PRIMARY KEY, 170 | name TEXT, 171 | password TEXT 172 | );"); 173 | ActiveRecord::execute("CREATE TABLE IF NOT EXISTS contact ( 174 | id INTEGER PRIMARY KEY, 175 | user_id INTEGER, 176 | email TEXT, 177 | address TEXT 178 | );"); 179 | ``` 180 | ### 插入数据 181 | ```php 182 | $user = new User(); 183 | $user->name = 'demo'; 184 | $user->password = md5('demo'); 185 | var_dump($user->insert()); 186 | ``` 187 | ### 插入一个属于当前用户的联系方式 188 | ```php 189 | $contact = new Contact(); 190 | $contact->address = 'test'; 191 | $contact->email = 'test1234456@domain.com'; 192 | $contact->user_id = $user->id; 193 | var_dump($contact->insert()); 194 | ``` 195 | ### 使用关联关系的例子 196 | ```php 197 | $user = new User(); 198 | // find one user 199 | var_dump($user->notnull('id')->orderby('id desc')->find()); 200 | echo "\nContact of User # {$user->id}\n"; 201 | // get contacts by using relation: 202 | // 'contacts' => array(self::HAS_MANY, 'Contact', 'user_id'), 203 | var_dump($user->contacts); 204 | 205 | $contact = new Contact(); 206 | // find one contact 207 | var_dump($contact->find()); 208 | // get user by using relation: 209 | // 'user' => array(self::BELONGS_TO, 'User', 'user_id'), 210 | var_dump($contact->user); 211 | ``` 212 | 213 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bephp/activerecord", 3 | "type": "library", 4 | "description": "micro activerecord library in PHP(only 400 lines with comments), support chain calls and relations(HAS_ONE, HAS_MANY, BELONGS_TO).", 5 | "keywords": ["activerecord", "orm", "pdo", "relation", "micro", "database"], 6 | "homepage": "http://lloydzhou.github.io/activerecord/", 7 | "license": "MIT", 8 | "require": { 9 | "php": ">=5.3.0" 10 | }, 11 | "require-dev": { 12 | "phpdocumentor/phpdocumentor": "^2.8", 13 | "satooshi/php-coveralls":"*", 14 | "phpunit/phpunit": "*" 15 | }, 16 | "autoload": { 17 | "files": [ "ActiveRecord.php" ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /doc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./vendor/bin/phpdoc -t doc -f ActiveRecord.php 4 | cd doc 5 | git init 6 | git config user.name "Travis CI" 7 | git config user.email "lloydzhou@qq.com" 8 | git add . 9 | git commit -m "Deploy to GitHub Pages" 10 | git push --force --quiet "https://$GH_TOKEN@$GH_REF" master:gh-pages > /dev/null 2>&1 11 | 12 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |