├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── examples ├── error_callback.php └── sakila │ ├── _config.php │ ├── customers.php │ └── films_by_category.php ├── src └── database │ ├── DB.php │ ├── Query.php │ └── Statement.php └── test.php /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | 3 | /vendor/ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Aleksandar Babic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PDO wrapper with query builder 2 | ========================== 3 | 4 | PDO wrapper extends PDO and PDOStatement classes and add some nice methods as insert/update/delete and so on. Also, there is very useful SQL query builder. 5 | 6 | API 7 | --- 8 | Because library extends [PDO driver](http://php.net/manual/en/book.pdo.php), you can use all of native PDO methods and new additional: 9 | ### DB - The database class 10 | + `insert` - insert object or array as row to database table (optionaly: using prepared statement) 11 | + `update` - update existent row in database table (optionaly: using prepared statement) 12 | + `replace` - insert or replace (using REPLACE table... syntax) 13 | + `save` - save data to table (method determinate does insert or update will be used) 14 | + `delete` - delete row(s) in database table 15 | + `count` - shortcut for SELECT COUNT(*) statement 16 | + `select` - query build object 17 | + `createQuery` - create new query builder 18 | + `getColumnsFromTable` - all columns from table as array 19 | 20 | ### Statement 21 | + `fetchInto` - fetch row into object (optionaly: only from specific table) 22 | + `fetchIntoFromLastRow` - fetch another object from last row (based on table name) 23 | + `fetchCollection` - fetch collection of objects (custom defined object or stdClass) 24 | + `getColumnValue` - value from specific column 25 | 26 | ### Query - Build SQL query object 27 | + `select` - statement for SELECT 28 | + `from` - statement for FROM 29 | + `where` - adding new WHERE statement. Multiple where will be joined by AND 30 | + `whereIn` - adding WHERE IN (...) statement 31 | + `whereNotIn` - adding WHERE NOT IN (...) statement 32 | + `having` - statement for HAVING 33 | + `join` - join table syntax 34 | + `groupBy` - GROUP BY statement 35 | + `orderBy` - ORDER BY statement 36 | + `limit` - LIMIT statement 37 | + `getQuery` - buld and return query string 38 | + `execute` - execute query 39 | 40 | See more information about [how to use database query builder](https://github.com/salebab/database/wiki/How-to-use-Database-query-builder). 41 | 42 | Usage examples 43 | ----------------- 44 | 45 | ### Creating database instance 46 | $db = new database\DB("mysql:host=localhost;dbname=YOUR_DB_NAME", "YOUR_DB_USERNAME", "YOUR_DB_PASSWORD"); 47 | 48 | ### Select 49 | Execute query and fetch **User** object: 50 | 51 | class User {} 52 | 53 | $user_id = 1; 54 | $sql = "SELECT * FROM users WHERE user_id = ? AND is_active = ?"; 55 | $user = $db->executeQuery($sql, array($user_id, 1)) 56 | ->fetchInto(new User); // or ->fetchObject("User") as in standard PDO driver 57 | 58 | If you need a collection of **User** objects, you can use `fetchCollection` method: 59 | 60 | $users = $db->executeQuery($sql, array($user_id, 1)) 61 | ->fetchCollection(new User); // or ->fetchCollection("User"); 62 | 63 | 64 | More complex, with query builder. You can build 'native' structure of objects. 65 | For example, you can fetch collection of objects **Post** and every **Post** object may have a property `$author` which is a instance of **User** object: 66 | 67 | class User 68 | { 69 | /** 70 | * Get user's first and last name 71 | * 72 | * @return string 73 | */ 74 | function getName() { 75 | return $this->first_name . " ". $this->last_name; 76 | } 77 | } 78 | 79 | class Post 80 | { 81 | /** 82 | * @var User 83 | */ 84 | public $author; 85 | } 86 | 87 | // Library need FETCH_TABLE_NAMES option for mapping class names and table names 88 | $db->setFetchTableNames(1); 89 | 90 | $sql = $db->select("p.*, u.*") 91 | ->from("posts p") 92 | ->join("INNER JOIN users u USING(user_id)") 93 | ->where("u.user_id = ?", $user_id) 94 | ->orderBy("p.title"); 95 | 96 | $stmt = $sql->execute(); 97 | 98 | /* @var Post[] $post_collection */ 99 | $post_collection = array(); 100 | 101 | // Fetching data into Post object from posts table (p is alias) 102 | while($post = $stmt->fetchInto(new Post, "p")) { 103 | 104 | // fetch User object from users table (u is alias) 105 | $post->author = $stmt->fetchIntoFromLastRow(new User, "u"); 106 | 107 | $post_collection[] = $post; 108 | } 109 | 110 | // You can send $post_collection from model to view in your controller, so here is usage in view 111 | foreach($post_collection as $post) { 112 | echo $post->author->getName(); 113 | } 114 | 115 | ### Insert 116 | Library has `insert` method for easy inserting **array or object** as row to database table. Note that all other properties or elements that not match column names will be ignored. 117 | 118 | $data = array( 119 | "username" => "User 1234", 120 | "email" => "user@example.com", 121 | "mtime" => time() 122 | ); 123 | $db->insert("users", $data); 124 | 125 | ### Insert with prepared statement 126 | Third param for `insert()` method is "unique prepared stmt key". Every insert which have that key will use the same prepared statement. 127 | 128 | foreach($data_array as $data) { 129 | $db->insert("users", $data, "unique_stmt_key"); 130 | } 131 | 132 | ### Update 133 | Some examples of update statement 134 | 135 | $user_id = 1; 136 | $db->update("users", $data, "user_id = ?", $user_id); 137 | $db->update("users", $data, "user_id = ? AND email = ?", array(1, "user@example.com")); 138 | 139 | 140 | ### Saving data 141 | Automatic determination of INSERT or UPDATE. If $data['user_id'] exits it will be UPDATE, otherwise it will be INSERT. 142 | 143 | $db->save("users", $data, "user_id"); // user_id is name of PRIMARY column 144 | 145 | ### More examples 146 | 147 | // Delete row in table 148 | // some as $db->exec("DELETE FROM users WHERE user_id = 1"); 149 | $db->delete("users", "user_id = ?", $user_id); 150 | 151 | // Count rows in table 152 | $count = $db->count("users"); 153 | 154 | /* @var User[] $users Collection of User objects */ 155 | $users = $db->executeQuery("SELECT * FROM users")->fetchCollection(new User); 156 | 157 | [See more examples for Sakila database](https://github.com/salebab/database/tree/master/examples/sakila) -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sorskod/db", 3 | "description": "PDO wrapper. Extends PDO and PDOStatement with useful methods.", 4 | "keywords" : ["database", "pdo", "mysql"], 5 | "license": "MIT", 6 | 7 | "authors": [ 8 | { 9 | "name": "Aleksandar Babic", 10 | "email": "salebab@gmail.com" 11 | } 12 | ], 13 | 14 | "require": { 15 | "php": ">=5.3.0" 16 | }, 17 | 18 | "autoload": { 19 | "psr-0": {"database\\": "src/"} 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/error_callback.php: -------------------------------------------------------------------------------- 1 | "mysql:host=localhost;dbname=sakila", 8 | "username" => "root", 9 | "password" => "root" 10 | )); 11 | 12 | DB::registerExceptionCallback(function(Exception $e) { 13 | echo "Error callback: ". $e->getMessage(); 14 | }); 15 | 16 | DB::getInstance(); -------------------------------------------------------------------------------- /examples/sakila/_config.php: -------------------------------------------------------------------------------- 1 | setFetchTableNames(1); 15 | $stmt = $db->select("c.customer_id, c.first_name, c.last_name") 16 | ->from("customer c") 17 | ->limit(5) 18 | 19 | //customer address 20 | ->select("ca.address, ca.postal_code, ca.phone") 21 | ->join("INNER JOIN address ca ON ca.address_id = c.address_id") 22 | 23 | // customer city 24 | ->select("city.city") 25 | ->join("INNER JOIN city ON city.city_id = ca.city_id") 26 | 27 | //store address 28 | ->select("s.store_id, sa.address") 29 | ->join("INNER JOIN store s ON s.store_id = c.store_id") 30 | ->join("INNER JOIN address sa ON sa.address_id = s.address_id") 31 | 32 | ->where("c.customer_id > ?", 120) 33 | ->where("c.customer_id < ?", 345) 34 | ->execute(); 35 | 36 | $customers = array(); 37 | 38 | while($customer = $stmt->fetchInto(new Customer, "c")) { 39 | $customer->address = $stmt->fetchIntoFromLastRow(new Address, "ca"); 40 | $customer->address->city = $stmt->fetchIntoFromLastRow(new City, "city"); 41 | 42 | $customer->store = new Store; 43 | $stmt->fetchIntoFromLastRow($customer->store, "s"); 44 | $stmt->fetchIntoFromLastRow($customer->store, "sa"); 45 | 46 | 47 | $customers[] = $customer; 48 | } 49 | $db->setFetchTableNames(0); // reset to default 50 | echo "
";
51 | print_r($customers);
52 | echo "
"; 53 | -------------------------------------------------------------------------------- /examples/sakila/films_by_category.php: -------------------------------------------------------------------------------- 1 | select("f.film_id, f.title") 17 | ->from("film f") 18 | ->join("INNER JOIN film_category fc ON fc.film_id = f.film_id") 19 | ->join("INNER JOIN category c ON c.category_id = fc.category_id") 20 | ->where("c.name = ?", $category_name) 21 | ->limit(5) 22 | ->execute(); 23 | 24 | while($film = $stmt->fetchInto(new Film)) { 25 | var_dump($film); 26 | } 27 | -------------------------------------------------------------------------------- /src/database/DB.php: -------------------------------------------------------------------------------- 1 | array("database\\Statement", array($this)), 111 | \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, 112 | ); 113 | 114 | try { 115 | // We're using @ because PDO produces Warning before PDOException. 116 | @parent::__construct($dsn, $username, $password, $options); 117 | } catch (\Exception $e) { 118 | if(null !== self::$exception_callback && is_callable(self::$exception_callback)) { 119 | call_user_func_array(self::$exception_callback, array($e)); 120 | } else { 121 | throw $e; 122 | } 123 | } 124 | } 125 | 126 | /** 127 | * Check if columns exists for INSERT or UPDATE 128 | * To turn ON use checkColumns(true) 129 | * To turn OFF use checkColumns(false) 130 | * 131 | * @param bool|int $set 132 | * @return bool 133 | */ 134 | function checkColumns($set = null) 135 | { 136 | if ($set !== null) { 137 | $this->check_columns = $set; 138 | } 139 | 140 | return $this->check_columns; 141 | } 142 | 143 | /** 144 | * Remove non-existed columns before insert/update 145 | * 146 | * @param string $table 147 | * @param array $data 148 | * @param mixed $stmt_key 149 | * @return array 150 | */ 151 | private function removeNonExistentColumns($table, &$data, $stmt_key = null) 152 | { 153 | if ($this->check_columns) { 154 | 155 | // use previous columns or get new 156 | if (!empty($stmt_key) && empty($this->prev_columns[$stmt_key])) { 157 | $this->prev_columns[$stmt_key] = $this->getColumnsFromTable($table); 158 | $columns = $this->prev_columns[$stmt_key]; 159 | } elseif (!empty($stmt_key) && !empty($this->prev_columns[$stmt_key])) { 160 | $columns = $this->prev_columns[$stmt_key]; 161 | } else { 162 | $columns = $this->getColumnsFromTable($table); 163 | } 164 | 165 | $new_data = array(); 166 | foreach ($columns as $column) { 167 | if (array_key_exists($column, $data)) { 168 | $new_data[$column] = $data[$column]; 169 | } 170 | } 171 | $data = $new_data; 172 | } 173 | return $data; 174 | } 175 | 176 | /** 177 | * Build and Get SET statement 178 | * 179 | * $this->getSetStmt(DB::INSERT, "mytable", array("name" => "John")); 180 | * will return: 181 | * INSERT INTO 182 | * @param string $syntax INSERT, UPDATE, REPLACE 183 | * @param string $table 184 | * @param array $data 185 | * @param null $where 186 | * @return \PDOStatement 187 | */ 188 | private function getSetStmt($syntax, $table, $data, $where = null) 189 | { 190 | $columns = array(); 191 | 192 | foreach (array_keys($data) as $column) { 193 | $columns[] = "`" . $column . "` = ?"; 194 | } 195 | $columns = implode(", ", $columns); 196 | 197 | $sql = "$syntax `$table` SET " . $columns . $this->buildWhere($where); 198 | 199 | return $this->prepare($sql); 200 | } 201 | 202 | /** 203 | * Perform INSERT, UPDATE, REPLACE 204 | * 205 | * @param string $syntax 206 | * @param string $table 207 | * @param array $data 208 | * @param null $where 209 | * @param array $where_params 210 | * @param null $stmt_key 211 | * @return Statement|\PDOStatement 212 | */ 213 | private function executeBySyntax($syntax, $table, $data, $where = null, $where_params = array(), $stmt_key = null) 214 | { 215 | if (!is_null($where) && !is_array($where)) { 216 | $where = array($where); 217 | } 218 | 219 | if (is_object($data)) { 220 | $data = (array) $data; 221 | } 222 | 223 | $data = $this->removeNonExistentColumns($table, $data, $stmt_key); 224 | 225 | // support for scalar param 226 | if (!is_array($where_params)) { 227 | $where_params = array($where_params); 228 | } 229 | 230 | if (empty($stmt_key)) { 231 | $stmt = $this->getSetStmt($syntax, $table, $data, $where); 232 | } elseif (empty($this->prev_stmt[$stmt_key])) { 233 | $stmt = $this->getSetStmt($syntax, $table, $data, $where); 234 | $this->prev_stmt[$stmt_key] = $stmt; 235 | } else { 236 | $stmt = $this->prev_stmt[$stmt_key]; 237 | } 238 | 239 | $stmt->execute(array_merge(array_values($data), $where_params)); 240 | 241 | return $stmt; 242 | } 243 | /** 244 | * Insert one row 245 | * 246 | * @throw PDOException 247 | * 248 | * @param string $table 249 | * @param array $data 250 | * @param int|string|null $stmt_key Unique key to use previous prepared stmt 251 | * @return Statement 252 | */ 253 | function insert($table, $data, $stmt_key = null) 254 | { 255 | return $this->executeBySyntax(self::INSERT, $table, $data, null, array(), $stmt_key); 256 | } 257 | 258 | /** 259 | * Update row in table, optionally use previous prepared stmt by stmt_key 260 | * 261 | * @throw PDOException 262 | * 263 | * @param string $table 264 | * @param array $data 265 | * @param mixed $where 266 | * @param mixed|array $where_params 267 | * @param int|string|null $stmt_key Unique key to use previous prepared stmt 268 | * @return Statement 269 | */ 270 | function update($table, $data, $where, $where_params = array(), $stmt_key = null) 271 | { 272 | return $this->executeBySyntax(self::UPDATE, $table, $data, $where, $where_params, $stmt_key); 273 | } 274 | 275 | /** 276 | * Insert or replace row in a table 277 | * 278 | * @throw PDOException 279 | * 280 | * @param string $table 281 | * @param array $data 282 | * @param int|string|null $stmt_key 283 | * @return Statement 284 | */ 285 | function replace($table, $data, $stmt_key = null) 286 | { 287 | return $this->executeBySyntax(self::REPLACE, $table, $data, null, array(), $stmt_key); 288 | } 289 | 290 | /** 291 | * Delete rows from table 292 | * 293 | * @throw PDOException 294 | * 295 | * @param string $table 296 | * @param mixed $where 297 | * @param mixed $where_params 298 | * @return Statement 299 | */ 300 | function delete($table, $where, $where_params) 301 | { 302 | $sql = "DELETE FROM " . $table . $this->buildWhere($where); 303 | $stmt = $this->executeQuery($sql, $where_params); 304 | 305 | return $stmt; 306 | } 307 | 308 | /** 309 | * Count rows in one table - very simple implementation 310 | * 311 | * @param string $table 312 | * @param mixed $where 313 | * @param array $where_params 314 | * @return int 315 | */ 316 | function count($table, $where, $where_params = null) 317 | { 318 | $sql = "SELECT COUNT(*) FROM " . $table . $this->buildWhere($where); 319 | $stmt = $this->executeQuery($sql, $where_params); 320 | 321 | return $stmt->fetchColumn(); 322 | } 323 | 324 | /** 325 | * @deprecated since version 1.0 326 | */ 327 | function executeQuery($sql, $params = null) 328 | { 329 | return $this->execQueryString($sql, $params); 330 | } 331 | 332 | /** 333 | * Prepare & execute query with params 334 | * 335 | * @throw PDOException 336 | * 337 | * @param string $sql 338 | * @param array|null $params 339 | * @return Statement 340 | */ 341 | function execQueryString($sql, $params = null) 342 | { 343 | if (!is_array($params) && !is_null($params)) { 344 | $params = array($params); 345 | } 346 | 347 | $stmt = $this->prepare($sql); 348 | $stmt->execute($params); 349 | return $stmt; 350 | } 351 | 352 | /** 353 | * @param Query $query 354 | * @return Statement 355 | */ 356 | public function execQuery(Query $query) 357 | { 358 | return $this->execQueryString($query->getQuery(), $query->getParams()); 359 | } 360 | 361 | /** 362 | * Build where statement for SQL query 363 | * 364 | * @param mixed $where 365 | * @param string $operand AND | OR 366 | * @return string 367 | */ 368 | function buildWhere($where, $operand = "AND") 369 | { 370 | if (empty($where)) { 371 | return ""; 372 | } 373 | 374 | if (is_array($where)) { 375 | $wheres = array(); 376 | foreach ($where as $k => $w) { 377 | $wheres[] = "(" . $w . ")"; 378 | } 379 | $where = implode(" $operand ", $wheres); 380 | } 381 | 382 | return " WHERE " . $where; 383 | } 384 | 385 | /** 386 | * Get Database Query Builder 387 | * @return Query 388 | */ 389 | function createQuery() 390 | { 391 | return new Query($this); 392 | } 393 | 394 | /** 395 | * Shortcut for createQuery()->select 396 | * 397 | * @param string $statement 398 | * @return Query 399 | */ 400 | function select($statement = "") 401 | { 402 | return $this->createQuery()->select($statement); 403 | } 404 | 405 | /** 406 | * Get all columns from table 407 | * 408 | * @throw PDOException 409 | * 410 | * @param $table 411 | * @return array 412 | */ 413 | function getColumnsFromTable($table) 414 | { 415 | $sql = "DESCRIBE $table"; 416 | 417 | return $this->executeQuery($sql) 418 | ->fetchAll(self::FETCH_COLUMN); 419 | } 420 | 421 | /** 422 | * Save data to table 423 | * 424 | * @throw PDOException 425 | * 426 | * @param string $table 427 | * @param array $data 428 | * @param string $primary_key Name of primary key column 429 | * @param string|int $stmt_key 430 | * @return Statement 431 | */ 432 | function save($table, $data, $primary_key, $stmt_key = null) 433 | { 434 | // Update if primary key exists in data set or insert new row 435 | if (!empty($data[$primary_key])) { 436 | return $this->update($table, $data, $primary_key . " = ?", $data[$primary_key], $stmt_key); 437 | } else { 438 | return $this->insert($table, $data, $stmt_key); 439 | } 440 | } 441 | 442 | 443 | /** 444 | * Set fetch table names attribute 445 | * 446 | * @param int $option 1 or 0 447 | */ 448 | function setFetchTableNames($option = 1) 449 | { 450 | $this->setAttribute(self::ATTR_FETCH_TABLE_NAMES, $option); 451 | $this->fetch_table_names = $option; 452 | } 453 | 454 | /** 455 | * Register exception callback 456 | * If connection to server fails, exception will be passed to callback as first param 457 | * @param \Closure $callback 458 | */ 459 | public static function registerExceptionCallback($callback) 460 | { 461 | self::$exception_callback = $callback; 462 | } 463 | } 464 | -------------------------------------------------------------------------------- /src/database/Query.php: -------------------------------------------------------------------------------- 1 | db = $db; 37 | } 38 | 39 | /** 40 | * Add statement for select - SELECT [?] FROM ... 41 | * 42 | * Examples: 43 | * $sql->select("u.*") 44 | * ->select("b.*, COUNT(*) as total") 45 | * 46 | * @param string $statement 47 | * @return Query 48 | */ 49 | public function select($statement) 50 | { 51 | $this->select[] = $statement; 52 | 53 | return $this; 54 | } 55 | 56 | /** 57 | * Add statement for from - SELECT * FROM [?] ... 58 | * 59 | * Examples: 60 | * $sql->from("users"); 61 | * $sql->from("users u, posts p"); 62 | * 63 | * @param string $statement 64 | * @return Query 65 | */ 66 | public function from($statement) 67 | { 68 | $this->from[] = $statement; 69 | 70 | return $this; 71 | } 72 | 73 | /** 74 | * Add statement for where - ... WHERE [?] ... 75 | * 76 | * Examples: 77 | * $sql->where("user_id = ?", $user_id); 78 | * $sql->where("u.registered > ? AND (u.is_active = ? OR u.column IS NOT NULL)", array($registered, 1)); 79 | * 80 | * @param string $statement 81 | * @param mixed $params 82 | * @return Query 83 | */ 84 | public function where($statement, $params = null) 85 | { 86 | $this->where[] = $statement; 87 | $this->addParams($params); 88 | 89 | return $this; 90 | } 91 | 92 | /** 93 | * Add where in statement 94 | * 95 | * @param string $column 96 | * @param array $params 97 | * 98 | * @return Query 99 | */ 100 | public function whereIn($column, $params) 101 | { 102 | $this->prepareWhereInStatement($column, $params, false); 103 | $this->addParams($params); 104 | 105 | return $this; 106 | } 107 | 108 | /** 109 | * Add where not in statement 110 | * 111 | * @param $column 112 | * @param $params 113 | * @return Query 114 | */ 115 | public function whereNotIn($column, $params) 116 | { 117 | $this->prepareWhereInStatement($column, $params, true); 118 | $this->addParams($params); 119 | 120 | return $this; 121 | } 122 | 123 | /** 124 | * Add statement for HAVING ... 125 | * @param string $statement 126 | * @param mixed $params 127 | * @return Query 128 | */ 129 | public function having($statement, $params = null) 130 | { 131 | $this->having[] = $statement; 132 | $this->addParams($params); 133 | 134 | return $this; 135 | } 136 | 137 | /** 138 | * Add statement for join 139 | * 140 | * Examples: 141 | * $sql->join("INNER JOIN posts p ON p.user_id = u.user_id") 142 | * 143 | * @param string $statement 144 | * @return Query 145 | */ 146 | public function join($statement) 147 | { 148 | $this->join[] = $statement; 149 | 150 | return $this; 151 | } 152 | 153 | /** 154 | * Add statement for group - GROUP BY [...] 155 | * 156 | * Examples: 157 | * $sql->groupBy("user_id"); 158 | * $sql->groupBy("u.is_active, p.post_id"); 159 | * 160 | * @param string $statement 161 | * @return Query 162 | */ 163 | public function groupBy($statement) 164 | { 165 | $this->groupBy[] = $statement; 166 | 167 | return $this; 168 | } 169 | 170 | /** 171 | * Add statement for order - ORDER BY [...] 172 | * 173 | * Examples: 174 | * $sql->orderBy("registered"); 175 | * $sql->orderBy("is_active, registered DESC"); 176 | * 177 | * @param string $statement 178 | * @return Query 179 | */ 180 | public function orderBy($statement) 181 | { 182 | $this->orderBy[] = $statement; 183 | 184 | return $this; 185 | } 186 | 187 | /** 188 | * Add statement for limit - LIMIT [...] 189 | * 190 | * Examples: 191 | * $sql->limit(30); 192 | * $sql->limit(30,30); 193 | * 194 | * @param int $limit 195 | * @param int $offset 196 | * @return Query 197 | */ 198 | public function limit($limit, $offset = null) 199 | { 200 | $this->limit = ''; 201 | 202 | if(!is_null($offset)) { 203 | $this->limit = $offset . ', '; 204 | } 205 | 206 | $this->limit .= $limit; 207 | 208 | return $this; 209 | } 210 | 211 | /** 212 | * Returns generated SQL query 213 | * 214 | * @return string 215 | */ 216 | public function getQuery() 217 | { 218 | $sql = $this->prepareSelectString(); 219 | $sql .= $this->prepareJoinString(); 220 | $sql .= $this->prepareWhereString(); 221 | $sql .= $this->prepareGroupByString(); 222 | $sql .= $this->prepareHavingString(); 223 | $sql .= $this->prepareOrderByString(); 224 | $sql .= $this->prepareLimitString(); 225 | 226 | return $sql; 227 | } 228 | 229 | /** 230 | * Returns prepared select string 231 | * 232 | * @return string 233 | */ 234 | private function prepareSelectString() 235 | { 236 | if(empty($this->select)) { 237 | $this->select("*"); 238 | } 239 | 240 | return "SELECT " . implode(", ", $this->select) . " FROM " . implode(", ", $this->from) . " "; 241 | } 242 | 243 | /** 244 | * Execute built query 245 | * This will prepare query, bind params and execute query 246 | * 247 | * @return Statement 248 | */ 249 | public function execute() 250 | { 251 | if($this->db === null) { 252 | $this->db = DB::getInstance(); 253 | } 254 | return $this->db->execQuery($this); 255 | } 256 | 257 | /** 258 | * Clear previous assigned select columns 259 | * @return Query 260 | */ 261 | public function clearSelect() 262 | { 263 | $this->select = array(); 264 | 265 | return $this; 266 | } 267 | 268 | /** 269 | * Clear previous assigned group by 270 | * @return Query 271 | */ 272 | public function clearGroupBy() 273 | { 274 | $this->groupBy = array(); 275 | 276 | return $this; 277 | } 278 | 279 | /** 280 | * Add param(s) to stack 281 | * 282 | * @param array $params 283 | * 284 | * @return void 285 | */ 286 | public function addParams($params) 287 | { 288 | if (is_null($params)) { 289 | return; 290 | } 291 | 292 | if(!is_array($params)) { 293 | $params = array($params); 294 | } 295 | 296 | $this->params = array_merge($this->params, $params); 297 | } 298 | 299 | /** 300 | * @return array 301 | */ 302 | public function getParams() 303 | { 304 | return $this->params; 305 | } 306 | 307 | /** 308 | * Prepares where in statement 309 | * 310 | * @param string $column 311 | * @param array $params 312 | * @param bool $not_in Use NOT IN statement 313 | * 314 | * @return void 315 | */ 316 | private function prepareWhereInStatement($column, $params, $not_in = false) 317 | { 318 | $qm = array_fill(0, count($params), "?"); 319 | $in = ($not_in) ? "NOT IN" : "IN"; 320 | $this->where[] = $column . " " . $in . " (" . implode(", ", $qm) . ")"; 321 | } 322 | 323 | /** 324 | * Returns prepared join string 325 | * 326 | * @return string 327 | */ 328 | private function prepareJoinString() 329 | { 330 | if (!empty($this->join)) { 331 | return implode(" ", $this->join) . " "; 332 | } 333 | 334 | return ''; 335 | } 336 | 337 | /** 338 | * Returns prepared where string 339 | * 340 | * @return string 341 | */ 342 | private function prepareWhereString() 343 | { 344 | if (!empty($this->where)) { 345 | return "WHERE " . implode(" AND ", $this->where) . " "; 346 | } 347 | 348 | return ''; 349 | } 350 | 351 | /** 352 | * Returns prepared group by string 353 | * 354 | * @return string 355 | */ 356 | private function prepareGroupByString() 357 | { 358 | if (!empty($this->groupBy)) { 359 | return "GROUP BY " . implode(", ", $this->groupBy) . " "; 360 | } 361 | 362 | return ''; 363 | } 364 | 365 | /** 366 | * Returns prepared having string 367 | * 368 | * @return string 369 | */ 370 | private function prepareHavingString() 371 | { 372 | if (!empty($this->having)) { 373 | return "HAVING " . implode(", ", $this->having) . " "; 374 | } 375 | 376 | return ''; 377 | } 378 | 379 | /** 380 | * Returns prepared order by string 381 | * 382 | * @return string 383 | */ 384 | private function prepareOrderByString() 385 | { 386 | if (!empty($this->orderBy)) { 387 | return "ORDER BY " . implode(", ", $this->orderBy) . " "; 388 | } 389 | 390 | return ''; 391 | } 392 | 393 | /** 394 | * Returns prepared limit string 395 | * 396 | * @return string 397 | */ 398 | private function prepareLimitString() 399 | { 400 | if (!empty($this->limit)) { 401 | return "LIMIT " . $this->limit; 402 | } 403 | 404 | return ''; 405 | } 406 | } 407 | -------------------------------------------------------------------------------- /src/database/Statement.php: -------------------------------------------------------------------------------- 1 | db = $db; 34 | } 35 | 36 | 37 | /** 38 | * Fetch data into object's properties. 39 | * If $from_table is defined, only data from that table will be assigned 40 | * 41 | * Note: After value is assigned to property, it will be unset from last_row 42 | * 43 | * @param object $object 44 | * @param string $from_table If isn't used, method will return all data in one object 45 | * @param int $fetch_from Fetch data from next or last fetched row. DB::FETCH_FROM_NEXT_ROW or DB::FETCH_FROM_LAST_ROW 46 | * @return object|NULL 47 | */ 48 | function fetchInto($object, $from_table = "", $fetch_from = DB::FETCH_FROM_NEXT_ROW) 49 | { 50 | if ($from_table == "") { 51 | $this->db->setFetchTableNames(0); 52 | $this->setFetchMode(DB::FETCH_INTO, $object); 53 | return $this->fetch(); 54 | } elseif ($fetch_from == DB::FETCH_FROM_NEXT_ROW) { 55 | $this->setFetchMode(DB::FETCH_ASSOC); 56 | $this->last_row = $this->fetch(); 57 | } 58 | 59 | if (empty($this->last_row)) { 60 | return null; 61 | } 62 | 63 | $table = ""; 64 | // Copy values of last_row to object's properties 65 | foreach ($this->last_row as $key => $value) { 66 | 67 | if ($this->db->fetch_table_names) { 68 | list($table, $column) = explode($this->delimiter, $key, 2); 69 | } else { 70 | $column = $key; 71 | } 72 | 73 | // copy 74 | if ($from_table == $table OR empty($table)) { 75 | $object->{$column} = $value; 76 | unset($this->last_row[$key]); 77 | } 78 | // For aliases or functions (count()), assign to first object 79 | // example: .store_total_books become store.total_books 80 | elseif ($from_table != "" && $table == "" && substr($key, 1, strlen($from_table)) == $from_table) { 81 | $column = substr($key, strlen($from_table) + 2); 82 | $object->{$column} = $value; 83 | unset($this->last_row[$key]); 84 | } 85 | } 86 | 87 | return $object; 88 | } 89 | 90 | /** 91 | * Fetch data into object from last fetched row. 92 | * This is shortcut for fetchInto($object, $from_table, DB::FETCH_FROM_LAST_ROW); 93 | * 94 | * @param object $object 95 | * @param string $from_table 96 | * @return object|NULL 97 | */ 98 | function fetchIntoFromLastRow($object, $from_table) 99 | { 100 | return $this->fetchInto($object, $from_table, DB::FETCH_FROM_LAST_ROW); 101 | } 102 | 103 | /** 104 | * Fetch collection of objects (do the some thing as fetchAll) 105 | * 106 | * @param string|object $class_name 107 | * @return array 108 | */ 109 | function fetchCollection($class_name = "stdClass") 110 | { 111 | /* backward compatibility, you can use object instead of class name */ 112 | if (is_object($class_name)) { 113 | $class_name = get_class($class_name); 114 | } 115 | 116 | return $this->fetchAll(DB::FETCH_CLASS, $class_name); 117 | } 118 | 119 | /** 120 | * Get value from column, from last row 121 | * 122 | * @param string $column_name 123 | * @return mixed|NULL 124 | */ 125 | function getColumnValue($column_name) 126 | { 127 | return isset($this->last_row[$column_name]) ? $this->last_row[$column_name] : null; 128 | } 129 | 130 | function closeCursor() 131 | { 132 | $this->last_row = null; 133 | return parent::closeCursor(); 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /test.php: -------------------------------------------------------------------------------- 1 | "mysql:host=localhost;dbname=mobjizz", 7 | "username" => "root", 8 | "password" => "root" 9 | )); 10 | 11 | $db = \database\DB::getInstance("set"); --------------------------------------------------------------------------------