object_name = strtolower(substr(get_class($this), 0, -6)); $this->object_plural = inflector::plural($this->object_name); if (!isset($this->sorting)) { $this->sorting = array($this->primary_key => 'asc'); } $this->__initialize(); $this->clear(); if (is_object($id)) { $this->load_values((array) $id); } elseif (!empty($id)) { $this->find($id); } } public function __initialize() { if ( ! is_object($this->db)) { $this->db = Database::instance($this->db); } if (empty($this->table_name)) { $this->table_name = $this->object_name; if ($this->table_names_plural === TRUE) { $this->table_name = inflector::plural($this->table_name); } } if (is_array($this->ignored_columns)) { $this->ignored_columns = array_combine($this->ignored_columns, $this->ignored_columns); } $this->reload_columns(); } public function __sleep() { return array('object_name', 'object', 'changed', 'loaded', 'saved', 'sorting'); } public function __wakeup() { $this->__initialize(); if ($this->reload_on_wakeup === TRUE) { $this->reload(); } } public function __call($method, array $args) { if (method_exists($this->db, $method)) { if (in_array($method, array('query', 'get', 'insert', 'update', 'delete'))) throw new Kohana_Exception('orm.query_methods_not_allowed'); $this->db_applied[$method] = $method; $num_args = count($args); if ($method === 'select' AND $num_args > 3) { $this->db->select($args); } else { switch ($num_args) { case 0: if (in_array($method, array('open_paren', 'close_paren', 'enable_cache', 'disable_cache'))) { $this->db->$method(); } else { return $this->db->$method(); } break; case 1: $this->db->$method($args[0]); break; case 2: $this->db->$method($args[0], $args[1]); break; case 3: $this->db->$method($args[0], $args[1], $args[2]); break; case 4: $this->db->$method($args[0], $args[1], $args[2], $args[3]); break; default: call_user_func_array(array($this->db, $method), $args); break; } } return $this; } else { throw new Kohana_Exception('core.invalid_method', $method, get_class($this)); } } public function __get($column) { if (array_key_exists($column, $this->object)) { return $this->object[$column]; } elseif (isset($this->related[$column])) { return $this->related[$column]; } elseif ($column === 'primary_key_value') { return $this->object[$this->primary_key]; } elseif ($model = $this->related_object($column)) { if (in_array($model->object_name, $this->belongs_to) OR ! array_key_exists($this->foreign_key($column), $model->object)) { $where = array($model->table_name.'.'.$model->primary_key => $this->object[$this->foreign_key($column)]); } else { $where = array($this->foreign_key($column, $model->table_name) => $this->primary_key_value); } return $this->related[$column] = $model->find($where); } elseif (isset($this->has_many[$column])) { $through = ORM::factory(inflector::singular($this->has_many[$column])); $model = ORM::factory(inflector::singular($column)); $join_table = $through->table_name; $join_col1 = $through->foreign_key($model->object_name, $join_table); $join_col2 = $model->table_name.'.'.$model->primary_key; return $this->related[$column] = $model ->join($join_table, $join_col1, $join_col2) ->where($through->foreign_key($this->object_name, $join_table), $this->object[$this->primary_key]) ->find_all(); } elseif (in_array($column, $this->has_many)) { $model = ORM::factory(inflector::singular($column)); return $this->related[$column] = $model ->where($this->foreign_key($column, $model->table_name), $this->object[$this->primary_key]) ->find_all(); } elseif (in_array($column, $this->has_and_belongs_to_many)) { $model = ORM::factory(inflector::singular($column)); if ($this->has($model, TRUE)) { return $this->related[$column] = $model ->in($model->table_name.'.'.$model->primary_key, $this->changed_relations[$column]) ->find_all(); } else { return $this->related[$column] = $model ->where($model->table_name.'.'.$model->primary_key, NULL) ->find_all(); } } elseif (isset($this->ignored_columns[$column])) { return NULL; } elseif (in_array($column, array ( 'object_name', 'object_plural', 'primary_key', 'primary_val', 'table_name', 'table_columns', 'loaded', 'saved', 'has_one', 'belongs_to', 'has_many', 'has_and_belongs_to_many', 'load_with' ))) { return $this->$column; } else { throw new Kohana_Exception('core.invalid_property', $column, get_class($this)); } } public function __set($column, $value) { if (isset($this->ignored_columns[$column])) { return NULL; } elseif (isset($this->object[$column]) OR array_key_exists($column, $this->object)) { if (isset($this->table_columns[$column])) { $this->changed[$column] = $column; $this->saved = FALSE; } $this->object[$column] = $this->load_type($column, $value); } elseif (in_array($column, $this->has_and_belongs_to_many) AND is_array($value)) { $model = ORM::factory(inflector::singular($column)); if ( ! isset($this->object_relations[$column])) { $this->has($model); } $this->changed_relations[$column] = $value; if (isset($this->related[$column])) { unset($this->related[$column]); } } else { throw new Kohana_Exception('core.invalid_property', $column, get_class($this)); } } public function __isset($column) { return (isset($this->object[$column]) OR isset($this->related[$column])); } public function __unset($column) { unset($this->object[$column], $this->changed[$column], $this->related[$column]); } public function __toString() { return (string) $this->object[$this->primary_key]; } public function as_array() { $object = array(); foreach ($this->object as $key => $val) { $object[$key] = $this->$key; } return $object; } public function with($target_path) { if (isset($this->with_applied[$target_path])) { return $this; } $objects = explode(':', $target_path); $target = $this; foreach ($objects as $object) { $parent = $target; $target = $parent->related_object($object); if ( ! $target) { return $this; } } $target_name = $object; array_pop($objects); $parent_path = implode(':', $objects); if (empty($parent_path)) { $parent_path = $this->table_name; } else { if( ! isset($this->with_applied[$parent_path])) { $this->with($parent_path); } } $this->with_applied[$target_path] = TRUE; $select = array_keys($target->object); foreach ($select as $i => $column) { $select[$i] = $target_path.'.'.$column.' AS '.$target_path.':'.$column; } $this->db->select($select); if (in_array($target->object_name, $parent->belongs_to) OR ! isset($target->object[$parent->foreign_key($target_name)])) { $join_col1 = $target->foreign_key(TRUE, $target_path); $join_col2 = $parent->foreign_key($target_name, $parent_path); } else { $join_col2 = $parent->foreign_key(TRUE, $parent_path); $join_col1 = $parent->foreign_key($target_name, $target_path); } $join_table = new Database_Expression($target->db->table_prefix().$target->table_name.' AS '.$this->db->table_prefix().$target_path); $this->db->join($join_table, $join_col1, $join_col2, 'LEFT'); return $this; } public function find($id = NULL) { if ($id !== NULL) { if (is_array($id)) { $this->db->where($id); } else { $this->db->where($this->table_name.'.'.$this->unique_key($id), $id); } } return $this->load_result(); } public function find_all($limit = NULL, $offset = NULL) { if ($limit !== NULL AND ! isset($this->db_applied['limit'])) { $this->limit($limit); } if ($offset !== NULL AND ! isset($this->db_applied['offset'])) { $this->offset($offset); } return $this->load_result(TRUE); } public function select_list($key = NULL, $val = NULL) { if ($key === NULL) { $key = $this->primary_key; } if ($val === NULL) { $val = $this->primary_val; } return $this->select($key, $val)->find_all()->select_list($key, $val); } public function validate(Validation $array, $save = FALSE) { $safe_array = $array->safe_array(); if ( ! $array->submitted()) { foreach ($safe_array as $key => $value) { $value = $this->$key; if (is_object($value) AND $value instanceof ORM_Iterator) { $value = $value->primary_key_array(); } $array[$key] = $value; } } if ($status = $array->validate()) { $fields = $array->as_array(); foreach ($fields as $key => $value) { if (isset($safe_array[$key])) { $this->$key = $value; } } if ($save === TRUE OR is_string($save)) { $this->save(); if (is_string($save)) { url::redirect($save); } } } return $status; } public function save() { if ( ! empty($this->changed)) { $data = array(); foreach ($this->changed as $column) { $data[$column] = $this->object[$column]; } if ($this->loaded === TRUE) { $query = $this->db ->where($this->primary_key, $this->object[$this->primary_key]) ->update($this->table_name, $data); $this->saved = TRUE; } else { $query = $this->db ->insert($this->table_name, $data); if ($query->count() > 0) { if (empty($this->object[$this->primary_key])) { $this->object[$this->primary_key] = $query->insert_id(); } $this->loaded = $this->saved = TRUE; } } if ($this->saved === TRUE) { $this->changed = array(); } } if ($this->saved === TRUE AND ! empty($this->changed_relations)) { foreach ($this->changed_relations as $column => $values) { $added = array_diff($values, $this->object_relations[$column]); $removed = array_diff($this->object_relations[$column], $values); if (empty($added) AND empty($removed)) { continue; } unset($this->related[$column]); $model = ORM::factory(inflector::singular($column)); if (($join_table = array_search($column, $this->has_and_belongs_to_many)) === FALSE) continue; if (is_int($join_table)) { $join_table = $model->join_table($this->table_name); } $object_fk = $this->foreign_key(NULL); $related_fk = $model->foreign_key(NULL); if ( ! empty($added)) { foreach ($added as $id) { $this->db->insert($join_table, array ( $object_fk => $this->object[$this->primary_key], $related_fk => $id, )); } } if ( ! empty($removed)) { $this->db ->where($object_fk, $this->object[$this->primary_key]) ->in($related_fk, $removed) ->delete($join_table); } unset($this->object_relations[$column], $this->changed_relations[$column]); } } return $this; } public function delete($id = NULL) { if ($id === NULL AND $this->loaded) { $id = $this->object[$this->primary_key]; } $this->db->where($this->primary_key, $id)->delete($this->table_name); return $this->clear(); } public function delete_all($ids = NULL) { if (is_array($ids)) { $this->db->in($this->primary_key, $ids); } elseif (is_null($ids)) { $this->db->where('1=1'); } else { return $this; } $this->db->delete($this->table_name); return $this->clear(); } public function clear() { $columns = array_keys($this->table_columns); $values = array_combine($columns, array_fill(0, count($columns), NULL)); $this->load_values($values); return $this; } public function reload() { return $this->find($this->object[$this->primary_key]); } public function reload_columns($force = FALSE) { if ($force === TRUE OR empty($this->table_columns)) { if (isset(ORM::$column_cache[$this->object_name])) { $this->table_columns = ORM::$column_cache[$this->object_name]; } else { ORM::$column_cache[$this->object_name] = $this->table_columns = $this->list_fields(); } } return $this; } public function has(ORM $model, $any = FALSE) { $related = ($model->table_names_plural === TRUE) ? $model->object_plural : $model->object_name; if (($join_table = array_search($related, $this->has_and_belongs_to_many)) === FALSE) return FALSE; if (is_int($join_table)) { $join_table = $model->join_table($this->table_name); } if ( ! isset($this->object_relations[$related])) { $this->changed_relations[$related] = $this->object_relations[$related] = $this->load_relations($join_table, $model); } if ( ! $model->empty_primary_key()) { return in_array($model->primary_key_value, $this->changed_relations[$related]); } elseif ($any) { return ! empty($this->changed_relations[$related]); } else { return FALSE; } } public function add(ORM $model) { if ($this->has($model)) return TRUE; $column = $model->object_plural; $this->changed_relations[$column][] = $model->primary_key_value; if (isset($this->related[$column])) { unset($this->related[$column]); } return TRUE; } public function remove(ORM $model) { if ( ! $this->has($model)) return FALSE; $column = $model->object_plural; if (($key = array_search($model->primary_key_value, $this->changed_relations[$column])) === FALSE) return FALSE; unset($this->changed_relations[$column][$key]); if (isset($this->related[$column])) { unset($this->related[$column]); } return TRUE; } public function count_all() { return $this->db->count_records($this->table_name); } function count_found_rows () { return $this->db->count_found_rows; } public function list_fields($table = NULL) { if ($table === NULL) { $table = $this->table_name; } return $this->db->list_fields($table); } public function field_data($table) { return $this->db->field_data($table); } public function clear_cache($sql = NULL) { $this->db->clear_cache($sql); ORM::$column_cache = array(); return $this; } public function unique_key($id) { return $this->primary_key; } public function foreign_key($table = NULL, $prefix_table = NULL) { if ($table === TRUE) { if (is_string($prefix_table)) { return $prefix_table.'.'.$this->primary_key; } else { return $this->table_name.'.'.$this->primary_key; } } if (is_string($prefix_table)) { $prefix_table .= '.'; } if (isset($this->foreign_key[$table])) { $foreign_key = $this->foreign_key[$table]; } else { if ( ! is_string($table) OR ! array_key_exists($table.'_'.$this->primary_key, $this->object)) { $table = $this->table_name; if (strpos($table, '.') !== FALSE) { list ($schema, $table) = explode('.', $table, 2); } if ($this->table_names_plural === TRUE) { $table = inflector::singular($table); } } $foreign_key = $table.'_'.$this->primary_key; } return $prefix_table.$foreign_key; } public function join_table($table) { if ($this->table_name > $table) { $table = $table.'_'.$this->table_name; } else { $table = $this->table_name.'_'.$table; } return $table; } protected function related_object($object) { if (isset($this->has_one[$object])) { $object = ORM::factory($this->has_one[$object]); } elseif (isset($this->belongs_to[$object])) { $object = ORM::factory($this->belongs_to[$object]); } elseif (in_array($object, $this->has_one) OR in_array($object, $this->belongs_to)) { $object = ORM::factory($object); } else { return FALSE; } return $object; } public function load_values(array $values) { if (array_key_exists($this->primary_key, $values)) { $this->object = $this->changed = $this->related = array(); $this->loaded = $this->saved = ($values[$this->primary_key] !== NULL); } $related = array(); foreach ($values as $column => $value) { if (strpos($column, ':') === FALSE) { if (isset($this->table_columns[$column])) { $value = $this->load_type($column, $value); } $this->object[$column] = $value; } else { list ($prefix, $column) = explode(':', $column, 2); $related[$prefix][$column] = $value; } } if ( ! empty($related)) { foreach ($related as $object => $values) { $this->related[$object] = $this->related_object($object)->load_values($values); } } return $this; } protected function load_type($column, $value) { $type = gettype($value); if ($type == 'object' OR $type == 'array' OR ! isset($this->table_columns[$column])) return $value; $column = $this->table_columns[$column]; if ($value === NULL AND ! empty($column['null'])) return $value; if ( ! empty($column['binary']) AND ! empty($column['exact']) AND (int) $column['length'] === 1) { $column['type'] = 'boolean'; } switch ($column['type']) { case 'int': if ($value === '' AND ! empty($column['null'])) { $value = NULL; } elseif ((float) $value > PHP_INT_MAX) { $value = (string) $value; } else { $value = (int) $value; } break; case 'float': $value = (float) $value; break; case 'boolean': $value = (bool) $value; break; case 'string': $value = (string) $value; break; } return $value; } protected function load_result($array = FALSE) { if ($array === FALSE) { $this->db->limit(1); } if ( ! isset($this->db_applied['select'])) { $this->db->select($this->table_name.'.*'); } if ( ! empty($this->load_with)) { foreach ($this->load_with as $alias => $object) { if (is_string($alias)) { $this->with($alias); } else { $this->with($object); } } } if ( ! isset($this->db_applied['orderby']) AND ! empty($this->sorting)) { $sorting = array(); foreach ($this->sorting as $column => $direction) { if (strpos($column, '.') === FALSE) { $column = $this->table_name.'.'.$column; } $sorting[$column] = $direction; } $this->db->orderby($sorting); } $result = $this->db->get($this->table_name); if ($array === TRUE) { return new ORM_Iterator($this, $result); } if ($result->count() === 1) { $this->load_values($result->result(FALSE)->current()); } else { $this->clear(); } return $this; } protected function load_relations($table, ORM $model) { $this->db->push(); $query = $this->db ->select($model->foreign_key(NULL).' AS id') ->from($table) ->where($this->foreign_key(NULL, $table), $this->object[$this->primary_key]) ->get() ->result(TRUE); $this->db->pop(); $relations = array(); foreach ($query as $row) { $relations[] = $row->id; } return $relations; } protected function empty_primary_key() { return (empty($this->object[$this->primary_key]) AND $this->object[$this->primary_key] !== '0'); } }