├── CHANGELOG
├── README.md
├── config.php
├── dumbledorm.php
├── generate.php
└── test.php
/CHANGELOG:
--------------------------------------------------------------------------------
1 | 0.1 - Dec 18 2010
2 | -added MIT License
3 | -updated and reformatted documentation (thanks to nvartolomei for the first pass)
4 | -fixed bug where certain data may be hydrated incorrectly in Db::hydrate
5 | -added PDO::ERRMODE_EXCEPTION to connection attributes (fixes bug reported by https://github.com/jiminoc)
6 | -added execute method to Db class
7 | -refactored save/delete methods to use new execute() method
8 | -updated test.php to use new execute() method
9 |
10 | 0.1.1 - Dec 31 2010
11 | -Big bugfix on updates that caused updating multiple fields in a single save() to fail
12 | -Fixed bug where ordering in update data fields may be mismatched
13 | -Added new PlainSql class for inserting plain sql such as NOW() or DATE(NOW()) into sql values
14 | -Setting null to a field in php will set the field to NULL in mysql update/insert statement (before only set to empty string)
15 | -Updated the docs with some undocumented functionality
16 | -Updated test.php with more tests for new/previously undocumented functionality
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #DumbledORM
2 | A PHP Novelty ORM
3 |
4 | ##Requirements:
5 | * PHP 5.3+
6 | * All tables must have a single (not composite) primary key called `id` that is an auto-incrementing integer
7 | * All foreign keys must follow the convention of `table_name_id`
8 | * All meta tables must be:
9 | * named `table_name_meta`
10 | * have a foreign key to the corresponding parent table
11 | * have a column called `key`
12 | * have a column called `val`
13 |
14 |
15 | ##Setup:
16 |
17 | 1. Download/clone DumbledORM
18 | 2. Ensure that your config.php has the correct host, user, pass, etc.
19 | 3. When you have set up your database or have made a change, go to the command line and type ```./generate.php```
20 | 4. Add the following lines to your code:
21 |
22 | require('config.php');
23 | require('dumbledorm.php');
24 | require('./model/base.php');
25 |
26 | That's it. There's an autoloader built in for the generated classes.
27 |
28 | ###Database configuration
29 |
30 | The PHP Data objects or PDO extension is used to access mysql and the configuration file `config.php` is home for class
31 | DbConfig the source where DumbledORM finds your database settings.
32 |
33 | You are able to configure the settings for host, port, database, username and password.
34 |
35 | ```
36 | class DbConfig {
37 | const HOST = 'localhost';
38 | const PORT = 3306;
39 | const DBNAME = 'test_database';
40 | const USER = 'root';
41 | const PASSWORD = 'password';
42 | }
43 | ```
44 |
45 | NOTE: On rare occations mysql will not resolve localhost and PDO will attempt to connect to a unix socket,
46 | if this fails you will likely find a PDOException complaining that there is "No such file or directory".
47 | By changing localhost to the ip 127.0.0.1 instead mysql will be able to resolve the host and a connection can be established.
48 |
49 | ###CLI Script generate.php
50 |
51 | DumbledORM includes a PHP script to generate your database schema model classes.
52 |
53 | At the command line type ```./generate.php -h``` for usage
54 |
55 | ```
56 | Generate DumbledORM models.
57 |
58 | Usage:
59 | ./generate.php
60 |
61 |
62 | -h, -?, --help, -help Print this help message
63 | -p, --prefix Prefix generated classes
64 | -d, --dir Output directory for the model instead of the default ./model
65 | ```
66 |
67 |
68 | ###Builder configuration
69 |
70 | To generate the model programatically:
71 |
72 | require('config.php');
73 | require('dumbledorm.php');
74 | Builder::generateBase();
75 |
76 | `Builder::generateBase()` will always overwrite `base.php` but never any generated classes.
77 |
78 | If you want to prefix the classes that are generated:
79 |
80 | Builder::generateBase('myprefix');
81 |
82 | If you want to put the generated classes in a different directory than the default "model":
83 |
84 | Builder::generateBase(null,'mymodeldir/model');
85 |
86 | ###Testing
87 |
88 | DumbledORM includes a simple test script. You can run it from the command line. Just modify the DbConfig in the test script to your params.
89 |
90 | php test.php
91 |
92 | ##Usage
93 |
94 | ####Create a new record
95 | $user = new User(array(
96 | 'name' => 'Jason',
97 | 'email' => 'jasonmoo@me.com',
98 | 'created_at' => new PlainSql('NOW()'),
99 | ));
100 | $user->save();
101 |
102 | ####Load an existing record and modify it
103 | $user = new User(13); // load record with id 13
104 | $user->setName('Jason')->save();
105 |
106 | ####Find a single record and delete it
107 | User::one(array('name' => 'Jason'))->delete();
108 |
109 | ####Find all records matching both fields and delete them all
110 | User::find(array('name' => 'Jason','job' => 'PHP Dev'))->delete();
111 |
112 | ####Find all records matching a query and modify them
113 | // applies setLocation and save to the entire set
114 | PhoneNumber::select('`number` like "607%"')
115 | ->setLocation('Ithaca, NY')
116 | ->setType(null) // sets field to SQL NULL
117 | ->save();
118 |
119 | ####Find all records matching a query and access a single record by id
120 | $users = User::select('`name` like ?',$val);
121 | echo $users[13]->getId(); // 13
122 |
123 | ####Find all records matching a query and iterate over them
124 | foreach (User::select('`name` like ? and `job` IS NOT NULL order by `name`',$val) as $id => $user) {
125 | echo $user->getName().": $id\n"; // Jason: 13
126 | }
127 |
128 | ####Create a related record
129 | $user->create(new PhoneNumber(array(
130 | 'type' => 'home',
131 | 'number' => '607-333-2840',
132 | )))->save();
133 |
134 | ####Fetch a related record and modify it
135 | // fetches a single record only
136 | $user->getPhoneNumber()->setType('work')->save();
137 |
138 | ####Fetch all related records and iterate over them.
139 | // boolean true causes all related records to be fetched
140 | foreach ($user->getPhoneNumber(true) as $ph) {
141 | echo $ph->getType().': '.$ph->getNumber();
142 | }
143 |
144 | ####Fetch all related records matching a query and modify them
145 | $user->getPhoneNumber('`type` = ?',$type)
146 | ->setType($new_type)
147 | ->save()
148 |
149 | ####Set/Get metadata for a record
150 | // set a batch
151 | $user->addMeta(array(
152 | 'background' => 'blue',
153 | 'last_page' => '/',
154 | ));
155 | // set a single
156 | $user->setMeta('background','blue');
157 | // get a single
158 | $user->getMeta('background'); // blue
159 | // metadata saved automatically
160 | $user->save();
161 |
--------------------------------------------------------------------------------
/config.php:
--------------------------------------------------------------------------------
1 |
8 | * @link http://github.com/jasonmoo/DumbledORM
9 | * @package DumbledORM
10 | *
11 | * DumbledORM is a novelty PHP ORM
12 | *
13 | * Copyright (c) 2010 Jason Mooberry
14 | *
15 | * Permission is hereby granted, free of charge, to any person obtaining a copy
16 | * of this software and associated documentation files (the "Software"), to deal
17 | * in the Software without restriction, including without limitation the rights
18 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19 | * copies of the Software, and to permit persons to whom the Software is furnished
20 | * to do so, subject to the following conditions:
21 | *
22 | * The above copyright notice and this permission notice shall be included in all
23 | * copies or substantial portions of the Software.
24 | *
25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31 | * THE SOFTWARE.
32 | *
33 | */
34 |
35 | /**
36 | * exceptional moments defined here
37 | */
38 | class RecordNotFoundException extends Exception {}
39 |
40 | /**
41 | * Class for denoting sql that should be inserted into the query directly without escaping
42 | *
43 | */
44 | final class PlainSql {
45 | private $_sql;
46 | public function __construct($sql) { $this->_sql = $sql; }
47 | public function __toString() { return $this->_sql; }
48 | }
49 |
50 | /**
51 | * Builder class for required for generating base classes
52 | *
53 | */
54 | abstract class Builder {
55 |
56 | /**
57 | * simple cameCasing method
58 | *
59 | * @param string $string
60 | * @return string
61 | */
62 | public static function camelCase($string) {
63 | return ucfirst(preg_replace("/_(\w)/e","strtoupper('\\1')",strtolower($string)));
64 | }
65 |
66 | /**
67 | * simple un_camel_casing method
68 | *
69 | * @param string $string
70 | * @return string
71 | */
72 | public static function unCamelCase($string) {
73 | return strtolower(preg_replace("/(\w)([A-Z])/","\\1_\\2",$string));
74 | }
75 |
76 | /**
77 | * re/generates base classes for db schema
78 | *
79 | * @param string $prefix
80 | * @param string $dir
81 | * @return void
82 | */
83 | public static function generateBase($prefix=null,$dir='model') {
84 | $tables = array();
85 | foreach (Db::query('show tables',null,PDO::FETCH_NUM) as $row) {
86 | foreach (Db::query('show columns from `'.$row[0].'`') as $col) {
87 | if ($col['Key'] === 'PRI') {
88 | $tables[$row[0]]['pk'] = $col['Field']; break;
89 | }
90 | }
91 | }
92 | foreach (array_keys($tables) as $table) {
93 | foreach (Db::query('show columns from `'.$table.'`') as $col) {
94 | if (substr($col['Field'],-3,3) === '_id') {
95 | $rel = substr($col['Field'],0,-3);
96 | if (array_key_exists($rel,$tables)) {
97 | if ($table === "{$rel}_meta") {
98 | $tables[$rel]['meta']['class'] = self::camelCase($table);
99 | $tables[$rel]['meta']['field'] = $col['Field'];
100 | }
101 | $tables[$table]['relations'][$rel] = array('fk' => 'id', 'lk' => $col['Field']);
102 | $tables[$rel]['relations'][$table] = array('fk' => $col['Field'], 'lk' => 'id');
103 | }
104 | }
105 | }
106 | }
107 | $basetables = " $conf) {
109 | $relations = preg_replace('/[\n\t\s]+/','',var_export((array)@$conf['relations'],true));
110 | $meta = isset($conf['meta']) ? "\$meta_class = '{$conf['meta']['class']}', \$meta_field = '{$conf['meta']['field']}'," : '';
111 | $basetables .= "class ".$prefix.self::camelCase($table)."Base extends BaseTable { protected static \$table = '$table', \$pk = '{$conf['pk']}', $meta \$relations = $relations; }\n";
112 | }
113 | @mkdir("./$dir",0777,true);
114 | file_put_contents("./$dir/base.php",$basetables);
115 | foreach (array_keys($tables) as $table) {
116 | $file = "./$dir/$prefix".self::camelCase($table).'.class.php';
117 | if (!file_exists($file)) {
118 | file_put_contents($file,"setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
146 | }
147 | return self::$_pdo;
148 | }
149 |
150 | /**
151 | * execute sql as a prepared statement
152 | *
153 | * @param string $sql
154 | * @param mixed $params
155 | * @return PDOStatement
156 | */
157 | public static function execute($sql,$params=null) {
158 |
159 | $params = is_array($params) ? $params : array($params);
160 |
161 | if ($params) {
162 | // using preg_replace_callback ensures that any inserted PlainSql
163 | // with ?'s in it will not be confused for replacement markers
164 | $sql = preg_replace_callback('/\?/',function($a) use (&$params) {
165 | $a = array_shift($params);
166 | if ($a instanceof PlainSql) {
167 | return $a;
168 | }
169 | $params[] = $a;
170 | return '?';
171 | },$sql);
172 | }
173 | $stmt = self::pdo()->prepare($sql);
174 | $stmt->execute($params);
175 | return $stmt;
176 | }
177 |
178 | /**
179 | * execute sql as a prepared statement and return all records
180 | *
181 | * @param string $query
182 | * @param mixed $params
183 | * @param PDO constant $fetch_style
184 | * @return Array
185 | */
186 | public static function query($query,$params=null,$fetch_style=PDO::FETCH_ASSOC) {
187 | return self::execute($query,$params)->fetchAll($fetch_style);
188 | }
189 |
190 | /**
191 | * run a query and return the results as a ResultSet of BaseTable objects
192 | *
193 | * @param BaseTable $obj
194 | * @param string $query
195 | * @param mixed $params
196 | * @return ResultSet
197 | */
198 | public static function hydrate(BaseTable $obj,$query,$params=null) {
199 | $set = array();
200 | foreach (self::query($query,$params) as $record) {
201 | $clone = clone $obj;
202 | $clone->hydrate($record);
203 | $set[$clone->getId()] = $clone;
204 | }
205 | return new ResultSet($set);
206 | }
207 |
208 | }
209 |
210 | /**
211 | * class to manage result array more effectively
212 | *
213 | */
214 | final class ResultSet extends ArrayIterator {
215 |
216 | /**
217 | * magic method for applying called methods to all members of result set
218 | *
219 | * @param string $method
220 | * @param Array $params
221 | * @return $this
222 | */
223 | public function __call($method,$params=array()) {
224 | foreach ($this as $obj) {
225 | call_user_func_array(array($obj,$method),$params);
226 | }
227 | return $this;
228 | }
229 |
230 | }
231 |
232 | /**
233 | * base functionality available to all objects extending from a generated base class
234 | *
235 | */
236 | abstract class BaseTable {
237 |
238 | protected static
239 | /**
240 | * table name
241 | */
242 | $table,
243 | /**
244 | * primary key
245 | */
246 | $pk,
247 | /**
248 | * table relations array
249 | */
250 | $relations,
251 | /**
252 | * metadata class name
253 | */
254 | $meta_class,
255 | /**
256 | * metadata field
257 | */
258 | $meta_field;
259 |
260 | protected
261 | /**
262 | * record data array
263 | */
264 | $data,
265 | /**
266 | * metadata array
267 | */
268 | $meta,
269 | /**
270 | * relation data array
271 | */
272 | $relation_data,
273 | /**
274 | * record primary key value
275 | */
276 | $id,
277 | /**
278 | * array of data fields that have changed since hydration
279 | */
280 | $changed;
281 |
282 | /**
283 | * search for single record in self::$table
284 | *
285 | * @param Array $constraints
286 | * @return BaseTable
287 | */
288 | final public static function one(Array $constraints) {
289 | return self::select('`'.implode('` = ? and `',array_keys($constraints)).'` = ? limit 1',array_values($constraints))->current();
290 | }
291 |
292 | /**
293 | * search for any number of records in self::$table
294 | *
295 | * @param Array $constraints
296 | * @return ResultSet
297 | */
298 | final public static function find(Array $constraints) {
299 | return self::select('`'.implode('` = ? and `',array_keys($constraints)).'` = ?',array_values($constraints));
300 | }
301 |
302 | /**
303 | * execute a query in self::$table
304 | *
305 | * @param string $qs
306 | * @param mixed $params
307 | * @return ResultSet
308 | */
309 | final public static function select($qs,$params=null) {
310 | return Db::hydrate(new static,'select * from `'.static::$table.'` where '.$qs,$params);
311 | }
312 |
313 | /**
314 | * construct object and load supplied data or fetch data by supplied id
315 | *
316 | * @param mixed $val
317 | */
318 | public function __construct($val=null) {
319 | if (is_array($val)) {
320 | $this->data = $val;
321 | $this->changed = array_flip(array_keys($this->data));
322 | $this->_loadMeta();
323 | } else if (is_numeric($val)) {
324 | if (!$obj = self::one(array(static::$pk => $val))) {
325 | throw new RecordNotFoundException("Nothing to be found with id $val");
326 | }
327 | $this->hydrate($obj->toArray());
328 | }
329 | }
330 |
331 | /**
332 | * most of the magic in here makes it all work
333 | * - handles all getters and setters on columns and relations
334 | *
335 | * @param string $method
336 | * @param Array $params
337 | * @return mixed
338 | */
339 | final public function __call($method,$params=array()) {
340 | $name = Builder::unCamelCase(substr($method,3,strlen($method)));
341 | if (strpos($method,'get')===0) {
342 | if (array_key_exists($name,$this->data)) {
343 | return $this->data[$name];
344 | }
345 | if (isset(static::$relations[$name])) {
346 | $class = substr($method,3,strlen($method));
347 | if (count($params)) {
348 | if ($params[0] === true) {
349 | return @$this->relation_data[$name.'_all'] ?: $this->relation_data[$name.'_all'] = $class::find(array(static::$relations[$name]['fk'] => $this->getId()));
350 | }
351 | $qparams = array_merge(array($this->getId()),(array)@$params[1]);
352 | $qk = md5(serialize(array($name,$params[0],$qparams)));
353 | return @$this->relation_data[$qk] ?: $this->relation_data[$qk] = $class::select('`'.static::$relations[$name]['fk'].'` = ? and '.$params[0],$qparams);
354 | }
355 | return @$this->relation_data[$name] ?: $this->relation_data[$name] = $class::one(array(static::$relations[$name]['fk'] => $this->getId()));
356 | }
357 | }
358 | else if (strpos($method,'set')===0) {
359 | $this->changed[$name] = true;
360 | $this->data[$name] = array_shift($params);
361 | return $this;
362 | }
363 | throw new BadMethodCallException("No amount of magic can make $method work..");
364 | }
365 |
366 | /**
367 | * simple output object data as array
368 | *
369 | * @return Array
370 | */
371 | final public function toArray() {
372 | return $this->data;
373 | }
374 |
375 | /**
376 | * simple output object pk id
377 | *
378 | * @return integer
379 | */
380 | final public function getId() {
381 | return $this->id;
382 | }
383 |
384 | /**
385 | * store supplied data and bring object state to current
386 | *
387 | * @param Array $data
388 | * @return $this
389 | */
390 | final public function hydrate(Array $data) {
391 | $this->id = $data[static::$pk];
392 | $this->data = $data;
393 | $this->_loadMeta();
394 | $this->changed = array();
395 | return $this;
396 | }
397 |
398 | /**
399 | * create an object with a defined relation to this one.
400 | *
401 | * @param BaseTable $obj
402 | * @return BaseTable
403 | */
404 | final public function create(BaseTable $obj) {
405 | return $obj->{'set'.Builder::camelCase(static::$relations[Builder::unCamelCase(get_class($obj))]['fk'])}($this->id);
406 | }
407 |
408 | /**
409 | * insert or update modified object data into self::$table and any associated metadata
410 | *
411 | * @return void
412 | */
413 | public function save() {
414 | if (empty($this->changed)) return;
415 | $data = array_intersect_key($this->data,$this->changed);
416 |
417 | // use proper sql NULL for values set to php null
418 | foreach ($data as $key => $value) {
419 | if ($value === null) {
420 | $data[$key] = new PlainSql('NULL');
421 | }
422 | }
423 |
424 | if ($this->id) {
425 | $query = 'update `'.static::$table.'` set `'.implode('` = ?, `',array_keys($data)).'` = ? where `'.static::$pk.'` = '.$this->id.' limit 1';
426 | }
427 | else {
428 | $query = 'insert into `'.static::$table.'` (`'.implode('`,`',array_keys($data))."`) values (".rtrim(str_repeat('?,',count($data)),',').")";
429 | }
430 | Db::execute($query,array_values($data));
431 | if ($this->id === null) {
432 | $this->id = Db::pdo()->lastInsertId();
433 | }
434 | $this->meta->{'set'.Builder::camelCase(static::$meta_field)}($this->id)->save();
435 | $this->hydrate(self::one(array(static::$pk => $this->id))->toArray());
436 | }
437 |
438 | /**
439 | * delete this object's record from self::$table and any associated meta data
440 | *
441 | * @return void
442 | */
443 | public function delete() {
444 | Db::execute('delete from `'.static::$table.'` where `'.static::$pk.'` = ? limit 1',$this->getId());
445 | $this->meta->delete();
446 | }
447 |
448 | /**
449 | * add an array of key/val to the metadata
450 | *
451 | * @param Array $data
452 | * @return $this
453 | */
454 | public function addMeta(Array $data) {
455 | foreach ($data as $field => $val) {
456 | $this->setMeta($field,$val);
457 | }
458 | return $this;
459 | }
460 |
461 | /**
462 | * set a field of metadata
463 | *
464 | * @param string $field
465 | * @param string $val
466 | * @return $this
467 | */
468 | public function setMeta($field,$val) {
469 | if (empty($this->meta[$field])) {
470 | $meta_class = static::$meta_class;
471 | $this->meta[$field] = new $meta_class(array('key' => $field,'val' => $val));
472 | }
473 | else {
474 | $this->meta[$field]->setVal($val);
475 | }
476 | return $this;
477 | }
478 |
479 | /**
480 | * get a field of metadata
481 | *
482 | * @param string $field
483 | * @return mixed
484 | */
485 | public function getMeta($field) {
486 | return isset($this->meta[$field]) ? $this->meta[$field]->getVal() : null;
487 | }
488 |
489 | /**
490 | * internally fetch and load any associated metadata
491 | *
492 | * @return void
493 | */
494 | private function _loadMeta() {
495 | if (!$meta_class = static::$meta_class) {
496 | return $this->meta = new ResultSet;
497 | }
498 | foreach ($meta_class::find(array(static::$meta_field => $this->getId())) as $obj) {
499 | $meta[$obj->getKey()] = $obj;
500 | }
501 | $this->meta = new ResultSet((array)@$meta);
502 | }
503 |
504 | }
--------------------------------------------------------------------------------
/generate.php:
--------------------------------------------------------------------------------
1 | #!/usr/bin/php
2 |
5 |
6 | Generate DumbledORM models.
7 |
8 | Usage:
9 |
10 |
11 |
12 | -h, -?, --help, -help Print this help message
13 | -p, --prefix Prefix generated classes
14 | -d, --dir Output directory for the model instead of the default ./model
15 |
16 | 'prefix:',
20 | 'd:' => 'dir:',
21 | );
22 | $opt = getopt(implode('', array_keys($params)), $params);
23 |
24 | require('config.php');
25 | require('dumbledorm.php');
26 | Builder::generateBase($opt['p'], ($opt['d'] ? $opt['d'] : 'model'));
27 | }
28 | ?>
29 |
--------------------------------------------------------------------------------
/test.php:
--------------------------------------------------------------------------------
1 | getMessage().')');
34 | }
35 | try {
36 | Db::execute("CREATE TABLE `$dbname`.`user` (
37 | `id` int(11) NOT NULL AUTO_INCREMENT,
38 | `name` varchar(255) NOT NULL,
39 | `email` varchar(255) NOT NULL,
40 | `active` tinyint(4) NOT NULL,
41 | `created_at` datetime NOT NULL,
42 | PRIMARY KEY (`id`)
43 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;");
44 | Db::execute("CREATE TABLE `$dbname`.`phone_number` (
45 | `id` int(11) NOT NULL AUTO_INCREMENT,
46 | `user_id` int(11) NOT NULL,
47 | `number` varchar(255) NOT NULL,
48 | `type` varchar(255) NOT NULL,
49 | `location` varchar(255),
50 | PRIMARY KEY (`id`)
51 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8");
52 | Db::execute("CREATE TABLE `$dbname`.`post` (
53 | `id` int(11) NOT NULL AUTO_INCREMENT,
54 | `user_id` int(11) NOT NULL,
55 | `title` varchar(255) NOT NULL,
56 | `body` text NOT NULL,
57 | PRIMARY KEY (`id`)
58 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8");
59 | Db::execute("CREATE TABLE `$dbname`.`post_meta` (
60 | `id` int(11) NOT NULL AUTO_INCREMENT,
61 | `post_id` int(11) NOT NULL,
62 | `key` varchar(255) NOT NULL,
63 | `val` varchar(255) NOT NULL,
64 | PRIMARY KEY (`id`)
65 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8");
66 | Db::execute("CREATE TABLE `$dbname`.`orphan` (
67 | `id` int(11) NOT NULL AUTO_INCREMENT,
68 | `name` varchar(255) NOT NULL,
69 | `age` varchar(255) NOT NULL,
70 | PRIMARY KEY (`id`)
71 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8");
72 | OrmTest::assertTrue("Creating test database tables.",true);
73 | } catch (Exception $e) {
74 | throw new OrmTestException('Unable to create test tables. Probably a permissions issue. ('.$e->getMessage().')');
75 | }
76 | try {
77 | Builder::generateBase(null,$dbname);
78 | OrmTest::assertTrue("Checking for generated base classes in ./$dbname/base.php.",file_exists("./$dbname/base.php"));
79 | require("./$dbname/base.php");
80 | OrmTest::assertTrue('Checking for generated base classes in php scope.',class_exists('UserBase'));
81 | } catch (Exception $e) {
82 | Db::query("drop database $dbname");
83 | exec("read -p 'Press enter to delete the test directory named: $dbname OR CTRL+C TO ABORT'; rm -rf $dbname");
84 | throw new OrmTestException('Unable to generate model! ('.$e->getMessage().')');
85 | }
86 | try {
87 | foreach (array('Jason','Jim','Jerri','Al') as $name) {
88 | $user = new User(array(
89 | 'name' => $name,
90 | 'email' => "$name@not_a_domain.com",
91 | 'created_at' => new PlainSql('NOW()'),
92 | 'active' => 1,
93 | ));
94 | $user->save();
95 | }
96 | OrmTest::assertTrue('Testing save() on new object.',is_numeric($user->getId()));
97 |
98 | $user->setName("$name Johnson")->save();
99 | OrmTest::assertTrue('Testing updating field on hydrated object.',$user->getName() === "$name Johnson");
100 | $user->setName($name)->save();
101 |
102 | $id = $user->getId();
103 |
104 | $user = new User($id);
105 | OrmTest::assertTrue('Testing fetching object by id on new object.',$user->getName() === $name);
106 |
107 | $users = User::select('1=?',1);
108 | OrmTest::assertTrue('Testing fetching objects by select() method.',count($users) === 4 and $users->current()->getName() !== '');
109 | OrmTest::assertTrue('Testing array access by id of results set.',$users[$id]->getName() !== '');
110 |
111 | $users = User::find(array('active'=>1));
112 | OrmTest::assertTrue('Testing fetching objects by find() method.',count($users) === 4 and $users->current()->getName() !== '');
113 |
114 | $user = User::one(array('name' => 'Jason'));
115 | OrmTest::assertTrue('Testing fetching object by one() method.',$user->getName() === 'Jason');
116 |
117 | $phone = $user->create(new PhoneNumber(array(
118 | 'type' => 'home',
119 | 'number' => '607-000-0000',
120 | )));
121 | OrmTest::assertTrue('Testing create new object through create() method.',$phone instanceof PhoneNumber and $phone->getUserId() === $user->getId());
122 |
123 | foreach (array(111,222,333,444,555) as $prefix) {
124 | $user->create(new PhoneNumber(array(
125 | 'type' => 'home',
126 | 'number' => '607-'.$prefix.'-0000',
127 | 'location' => null,
128 | )))->save();
129 | }
130 |
131 | OrmTest::assertTrue('Loading records to test method appication to entire results set.',true);
132 |
133 | PhoneNumber::select('`number` like "607%" and `location` IS NULL order by `number`')
134 | ->setLocation('Ithaca, NY')
135 | ->setType('Cell Phone')
136 | ->save();
137 |
138 | $phones = PhoneNumber::find(array('location' => 'Ithaca, NY','type' => 'Cell Phone'));
139 | OrmTest::assertTrue('Checking to see if method application applied correctly.',count($phones) === 5);
140 |
141 | OrmTest::assertTrue('Testing getRelationClassName style magic method (singular).',$user->getPhoneNumber()->getLocation() === 'Ithaca, NY');
142 | OrmTest::assertTrue('Testing getRelationClassName style magic method (list) .',count($user->getPhoneNumber(true)) === 5 and $user->getPhoneNumber(true)->current()->getLocation() === 'Ithaca, NY');
143 | OrmTest::assertTrue('Testing getRelationClassName style magic method (custom) .',$user->getPhoneNumber('`number` like ?',"%111%")->current()->getNumber() === '607-111-0000');
144 |
145 |
146 | $post = $user->create(new Post(array(
147 | 'title' => 'test post',
148 | 'body' => "I ain't got no body..",
149 | )));
150 | $post->addMeta(array(
151 | 'background' => 'blue',
152 | 'border' => '1px solid grey',
153 | ));
154 | $post->setMeta('border','2px solid white');
155 | OrmTest::assertTrue('Testing addMeta(), setMeta() and getMeta() methods.',$post->getMeta('background') === 'blue' and $post->getMeta('border') === '2px solid white');
156 |
157 | $post->save();
158 | $post = $user->getPost();
159 | OrmTest::assertTrue('Testing getMeta() methods after save().',$post->getMeta('background') === 'blue' and $post->getMeta('border') === '2px solid white');
160 |
161 | $post->delete();
162 | OrmTest::assertTrue('Testing delete() method on an object.',count($user->getPost(true)) === 0);
163 |
164 | OrmTest::assertTrue('Testing complete. ('.OrmTest::$fails.' fails)',!OrmTest::$fails);
165 |
166 | Db::execute("drop database $dbname");
167 | exec("read -p 'Press enter to delete the test directory named: $dbname OR CTRL+C TO ABORT'; rm -rf $dbname");
168 | } catch (Exception $e) {
169 | Db::execute("drop database $dbname");
170 | exec("read -p 'Press enter to delete the test directory named: $dbname OR CTRL+C TO ABORT'; rm -rf $dbname");
171 | throw new OrmTestException('Testing failed with a quickness! ('.$e->getMessage().')');
172 | }
--------------------------------------------------------------------------------