diff options
author | Roman Tsiupa <draconyster@gmail.com> | 2013-01-18 03:32:22 +0200 |
---|---|---|
committer | Roman Tsiupa <draconyster@gmail.com> | 2013-01-18 23:09:23 +0200 |
commit | 3981b31aacc93b68285f74e2151a165f56d52bb7 (patch) | |
tree | 63e9e85b668b5f4c386bc0ad643c20cfc7de33b4 | |
parent | 6a220e18515a29f11ee4ae5818aecd22baf3c1d1 (diff) | |
download | PHPixie-3981b31aacc93b68285f74e2151a165f56d52bb7.zip PHPixie-3981b31aacc93b68285f74e2151a165f56d52bb7.tar.gz PHPixie-3981b31aacc93b68285f74e2151a165f56d52bb7.tar.bz2 |
ORM Module finished
-rw-r--r-- | application/classes/controller/home.php | 5 | ||||
-rw-r--r-- | application/classes/model/fairy.php | 2 | ||||
-rw-r--r-- | application/classes/model/flower.php | 5 | ||||
-rw-r--r-- | application/classes/model/tree.php | 1 | ||||
-rw-r--r-- | application/config/database.php | 2 | ||||
-rw-r--r-- | modules/database/classes/database/query.php | 17 | ||||
-rw-r--r-- | modules/database/classes/db.php | 7 | ||||
-rw-r--r-- | modules/database/classes/driver/mysql/db.php | 19 | ||||
-rw-r--r-- | modules/database/classes/driver/pdo/db.php | 8 | ||||
-rw-r--r-- | modules/database/classes/driver/pdo/query.php | 6 | ||||
-rw-r--r-- | modules/orm/classes/orm.php | 175 | ||||
-rw-r--r-- | modules/orm/classes/ormresult.php | 49 |
12 files changed, 218 insertions, 78 deletions
diff --git a/application/classes/controller/home.php b/application/classes/controller/home.php index 3b9dbf3..7ec6e86 100644 --- a/application/classes/controller/home.php +++ b/application/classes/controller/home.php @@ -2,7 +2,10 @@ class Home_Controller extends Controller {
public function action_index(){
- print_r(ORM::factory('fairy')->with('tree')->find_all()->as_array(true));
+ foreach(ORM::factory('fairy')->with ('tree.protector','tree.flower.protector')->find_all() as $fairy) {
+ echo $fairy->tree->protector->name;
+
+ }
}
}
diff --git a/application/classes/model/fairy.php b/application/classes/model/fairy.php index 31a942a..e5a8678 100644 --- a/application/classes/model/fairy.php +++ b/application/classes/model/fairy.php @@ -1,4 +1,4 @@ <?php
class Fairy_Model extends ORM{
- public $belongs_to=array('tree');
+ public $belongs_to=array('tree','flower');
}
\ No newline at end of file diff --git a/application/classes/model/flower.php b/application/classes/model/flower.php new file mode 100644 index 0000000..b817a7d --- /dev/null +++ b/application/classes/model/flower.php @@ -0,0 +1,5 @@ +<?php
+class Flower_Model extends ORM{
+ public $has_many=array('fairies');
+ public $belongs_to=array('protector'=>array('model'=>'fairy','key'=>'protector_id'));
+}
\ No newline at end of file diff --git a/application/classes/model/tree.php b/application/classes/model/tree.php index 0c4973d..721372e 100644 --- a/application/classes/model/tree.php +++ b/application/classes/model/tree.php @@ -1,4 +1,5 @@ <?php
class Tree_Model extends ORM{
public $has_many=array('fairies');
+ public $belongs_to=array('protector'=>array('model'=>'fairy','key'=>'protector_id'),'flower');
}
\ No newline at end of file diff --git a/application/config/database.php b/application/config/database.php index 0295f13..3a04f14 100644 --- a/application/config/database.php +++ b/application/config/database.php @@ -4,7 +4,7 @@ return array( 'default' => array(
'user'=>'root',
'password' => '',
- 'driver' => 'pdo',
+ 'driver' => 'mysql',
//'Connection' is required if you use the PDO driver
'connection'=>'mysql:host=localhost;dbname=phpixie',
diff --git a/modules/database/classes/database/query.php b/modules/database/classes/database/query.php index 984949b..a07a7ff 100644 --- a/modules/database/classes/database/query.php +++ b/modules/database/classes/database/query.php @@ -148,9 +148,14 @@ abstract class Query_Database { }
/**
- * Sets fields to be queried from the database
- *
- * @param mixed A single field or an array of them
+ * Sets fields to be queried from the database. You can add aliases to the fields
+ * by passing them as:
+ *
+ * array('field_name','alias')
+ *
+ * Example: $query->fields('id', array('name', 'fairy_name'))
+ *
+ * @param mixed $field,... Fields to be selected from the table
* @return mixed If no parameters are passed returns current array of fields,
* otherwise returns self.
* @access public
@@ -159,10 +164,8 @@ abstract class Query_Database { $p = func_get_args();
if (empty($p)) {
return $this->_fields;
- }elseif (is_array($p[0])) {
- $this->_fields=$p[0];
- }else {
- $this->_fields=array($p[0]);
+ }else{
+ $this->_fields=$p;
}
return $this;
}
diff --git a/modules/database/classes/db.php b/modules/database/classes/db.php index dc6934f..0d9ef94 100644 --- a/modules/database/classes/db.php +++ b/modules/database/classes/db.php @@ -45,6 +45,13 @@ abstract class DB { */
public abstract function get_insert_id();
+ /**
+ * Gets column names for the specified table
+ *
+ * @param string $table Name of the table to get columns from
+ * @return array Array of column names
+ * @access public
+ */
public abstract function list_columns($table);
/**
diff --git a/modules/database/classes/driver/mysql/db.php b/modules/database/classes/driver/mysql/db.php index e5a63df..ab0a3bb 100644 --- a/modules/database/classes/driver/mysql/db.php +++ b/modules/database/classes/driver/mysql/db.php @@ -36,7 +36,24 @@ class DB_Mysql_Driver extends DB{ Config::get("database.{$config}.db")
);
}
-
+
+ /**
+ * Gets column names for the specified table
+ *
+ * @param string $table Name of the table to get columns from
+ * @return array Array of column names
+ * @access public
+ */
+ public function list_columns($table) {
+ $columns=array();
+ $table_desc = $this->execute("DESCRIBE `$table`");
+
+ foreach($table_desc as $column)
+ $columns[] = $column->Field;
+
+ return $columns;
+ }
+
/**
* Builds a new Query implementation
*
diff --git a/modules/database/classes/driver/pdo/db.php b/modules/database/classes/driver/pdo/db.php index c73995d..b7b869f 100644 --- a/modules/database/classes/driver/pdo/db.php +++ b/modules/database/classes/driver/pdo/db.php @@ -61,6 +61,13 @@ class DB_PDO_Driver extends DB{ return $this->conn->lastInsertId();
}
+ /**
+ * Gets column names for the specified table
+ *
+ * @param string $table Name of the table to get columns from
+ * @return array Array of column names
+ * @access public
+ */
public function list_columns($table) {
$columns=array();
if ($this->db_type == 'mysql') {
@@ -80,6 +87,7 @@ class DB_PDO_Driver extends DB{ }
return $columns;
}
+
/**
* Executes a prepared statement query
*
diff --git a/modules/database/classes/driver/pdo/query.php b/modules/database/classes/driver/pdo/query.php index 647b0c4..f029197 100644 --- a/modules/database/classes/driver/pdo/query.php +++ b/modules/database/classes/driver/pdo/query.php @@ -127,7 +127,11 @@ class Query_PDO_Driver extends Query_Database { }else {
$first = false;
}
- $query.="{$this->escape_field($f)} ";
+ if(is_array($f)){
+ $query.= "{$this->escape_field($f[0])} AS {$f[1]} ";
+ }else {
+ $query.= "{$this->escape_field($f)} ";
+ }
}
}
$query.= "FROM {$this->quote($this->_table)} ";
diff --git a/modules/orm/classes/orm.php b/modules/orm/classes/orm.php index 8089ea5..c9848b1 100644 --- a/modules/orm/classes/orm.php +++ b/modules/orm/classes/orm.php @@ -64,6 +64,27 @@ class ORM { */
protected $has_many = array();
+ /**
+ * Associated query builder
+ * @var Query_Database
+ * @access public
+ */
+ public $query;
+
+ /**
+ * The name of the model
+ * @var string
+ * @access public
+ */
+ public $model_name;
+
+ /**
+ * Cached properties
+ * @var array
+ * @access public
+ */
+ public $cached = array();
+
/**
* An instance of the database connection
* @var DB
@@ -77,14 +98,7 @@ class ORM { * @access protected
*/
protected $_row = array();
-
- /**
- * Associated query builder
- * @var Query_Database
- * @access public
- */
- public $query;
-
+
/**
* A flag whether the row was loaded from the database
* @var boolean
@@ -92,22 +106,22 @@ class ORM { */
protected $_loaded = false;
- /**
- * The name of the model
- * @var string
- * @access public
- */
- public $model_name;
-
- /**
- * Cached properties
+
+ /**
+ * Relationships to be preloaded
* @var array
- * @access protected
+ * @access protected
*/
- protected $_cached = array();
+ protected $_with = array();
+ /**
+ * Cached column names for tables
+ * @var array
+ * @access protected
+ */
protected static $_column_cache = array();
+
/**
* Constructs the model. To use ORM it is enough to
* just create a model like this:
@@ -185,37 +199,62 @@ class ORM { * Finds all rows that meet set criteria.
*
* @return ORMResult Returns ORMResult that you can use in a 'foreach' loop.
+ * @throw Exception If the relationship specified using with() does not exist or is not of the belongs_to or has_one type
* @access public
*/
public function find_all() {
-
- $relations = array();
- $model_alias = $this->query->last_alias();
- $fields=array("{$model_alias}.*");
- foreach($this->_with as $rel) {
-
- $model = ORM::factory($rel['model']);
- $alias = $model->query->add_alias();
- $relations[$rel['name']] = $alias;
-
- if ($rel['type'] == 'belongs_to') {
- $this->query->join(array($model->table, $alias), array(
- $model_alias.'.'.$rel['key'],
- $alias.'.'.$model->id_field,
- ),'left');
- }else {
- $this->query->join(array($model->table, $alias), array(
- $model_alias.'.'.$this->id_field,
- $alias.'.'.$rel['key'],
- ), 'left');
+ $paths = array();
+ $fields = array();
+ $this_alias=$this->query->last_alias();
+ foreach($this->columns() as $column)
+ $fields[]=array("{$this_alias}.{$column}","{$this_alias}__{$column}");
+ foreach($this->_with as $target) {
+ $model = $this;
+ $model_alias=$this_alias;
+ $rels = explode('.', $target);
+ Debug::log($rels);
+ foreach($rels as $key => $rel_name) {
+ $path = implode('.', array_slice($rels, 0, $key + 1));
+ Debug::log($path);
+ Debug::log('ggg'.$model->model_name);
+ if (isset($paths[$path])) {
+ $model = $paths[$path]['model'];
+ $model_alias=$paths[$path]['alias'];
+ continue;
+ }
+ $alias=$this->query->add_alias();
+ $model_rels = array_merge($model->has_one, $model->has_many,$model->belongs_to);
+ $rel = Misc::arr($model_rels, $rel_name, false);
+
+ if (!$rel)
+ throw new Exception("Model '{$model->model_name}' doesn't have a '{$rel_name}' relation defined");
+ if ($rel['type'] == 'has_many')
+ throw new Exception("Relationship '{$rel_name}' is of has_many type and cannot be preloaded view with()");
+ Debug::log('rrr'.$rel['model']);
+ $rel_model = ORM::factory($rel['model']);
+
+ if ($rel['type'] == 'belongs_to') {
+ $this->query->join(array($rel_model->table, $alias), array(
+ $model_alias.'.'.$rel['key'],
+ $alias.'.'.$rel_model->id_field,
+ ),'left');
+ }else {
+ $this->query->join(array($rel_model->table, $alias), array(
+ $model_alias.'.'.$model->id_field,
+ $alias.'.'.$rel['key'],
+ ), 'left');
+ }
+
+ foreach($rel_model->columns() as $column)
+ $fields[]=array("{$alias}.{$column}","{$alias}__{$column}");
+ $model = $rel_model;
+ $model_alias = $alias;
+ $paths[$path] = array('alias' => $alias, 'model' => $model);
}
- $fields[]="{$alias}.*";
}
-
- $this->query->fields($fields);
- print_r($this->query->query());
- die;
- return new ORMResult(get_class($this), $res=$this->query->execute(),$model_alias,$relations);
+
+ call_user_func_array(array($this->query,'fields'),$fields);
+ return new ORMResult(get_class($this), $res=$this->query->execute(),$paths);
}
/**
@@ -305,10 +344,10 @@ class ORM { public function __get($column) {
if (array_key_exists($column,$this->_row))
return $this->_row[$column];
- if (array_key_exists($column,$this->_cached))
- return $this->_cached[$column];
+ if (array_key_exists($column,$this->cached))
+ return $this->cached[$column];
if (($val = $this->get($column))!==null) {
- $this->_cached[$column] = $val;
+ $this->cached[$column] = $val;
return $val;
}
$relations = array_merge($this->has_one, $this->has_many, $this->belongs_to);
@@ -348,7 +387,7 @@ class ORM { $model->query->fields(array("$new_alias.*"));
if ($target['type'] != 'has_many' && $this->loaded() ) {
$model = $model->find();
- $this->_cached[$column]=$model;
+ $this->cached[$column]=$model;
}
return $model;
}
@@ -374,7 +413,7 @@ class ORM { }else{
$this->_row[$column] = $val;
}
- $this->_cached=array();
+ $this->cached=array();
}
/**
@@ -425,7 +464,7 @@ class ORM { $model->$key = $this->_row[$this->id_field];
$model->save();
}
- $this->_cached=array();
+ $this->cached=array();
}
/**
@@ -468,21 +507,33 @@ class ORM { $model->$key = null;
$model->save();
}
- $this->_cached=array();
+ $this->cached=array();
}
- protected $_with = array();
+ /**
+ * Gets name column names of the table associated with the model.
+ *
+ * @return array Array of column names
+ * @access public
+ */
public function columns() {
if (!isset(ORM::$_column_cache[$this->table]))
- ORM::$_column_cache[$this->table] = array_keys(DB::instance($this->connection)->list_columns($this->table));
+ ORM::$_column_cache[$this->table] = DB::instance($this->connection)->list_columns($this->table);
return ORM::$_column_cache[$this->table];
}
- public function with($relation) {
- $rels = array_merge($this->has_one, $this->has_many,$this->belongs_to);
- $rel = Misc::arr($rels, $relation, false);
- if (!$rel)
- throw new Exception("Model doesn't have a '{$relation}' relation defined");
- $this->_with[] = $rel;
+
+ /**
+ * Defines which relationships should be preloaded. You can only preload
+ * belongs_to and has_one relationships. You can use the dot notation to
+ * preload deep relationsips, e.g. 'tree.protector' will preload the tree
+ * that a fairy lives in and also preload the protector of that tree.
+ *
+ * @param string $relationsip,... List of relationships to preload
+ * @return ORM Returns itself
+ * @access public
+ */
+ public function with() {
+ $this->_with = func_get_args();
return $this;
}
/**
@@ -499,7 +550,7 @@ class ORM { ->table($this->table)
->where($this->id_field, $this->_row[$this->id_field])
->execute();
- $this->_cached=array();
+ $this->cached=array();
}
/**
@@ -561,7 +612,7 @@ class ORM { $this->_row = array_merge($this->_row, $row);
if ($set_loaded)
$this->_loaded = true;
- $this->_cached=array();
+ $this->cached=array();
return $this;
}
diff --git a/modules/orm/classes/ormresult.php b/modules/orm/classes/ormresult.php index 381f37c..5d1ab50 100644 --- a/modules/orm/classes/ormresult.php +++ b/modules/orm/classes/ormresult.php @@ -25,19 +25,33 @@ class ORMResult implements Iterator { */
private $_dbresult;
+ /**
+ * Rules for preloaded relationships
+ * @var array
+ * @access private
+ */
+ private $_with = array();
+
/**
* Initialized an ORMResult with which model to use and which result to
* iterate over
*
- * @param string $model Model name
+ * @param string $model Model name
* @param Result_Database $dbresult Database result
+ * @param array $with Array of rules for preloaded relationships
* @return void
* @access public
*/
- public function __construct($model,$dbresult,$relations=array()){
+ public function __construct($model,$dbresult,$with=array()){
$this->_model=$model;
$this->_dbresult = $dbresult;
- print_r($this->_dbresult->current());die;
+ foreach($with as $path => $rel)
+ $this->_with[] = array(
+ 'path' => explode('.',$path),
+ 'path_count' => count(explode('.',$path)),
+ 'model' => $rel['model'],
+ 'columns' => $rel['model']->columns()
+ );
}
/**
@@ -58,9 +72,36 @@ class ORMResult implements Iterator { */
function current() {
$model = new $this->_model;
+
if (!$this->_dbresult->valid())
return $model;
- $model->values((array)$this->_dbresult->current(),true);
+
+ if(empty($this->_with))
+ return $model->values((array)$this->_dbresult->current(), true);
+
+ $data = (array)$this->_dbresult->current();
+
+ $model_data=array();
+ foreach($model->columns() as $column)
+ $model_data[$column] = array_shift($data);
+ $model->values($model_data, true);
+
+ foreach($this->_with as $rel) {
+ $rel_data = array();
+ foreach($rel['columns'] as $column)
+ $rel_data[$column] = array_shift($data);
+ $rel['model']->values($rel_data, true);
+
+ $owner = $model;
+ foreach($rel['path'] as $key => $child) {
+ if ($key == $rel['path_count'] - 1) {
+ $owner->cached[$child]=$rel['model'];
+ }else {
+ $owner=$owner->cached[$child];
+ }
+ }
+ }
+
return $model;
}
|