/core/libs/kumbia_active_record/kumbia_active_record.php
PHP | 2547 lines | 1543 code | 147 blank | 857 comment | 488 complexity | 443d59e1c2a65f1d1712a47d53534fc0 MD5 | raw file
Possible License(s): BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- <?php
- /**
- * KumbiaPHP web & app Framework
- *
- * LICENSE
- *
- * This source file is subject to the new BSD license that is bundled
- * with this package in the file LICENSE.
- *
- * @category Kumbia
- * @package ActiveRecord
- *
- * @copyright Copyright (c) 2005 - 2020 KumbiaPHP Team (http://www.kumbiaphp.com)
- * @license https://github.com/KumbiaPHP/KumbiaPHP/blob/master/LICENSE New BSD License
- */
- /**
- * @see Db
- */
- require CORE_PATH.'libs/db/db.php';
- /**
- * ActiveRecordBase Clase para el Mapeo Objeto Relacional.
- *
- * Active Record es un enfoque al problema de acceder a los datos de una
- * base de datos en forma orientada a objetos. Una fila en la
- * tabla de la base de datos (o vista) se envuelve en una clase,
- * de manera que se asocian filas únicas de la base de datos
- * con objetos del lenguaje de programación usado.
- * Cuando se crea uno de estos objetos, se añde una fila a
- * la tabla de la base de datos. Cuando se modifican los atributos del
- * objeto, se actualiza la fila de la base de datos.
- *
- * Propiedades Soportadas:
- * $db = Conexion al Motor de Base de datos
- * $database = Base de datos a la que se conecta, especificada en databases.ini
- * $source = Tabla que contiene la tabla que esta siendo mapeada
- * $fields = Listado de Campos de la tabla que han sido mapeados
- * $count = Conteo del ultimo Resultado de un Select
- * $primary_key = Listado de columnas que conforman la llave primaria
- * $non_primary = Listado de columnas que no son llave primaria
- * $not_null = Listado de campos que son not_null
- * $attributes_names = nombres de todos los campos que han sido mapeados
- * $debug = Indica si se deben mostrar los SQL enviados al RDBM en pantalla
- * $logger = Si es diferente de false crea un log utilizando la clase Logger
- * en library/kumbia/logger/logger.php, esta crea un archivo .txt en logs/ con todas las
- * operaciones realizadas en ActiveRecord, si $logger = "nombre", crea un
- * archivo con ese nombre
- *
- * Propiedades sin Soportar:
- * $dynamic_update : La idea es que en un futuro ActiveRecord solo
- * actualize los campos que han cambiado. (En Desarrollo)
- * $dynamic_insert : Indica si los valores del insert son solo aquellos
- * que sean no nulos. (En Desarrollo)
- * $select_before_update: Exige realizar una sentencia SELECT anterior
- * a la actualizacion UPDATE para comprobar que los datos no hayan sido
- * cambiados (En Desarrollo)
- * $subselect : Permitira crear una entidad ActiveRecord de solo lectura que
- * mapearia los resultados de un select directamente a un Objeto (En Desarrollo)
- *
- * @category Kumbia
- */
- class KumbiaActiveRecord
- {
- //Soportados
- /**
- * Resource de conexion a la base de datos.
- *
- * @var DbBase
- */
- protected $db;
- /**
- * Base de datos a la que se conecta.
- *
- * @var string
- */
- protected $database;
- /**
- * Schema donde esta la tabla.
- *
- * @var string
- */
- protected $schema;
- /**
- * Tabla utilizada para realizar el mapeo.
- *
- * @var string
- */
- protected $source;
- /**
- * Numero de resultados generados en la ultima consulta.
- *
- * @var int
- */
- protected $count;
- /**
- * Nombres de los atributos de la entidad.
- *
- * @var array
- */
- protected $fields = array();
- /**
- * LLaves primarias de la entidad.
- *
- * @var array
- */
- protected $primary_key = array();
- /**
- * Campos que no son llave primaria.
- *
- * @var array
- */
- protected $non_primary = array();
- /**
- * Campos que no permiten nulos.
- *
- * @var array
- */
- protected $not_null = array();
- /**
- * Campos que tienen valor por defecto.
- *
- * @var array
- */
- protected $_with_default = array();
- /**
- * Nombres de atributos, es lo mismo que fields.
- *
- * @var array
- */
- protected $alias = array();
- /**
- * Indica si la clase corresponde a un mapeo de una vista
- * en la base de datos.
- *
- * @var bool
- */
- protected $is_view = false;
- /**
- * Indica si el modelo esta en modo debug.
- *
- * @var bool
- */
- protected $debug = false;
- /**
- * Indica si se logearan los mensajes generados por la clase.
- *
- * @var mixed
- */
- protected $logger = false;
- /**
- * Indica si los datos del modelo deben ser persistidos.
- *
- * @var bool
- */
- protected $persistent = false;
- /**
- * Validaciones.
- *
- * inclusion_in: el campo pertenece a un conjunto de elementos
- * exclusion_of: el campo no pertenece a un conjunto de elementos
- * numericality_of: el campo debe ser númerico
- * format_of: el campo debe coincidir con la expresión regular
- * date_in: el campo debe ser una fecha válida
- * email_in: el campo debe ser un correo electrónico
- * uniqueness_of: el campo debe ser único
- *
- * @var array
- * */
- protected $_validates = array('inclusion_in' => array(), 'exclusion_of' => array(), 'numericality_of' => array(),
- 'format_of' => array(), 'date_in' => array(), 'email_in' => array(), 'uniqueness_of' => array(), );
- /**
- * Campos que terminan en _in.
- *
- * @var array
- */
- protected $_in = array();
- /**
- * Campos que terminan en _at.
- *
- * @var array
- */
- protected $_at = array();
- /**
- * Variable para crear una condicion basada en los
- * valores del where.
- *
- * @var string
- */
- protected $_where_pk;
- /**
- * Indica si ya se han obtenido los metadatos del Modelo.
- *
- * @var bool
- */
- protected $_dumped = false;
- /**
- * Indica si hay bloqueo sobre los warnings cuando una propiedad
- * del modelo no esta definida-.
- *
- * @var bool
- */
- protected $_dump_lock = false;
- /**
- * Tipos de datos de los campos del modelo.
- *
- * @var array
- */
- protected $_data_type = array();
- /**
- * Relaciones a las cuales tiene una cardinalidad 1-1.
- *
- * @var array
- */
- protected $_has_one = array();
- /**
- * Relaciones a las cuales tiene una cardinalidad 1-n.
- *
- * @var array
- */
- protected $_has_many = array();
- /**
- * Relaciones a las cuales tiene una cardinalidad 1-1.
- *
- * @var array
- */
- protected $_belongs_to = array();
- /**
- * Relaciones a las cuales tiene una cardinalidad n-n (muchos a muchos) o 1-n inversa.
- *
- * @var array
- */
- protected $_has_and_belongs_to_many = array();
- /**
- * Clases de las cuales es padre la clase actual.
- *
- * @var array
- */
- protected $parent_of = array();
- /**
- * Persistance Models Meta-data.
- */
- protected static $models = array();
- /**
- * Constructor del Modelo.
- *
- * @param array $data
- */
- public function __construct($data = null)
- {
- $this->_model_name();
- /*
- * Inicializa el modelo en caso de que exista initialize
- */
- if (method_exists($this, 'initialize')) {
- $this->initialize();
- }
- /*
- * Conecta a la bd
- * */
- $this->_connect();
- if ($data) {
- if (!is_array($data)) {
- $data = Util::getParams(func_get_args());
- }
- foreach ($this->fields as $field) {
- if (isset($data[$field])) {
- $this->$field = $data[$field];
- }
- }
- }
- }
- /**
- * Obtiene el nombre de la relacion en el RDBM a partir del nombre de la clase.
- */
- protected function _model_name()
- {
- if (!$this->source) {
- $this->source = Util::smallcase(get_class($this));
- }
- }
- /**
- * Establece publicamente el $source de la tabla.
- *
- * @param string $source
- */
- public function set_source($source)
- {
- $this->source = $source;
- }
- /**
- * Devuelve el source actual.
- *
- * @return string
- */
- public function get_source()
- {
- return $this->source;
- }
- /**
- * Establece la base datos a utilizar.
- *
- * @param string $database
- */
- public function set_database($database)
- {
- $this->database = $database;
- }
- /**
- * Devuelve la base de datos.
- *
- * @return string
- */
- public function get_database()
- {
- return $this->database ?: Config::read('config')['application']['database'];
- }
- /**
- * Pregunta si el ActiveRecord ya ha consultado la información de metadatos
- * de la base de datos o del registro persistente.
- *
- * @return bool
- */
- public function is_dumped()
- {
- return $this->_dumped;
- }
- /**
- * Devuelve los registros del modelo al que se está asociado.
- *
- * @param string $relmodel nombre del modelo asociado
- *
- * @return array|null|false si existen datos devolverá un array,
- * NULL si no hay datos asociados aun, y false si no existe ninguna asociación
- */
- protected function _get_relation_data($relmodel)
- {
- if (isset($this->_belongs_to[$relmodel])) {
- $relation = $this->_belongs_to[$relmodel];
- return (new $relation->model())->find_first($this->{$relation->fk});
- }
- if (isset($this->_has_one[$relmodel])) {
- $relation = $this->_has_one[$relmodel];
- if ($this->{$this->primary_key[0]}) {
- return (new $relation->model())->find_first("{$relation->fk}={$this->db->add_quotes($this->{$this->primary_key[0]}) }");
- }
- return null;
- }
- if (isset($this->_has_many[$relmodel])) {
- $relation = $this->_has_many[$relmodel];
- if ($this->{$this->primary_key[0]}) {
- return (new $relation->model())->find("{$relation->fk}={$this->db->add_quotes($this->{$this->primary_key[0]}) }");
- }
- return array();
- }
- if (isset($this->_has_and_belongs_to_many[$relmodel])) {
- $relation = $this->_has_and_belongs_to_many[$relmodel];
- $relation_model = self::get($relation->model);
- $source = ($this->schema ? "{$this->schema}." : null).$this->source;
- $relation_source = ($relation_model->schema ? "{$relation_model->schema}." : null).$relation_model->source;
- /*
- * Cargo atraves de que tabla se efectuara la relacion
- *
- */
- if (!isset($relation->through)) {
- if ($source > $relation_source) {
- $relation->through = "{$this->source}_{$relation_source}";
- } else {
- $relation->through = "{$relation_source}_{$this->source}";
- }
- } else {
- $through = explode('/', $relation->through);
- $relation->through = end($through);
- }
- if ($this->{$this->primary_key[0]}) {
- return $relation_model->find_all_by_sql("SELECT $relation_source.* FROM $relation_source, {$relation->through}, $source
- WHERE {$relation->through}.{$relation->key} = {$this->db->add_quotes($this->{$this->primary_key[0]}) }
- AND {$relation->through}.{$relation->fk} = $relation_source.{$relation_model->primary_key[0]}
- AND {$relation->through}.{$relation->key} = $source.{$this->primary_key[0]}
- ORDER BY $relation_source.{$relation_model->primary_key[0]}");
- }
- return array();
- }
- return false; //si no existe ninguna asociación devuelve false.
- }
- /**
- * Valida que los valores que sean leidos del objeto ActiveRecord esten definidos
- * previamente o sean atributos de la entidad.
- *
- * @param string $property
- */
- public function __get($property)
- {
- if (!$this->_dump_lock) {
- if (!isset($this->$property)) {
- return $this->_get_relation_data($property);
- }
- }
- return $this->$property;
- }
- /**
- * Valida que los valores que sean asignados al objeto ActiveRecord esten definidos
- * o sean atributos de la entidad.
- *
- * @param string $property
- * @param mixed $value
- */
- public function __set($property, $value)
- {
- if (!$this->_dump_lock) {
- if (is_object($value) && is_subclass_of($value, 'KumbiaActiveRecord')) {
- if (array_key_exists($property, $this->_belongs_to)) {
- $relation = $this->_belongs_to[$property];
- $value->dump_model();
- $this->{$relation->fk} = $value->{$value->primary_key[0]};
- return;
- } elseif (array_key_exists($property, $this->_has_one)) {
- $relation = $this->_has_one[$property];
- $value->{$relation->fk} = $this->{$this->primary_key[0]};
- return;
- }
- } elseif ($property == 'source') {
- $value = self::sql_item_sanitize($value);
- }
- }
- $this->$property = $value;
- }
- /**
- * Devuelve un valor o un listado dependiendo del tipo de Relación.
- */
- public function __call($method, $args = array())
- {
- if (substr($method, 0, 8) == 'find_by_') {
- $field = substr($method, 8);
- self::sql_item_sanitize($field);
- if (isset($args[0])) {
- $arg = array("conditions: $field = {$this->db->add_quotes($args[0])}");
- unset($args[0]);
- } else {
- $arg = array();
- }
- return call_user_func_array(array($this, 'find_first'), array_merge($arg, $args));
- }
- if (substr($method, 0, 9) == 'count_by_') {
- $field = substr($method, 9);
- self::sql_item_sanitize($field);
- if (isset($args[0])) {
- $arg = array("conditions: $field = {$this->db->add_quotes($args[0])}");
- unset($args[0]);
- } else {
- $arg = array();
- }
- return call_user_func_array(array($this, 'count'), array_merge($arg, $args));
- }
- if (substr($method, 0, 12) == 'find_all_by_') {
- $field = substr($method, 12);
- self::sql_item_sanitize($field);
- if (isset($args[0])) {
- $arg = array("conditions: $field = {$this->db->add_quotes($args[0])}");
- unset($args[0]);
- } else {
- $arg = array();
- }
- return call_user_func_array(array($this, 'find'), array_merge($arg, $args));
- }
- $model = preg_replace('/^get/', '', $method);
- $mmodel = Util::smallcase($model);
- if (($data = $this->_get_relation_data($mmodel)) !== false) {
- return $data;
- }
- if (method_exists($this, $method)) {
- call_user_func_array(array($this, $method), $args);
- } else {
- throw new KumbiaException("No existe el método '$method' en ActiveRecord::".get_class($this));
- }
- return $this->$method($args);
- }
- /**
- * Se conecta a la base de datos y descarga los meta-datos si es necesario.
- */
- protected function _connect()
- {
- if (!is_object($this->db)) {
- $this->db = Db::factory($this->database);
- }
- $this->db->debug = $this->debug;
- $this->db->logger = $this->logger;
- $this->dump();
- }
- /**
- * Cargar los metadatos de la tabla.
- */
- public function dump_model()
- {
- $this->_connect();
- }
- /**
- * Verifica si la tabla definida en $this->source existe
- * en la base de datos y la vuelca en dump_info.
- *
- * @return bool
- */
- protected function dump()
- {
- if ($this->_dumped) {
- return false;
- }
- if ($this->source) {
- $this->source = str_replace(';', '', strtolower($this->source));
- } else {
- $this->_model_name();
- if (!$this->source) {
- return false;
- }
- }
- $table = $this->source;
- $schema = $this->schema;
- if (!count(self::get_meta_data($this->source))) {
- $this->_dumped = true;
- $this->_dump_info($table, $schema);
- if (!count($this->primary_key)) {
- if (!$this->is_view) {
- throw new KumbiaException("No se ha definido una llave primaria para la tabla '$table' esto imposibilita crear el ActiveRecord para esta entidad");
- }
- }
- } else {
- if (!$this->is_dumped()) {
- $this->_dumped = true;
- $this->_dump_info($table, $schema);
- }
- }
- return true;
- }
- /**
- * Vuelca la información de la tabla $table en la base de datos
- * para armar los atributos y meta-data del ActiveRecord.
- *
- * @param string $table
- *
- * @return bool
- */
- protected function _dump_info($table, $schema = '')
- {
- $this->_dump_lock = true;
- if (!count(self::get_meta_data($table))) {
- $meta_data = $this->db->describe_table($table, $schema);
- if ($meta_data) {
- self::set_meta_data($table, $meta_data);
- }
- }
- foreach (self::get_meta_data($table) as $field) {
- $this->fields[] = $field['Field'];
- $aliasAux = $field['Field'];
- if ($field['Key'] === 'PRI') {
- $this->primary_key[] = $field['Field'];
- $this->alias[$field['Field']] = 'Código';
- } else {
- $this->non_primary[] = $field['Field'];
- }
- /*
- * Si se indica que no puede ser nulo, pero se indica un
- * valor por defecto, entonces no se incluye en la lista, ya que
- * al colocar un valor por defecto, el campo nunca sera nulo
- *
- */
- if ($field['Null'] == 'NO' && !(isset($field['Default']) && $field['Default'])) {
- $this->not_null[] = $field['Field'];
- }
- if (isset($field['Default']) && $field['Default']) {
- $this->_with_default[] = $field['Field'];
- }
- if ($field['Type']) {
- $this->_data_type[$field['Field']] = strtolower($field['Type']);
- }
- if (substr($field['Field'], strlen($field['Field']) - 3, 3) === '_at') {
- $this->_at[] = $field['Field'];
- $aliasAux = substr($field['Field'], 0, -3);
- }
- if (substr($field['Field'], strlen($field['Field']) - 3, 3) === '_in') {
- $this->_in[] = $field['Field'];
- $aliasAux = substr($field['Field'], 0, -3);
- }
- if (substr($field['Field'], strlen($field['Field']) - 3, 3) === '_id') {
- $aliasAux = substr($field['Field'], 0, -3);
- }
- //humanizando el alias
- $this->alias[$field['Field']] = ucwords(strtr($aliasAux, '_-', ' '));
- }
- $this->_dump_lock = false;
- return true;
- }
- /**
- * Retorna un array de los campos (fields) de una tabla Humanizados.
- *
- * @param $key
- *
- * @return array
- */
- public function get_alias($key = null)
- {
- if ($key === null) {
- return $this->alias;
- }
- if (isset($this->alias[$key])) {
- return $this->alias[$key];
- }
- throw new KumbiaException("No se pudo obtener el Alias, porque el key: \"$key\" no existe.");
- }
- /**
- * Asigna un nuevo valor al alias dado un key.
- *
- * @param string $key
- * @param string $value
- */
- public function set_alias($key, $value)
- {
- if (isset($this->alias[$key])) {
- $this->alias[$key] = $value;
- }
- throw new KumbiaException("No se pudo asignar el nuevo valor al Alias, porque el key: \"$key\" no existe.");
- }
- /**
- * Commit a Transaction.
- *
- * @return success
- */
- public function commit()
- {
- return $this->db->commit();
- }
- /**
- * Rollback a Transaction.
- *
- * @return success
- */
- public function rollback()
- {
- return $this->db->rollback();
- }
- /**
- * Start a transaction in RDBM.
- *
- * @return success
- */
- public function begin()
- {
- $this->_connect(); //(true);
- return $this->db->begin();
- }
- /**
- * Find all records in this table using a SQL Statement.
- *
- * @param string $sqlQuery
- *
- * @return ActiveRecord Cursor
- */
- public function find_all_by_sql($sqlQuery)
- {
- $results = array();
- foreach ($this->db->fetch_all($sqlQuery) as $result) {
- $results[] = $this->dump_result($result);
- }
- return $results;
- }
- /**
- * Find a record in this table using a SQL Statement.
- *
- * @param string $sqlQuery
- *
- * @return ActiveRecord Cursor
- */
- public function find_by_sql($sqlQuery)
- {
- $row = $this->db->fetch_one($sqlQuery);
- if ($row !== false) {
- $this->dump_result_self($row);
- return $this->dump_result($row);
- } else {
- return false;
- }
- }
- /**
- * Execute a SQL Statement directly.
- *
- * @param string $sqlQuery
- *
- * @return int affected
- */
- public function sql($sqlQuery)
- {
- return $this->db->query($sqlQuery);
- }
- /**
- * Return Fist Record.
- *
- * Recibe los mismos parametros que find
- *
- * @param mixed $what
- *
- * @return ActiveRecord Cursor
- */
- public function find_first($what = '')
- {
- $what = Util::getParams(func_get_args());
- $select = 'SELECT ';
- if (isset($what['columns'])) {
- $select .= self::sql_sanitize($what['columns']);
- } elseif (isset($what['distinct'])) {
- $select .= 'DISTINCT ';
- $select .= $what['distinct'] ? self::sql_sanitize($what['distinct']) : join(',', $this->fields);
- } else {
- $select .= join(',', $this->fields);
- }
- if ($this->schema) {
- $select .= " FROM {$this->schema}.{$this->source}";
- } else {
- $select .= " FROM {$this->source}";
- }
- $what['limit'] = 1;
- $select .= $this->convert_params_to_sql($what);
- $resp = false;
- $result = $this->db->fetch_one($select);
- if ($result) {
- $this->dump_result_self($result);
- $resp = $this->dump_result($result);
- }
- return $resp;
- }
- /**
- * Find data on Relational Map table.
- *
- * @param string $what
- *
- * @return ActiveRecord Cursor
- *
- * columns: columnas a utilizar
- * conditions : condiciones de busqueda en WHERE
- * join: inclusion inner join o outer join
- * group : campo para grupo en GROUP BY
- * having : condicion para el grupo
- * order : campo para criterio de ordenamiento ORDER BY
- * distinct: campos para hacer select distinct
- */
- public function find($what = '')
- {
- $what = Util::getParams(func_get_args());
- $select = 'SELECT ';
- if (isset($what['columns'])) {
- $select .= $what['columns'] ? self::sql_sanitize($what['columns']) : join(',', $this->fields);
- } elseif (isset($what['distinct'])) {
- $select .= 'DISTINCT ';
- $select .= $what['distinct'] ? self::sql_sanitize($what['distinct']) : join(',', $this->fields);
- } else {
- $select .= join(',', $this->fields);
- }
- if ($this->schema) {
- $select .= " FROM {$this->schema}.{$this->source}";
- } else {
- $select .= " FROM {$this->source}";
- }
- $select .= $this->convert_params_to_sql($what);
- $results = array();
- $all_results = $this->db->in_query($select);
- foreach ($all_results as $result) {
- $results[] = $this->dump_result($result);
- }
- $this->count = count($results, COUNT_NORMAL);
- if (isset($what[0]) && is_numeric($what[0])) {
- if (!isset($results[0])) {
- $this->count = 0;
- return false;
- }
- $this->dump_result_self($all_results[0]);
- $this->count = 1;
- return $results[0];
- }
- $this->count = count($results, COUNT_NORMAL);
- return $results;
- }
- /*
- * Arma una consulta SQL con el parametro $what, así:
- * $what = Util::getParams(func_get_args());
- * $select = "SELECT * FROM Clientes";
- * $select.= $this->convert_params_to_sql($what);
- *
- * @param string|array $what
- * @return string
- */
- public function convert_params_to_sql($what = '')
- {
- $select = '';
- if (is_array($what)) {
- if (!isset($what['conditions'])) {
- if (!isset($this->primary_key[0]) && (isset($this->id) || $this->is_view)) {
- $this->primary_key[0] = 'id';
- }
- self::sql_item_sanitize($this->primary_key[0]);
- if (isset($what[0])) {
- if (is_numeric($what[0])) {
- $what['conditions'] = "{$this->primary_key[0]} = ".(int) $what[0];
- } else {
- if ($what[0] == '') {
- $what['conditions'] = "{$this->primary_key[0]} = ''";
- } else {
- $what['conditions'] = $what[0];
- }
- }
- }
- }
- if (isset($what['join'])) {
- $select .= " {$what['join']}";
- }
- if (isset($what['conditions'])) {
- $select .= " WHERE {$what['conditions']}";
- }
- if (isset($what['group'])) {
- $select .= " GROUP BY {$what['group']}";
- }
- if (isset($what['having'])) {
- $select .= " HAVING {$what['having']}";
- }
- if (isset($what['order'])) {
- self::sql_sanitize($what['order']);
- $select .= " ORDER BY {$what['order']}";
- }
- $limit_args = array($select);
- if (isset($what['limit'])) {
- array_push($limit_args, 'limit: '.(int) $what['limit']);
- }
- if (isset($what['offset'])) {
- array_push($limit_args, 'offset: '.(int) $what['offset']);
- }
- if (count($limit_args) > 1) {
- $select = call_user_func_array(array($this, 'limit'), $limit_args);
- }
- } else {
- if (strlen($what)) {
- if (is_numeric($what)) {
- $select .= "WHERE {$this->primary_key[0]} = ".(int) $what[0];
- } else {
- $select .= "WHERE $what";
- }
- }
- }
- return $select;
- }
- /*
- * Devuelve una clausula LIMIT adecuada al RDBMS empleado
- *
- * limit: maxima cantidad de elementos a mostrar
- * offset: desde que elemento se comienza a mostrar
- *
- * @param string $sql consulta select
- * @return String clausula LIMIT adecuada al RDBMS empleado
- */
- public function limit($sql)
- {
- $args = func_get_args();
- return call_user_func_array(array($this->db, 'limit'), $args);
- }
- /**
- * Ejecuta un SELECT DISTINCT.
- *
- * @param string $what
- *
- * @return array
- *
- * Soporta parametros iguales a find
- */
- public function distinct($what = '')
- {
- $what = Util::getParams(func_get_args());
- if ($this->schema) {
- $table = $this->schema.'.'.$this->source;
- } else {
- $table = $this->source;
- }
- if (empty($what['columns'])) {
- $what['columns'] = $what['0'];
- }
- $what['columns'] = self::sql_sanitize($what['columns']);
- $select = "SELECT DISTINCT {$what['columns']} FROM $table ";
- /*
- * Se elimina el de indice cero ya que por defecto convert_params_to_sql lo considera como una condicion en WHERE
- */
- unset($what[0]);
- $select .= $this->convert_params_to_sql($what);
- $results = array();
- foreach ($this->db->fetch_all($select) as $result) {
- $results[] = $result[0];
- }
- return $results;
- }
- /**
- * Ejecuta una consulta en el RDBM directamente.
- *
- * @param string $sql
- *
- * @return resource
- */
- public static function static_select_one($sql)
- {
- $db = Db::factory();
- if (substr(ltrim($sql), 0, 7) != 'SELECT') {
- $sql = 'SELECT '.$sql;
- }
- $num = $db->fetch_one($sql);
- return $num[0];
- }
- /**
- * Realiza un conteo de filas.
- *
- * @param string $what
- *
- * @return int
- */
- public function count($what = '')
- {
- $what = Util::getParams(func_get_args());
- if ($this->schema) {
- $table = "{$this->schema}.{$this->source}";
- } else {
- $table = $this->source;
- }
- unset($what['order']);
- if (isset($what['distinct']) && $what['distinct']) {
- if (isset($what['group'])) {
- $select = "SELECT COUNT(*) FROM (SELECT DISTINCT {$what['distinct']} FROM $table ";
- $select .= $this->convert_params_to_sql($what);
- $select .= ') AS t ';
- } else {
- $select = "SELECT COUNT(DISTINCT {$what['distinct']}) FROM $table ";
- $select .= $this->convert_params_to_sql($what);
- }
- } else {
- $select = "SELECT COUNT(*) FROM $table ";
- $select .= $this->convert_params_to_sql($what);
- }
- $num = $this->db->fetch_one($select);
- return $num[0];
- }
- /**
- * Realiza un promedio sobre el campo $what.
- *
- * @param string $what
- *
- * @return array
- */
- public function average($what = '')
- {
- $what = Util::getParams(func_get_args());
- if (isset($what['column'])) {
- if (!$what['column']) {
- $what['column'] = $what[0];
- }
- } else {
- $what['column'] = $what[0];
- }
- unset($what[0]);
- self::sql_item_sanitize($what['column']);
- if ($this->schema) {
- $table = "{$this->schema}.{$this->source}";
- } else {
- $table = $this->source;
- }
- $select = "SELECT AVG({$what['column']}) FROM $table ";
- $select .= $this->convert_params_to_sql($what);
- $num = $this->db->fetch_one($select);
- return $num[0];
- }
- public function sum($what = '')
- {
- $what = Util::getParams(func_get_args());
- if (isset($what['column'])) {
- if (!$what['column']) {
- $what['column'] = $what[0];
- }
- } else {
- $what['column'] = $what[0];
- }
- unset($what[0]);
- self::sql_item_sanitize($what['column']);
- if ($this->schema) {
- $table = "{$this->schema}.{$this->source}";
- } else {
- $table = $this->source;
- }
- $select = "SELECT SUM({$what['column']}) FROM $table ";
- $select .= $this->convert_params_to_sql($what);
- $num = $this->db->fetch_one($select);
- return $num[0];
- }
- /**
- * Busca el valor maximo para el campo $what.
- *
- * @param string $what
- *
- * @return mixed
- */
- public function maximum($what = '')
- {
- $what = Util::getParams(func_get_args());
- if (isset($what['column'])) {
- if (!$what['column']) {
- $what['column'] = $what[0];
- }
- } else {
- $what['column'] = $what[0];
- }
- unset($what[0]);
- self::sql_item_sanitize($what['column']);
- if ($this->schema) {
- $table = "{$this->schema}.{$this->source}";
- } else {
- $table = $this->source;
- }
- $select = "SELECT MAX({$what['column']}) FROM $table ";
- $select .= $this->convert_params_to_sql($what);
- $num = $this->db->fetch_one($select);
- return $num[0];
- }
- /**
- * Busca el valor minimo para el campo $what.
- *
- * @param string $what
- *
- * @return mixed
- */
- public function minimum($what = '')
- {
- $what = Util::getParams(func_get_args());
- if (isset($what['column'])) {
- if (!$what['column']) {
- $what['column'] = $what[0];
- }
- } else {
- $what['column'] = $what[0];
- }
- unset($what[0]);
- self::sql_item_sanitize($what['column']);
- if ($this->schema) {
- $table = "{$this->schema}.{$this->source}";
- } else {
- $table = $this->source;
- }
- $select = "SELECT MIN({$what['column']}) FROM $table ";
- $select .= $this->convert_params_to_sql($what);
- $num = $this->db->fetch_one($select);
- return $num[0];
- }
- /**
- * Realiza un conteo directo mediante $sql.
- *
- * @param string $sqlQuery
- *
- * @return mixed
- */
- public function count_by_sql($sqlQuery)
- {
- $num = $this->db->fetch_one($sqlQuery);
- return $num[0];
- }
- /**
- * Iguala los valores de un resultado de la base de datos
- * en un nuevo objeto con sus correspondientes
- * atributos de la clase.
- *
- * @param array $result
- *
- * @return ActiveRecord
- */
- public function dump_result($result)
- {
- $obj = clone $this;
- /*
- * Consulta si la clase es padre de otra y crea el tipo de dato correcto
- */
- if (isset($result['type'])) {
- if (in_array($result['type'], $this->parent_of)) {
- if (class_exists($result['type'])) {
- $obj = new $result['type']();
- unset($result['type']);
- }
- }
- }
- $this->_dump_lock = true;
- if (is_array($result)) {
- foreach ($result as $k => $r) {
- if (!is_numeric($k)) {
- if (!is_object($r)) {
- $obj->$k = stripslashes($r);
- } else {
- $obj->$k = $r->load();
- }
- }
- }
- }
- $this->_dump_lock = false;
- return $obj;
- }
- /**
- * Iguala los valores de un resultado de la base de datos
- * con sus correspondientes atributos de la clase.
- *
- * @param array $result
- *
- * @return ActiveRecord
- */
- public function dump_result_self($result)
- {
- $this->_dump_lock = true;
- if (is_array($result)) {
- foreach ($result as $k => $r) {
- if (!is_numeric($k)) {
- if (!is_object($r)) {
- $this->$k = is_array($r) ? $r : stripslashes($r);
- } else {
- $this->$k = $r->load();
- }
- }
- }
- }
- $this->_dump_lock = false;
- }
- /**
- * Crea un nuevo registro utilizando los datos del $_REQUEST.
- *
- * @param string $form, equivalente a $_REQUEST[$form]
- *
- * @return bool success
- *
- * @deprecated No es seguro
- */
- public function create_from_request($form = null)
- {
- if (!$form) {
- $form = $this->source;
- }
- return $this->create($_REQUEST[$form]);
- }
- /**
- * Saves a new Row using values from $_REQUEST.
- *
- * @param string $form form name for request, equivalent to $_REQUEST[$form]
- *
- * @return bool success
- *
- * @deprecated No es seguro
- */
- public function save_from_request($form = null)
- {
- if (!$form) {
- $form = $this->source;
- }
- return $this->save($_REQUEST[$form]);
- }
- /**
- * Updates a Row using values from $_REQUEST.
- *
- * @param string $form form name for request, equivalent to $_REQUEST[$form]
- *
- * @return bool|null success
- */
- public function update_from_request($form = null)
- {
- if (!$form) {
- $form = $this->source;
- }
- return $this->update($_REQUEST[$form]);
- }
- /**
- * Creates a new Row in map table.
- *
- * @param mixed $values
- *
- * @return bool success
- */
- public function create()
- {
- if (func_num_args() > 0) {
- $params = Util::getParams(func_get_args());
- $values = (isset($params[0]) && is_array($params[0])) ? $params[0] : $params;
- foreach ($this->fields as $field) {
- if (isset($values[$field])) {
- $this->$field = $values[$field];
- }
- }
- }
- if ($this->primary_key[0] == 'id') {
- $this->id = null;
- }
- return $this->save();
- }
- /**
- * Consulta si un determinado registro existe o no
- * en la entidad de la base de datos.
- *
- * @return bool
- */
- public function exists($where_pk = '')
- {
- if ($this->schema) {
- $table = "{$this->schema}.{$this->source}";
- } else {
- $table = $this->source;
- }
- if (!$where_pk) {
- $where_pk = array();
- foreach ($this->primary_key as $key) {
- if ($this->$key) {
- $where_pk[] = " $key = '{$this->$key}'";
- }
- }
- if (count($where_pk)) {
- $this->_where_pk = join(' AND ', $where_pk);
- } else {
- return 0;
- }
- $query = "SELECT COUNT(*) FROM $table WHERE {$this->_where_pk}";
- } else {
- if (is_numeric($where_pk)) {
- $query = "SELECT COUNT(*) FROM $table WHERE {$this->primary_key[0]} = '$where_pk'";
- } else {
- $query = "SELECT COUNT(*) FROM $table WHERE $where_pk";
- }
- }
- $num = $this->db->fetch_one($query);
- return $num[0];
- }
- /**
- * Saves Information on the ActiveRecord Properties.
- *
- * @param array $values array de valores a cargar
- *
- * @return bool success
- */
- public function save($values = null)
- {
- if ($values) {
- if (!is_array($values)) {
- $values = Util::getParams(func_get_args());
- }
- foreach ($this->fields as $field) {
- if (isset($values[$field])) {
- $this->$field = $values[$field];
- }
- }
- }
- $ex = $this->exists();
- if ($this->schema) {
- $table = $this->schema.'.'.$this->source;
- } else {
- $table = $this->source;
- }
- //Run Validation Callbacks Before
- if (method_exists($this, 'before_validation')) {
- if ($this->before_validation() == 'cancel') {
- return false;
- }
- } else {
- if (isset($this->before_validation)) {
- $method = $this->before_validation;
- if ($this->$method() == 'cancel') {
- return false;
- }
- }
- }
- if (!$ex) {
- if (method_exists($this, 'before_validation_on_create')) {
- if ($this->before_validation_on_create() == 'cancel') {
- return false;
- }
- } else {
- if (isset($this->before_validation_on_create)) {
- $method = $this->before_validation_on_create;
- if ($this->$method() == 'cancel') {
- return false;
- }
- }
- }
- }
- if ($ex) {
- if (method_exists($this, 'before_validation_on_update')) {
- if ($this->before_validation_on_update() == 'cancel') {
- return false;
- }
- } else {
- if (isset($this->before_validation_on_update)) {
- $method = $this->before_validation_on_update;
- if ($this->$method() == 'cancel') {
- return false;
- }
- }
- }
- }
- /*
- * Validacion validates_presence
- *
- */
- if (isset($this->_validates['presence_of'])) {
- foreach ($this->_validates['presence_of'] as $f => $opt) {
- if (isset($this->$f) && (is_null($this->$f) || $this->$f === '')) {
- if (!$ex && $f == $this->primary_key[0]) {
- continue;
- }
- if (isset($opt['message'])) {
- Flash::error($opt['message']);
- return false;
- } else {
- $field = isset($opt['field']) ? $opt['field'] : $f;
- Flash::error("Error: El campo $field no puede ser nulo");
- return false;
- }
- }
- }
- }
- /*
- * Recordamos que aqui no aparecen los que tienen valores por defecto,
- * pero sin embargo se debe estar pendiente de validar en las otras verificaciones
- * los campos nulos, ya que en estas si el campo es nulo, realmente se refiere a un campo que
- * debe tomar el valor por defecto
- *
- */
- foreach ($this->not_null as $f) {
- if (in_array($f, $this->_with_default)) {
- continue;
- }
- if (!isset($this->$f) || is_null($this->$f) || $this->$f == '') {
- if (!$ex && $f == $this->primary_key[0]) {
- continue;
- }
- if (!$ex && in_array($f, $this->_at)) {
- continue;
- }
- if ($ex && in_array($f, $this->_in)) {
- continue;
- }
- Flash::error("Error: El campo $f no puede ser nulo");
- return false;
- }
- }
- /*
- * Validacion validates_length
- *
- */
- if (isset($this->_validates['length_of'])) {
- foreach ($this->_validates['length_of'] as $f => $opt) {
- if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
- $field = isset($opt['field']) ? $opt['field'] : $f;
- if (strlen($this->$f) < $opt['min']) {
- if (isset($opt['too_short'])) {
- Flash::error($opt['too_short']);
- } else {
- Flash::error("Error: El campo $field debe tener como mínimo $opt[min] caracteres");
- }
- return false;
- }
- if (strlen($this->$f) > $opt['max']) {
- if (isset($opt['too_long'])) {
- Flash::error($opt['too_long']);
- } else {
- Flash::error("Error: El campo $field debe tener como máximo $opt[max] caracteres");
- }
- return false;
- }
- }
- }
- }
- /*
- * Validacion validates_inclusion
- *
- */
- foreach ($this->_validates['inclusion_in'] as $f => $opt) {
- if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
- if (!in_array($this->$f, $opt['list'])) {
- if (isset($opt['message'])) {
- Flash::error($opt['message']);
- } else {
- $field = isset($opt['field']) ? $opt['field'] : $f;
- Flash::error("$field debe tener un valor entre (".join(',', $opt['list']).')');
- }
- return false;
- }
- }
- }
- /*
- * Validacion validates_exclusion
- *
- */
- foreach ($this->_validates['exclusion_of'] as $f => $opt) {
- if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
- if (in_array($this->$f, $opt['list'])) {
- if (isset($opt['message'])) {
- Flash::error($opt['message']);
- } else {
- $field = isset($opt['field']) ? $opt['field'] : $f;
- Flash::error("$field no debe tener un valor entre (".join(',', $opt['list']).')');
- }
- return false;
- }
- }
- }
- /*
- * Validacion validates_numericality
- *
- */
- foreach ($this->_validates['numericality_of'] as $f => $opt) {
- if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
- if (!is_numeric($this->$f)) {
- if (isset($opt['message'])) {
- Flash::error($opt['message']);
- } else {
- $field = isset($opt['field']) ? $opt['field'] : $f;
- Flash::error("$field debe tener un valor numérico");
- }
- return false;
- }
- }
- }
- /*
- * Validacion validates_format
- *
- */
- foreach ($this->_validates['format_of'] as $f => $opt) {
- if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
- if (!filter_var($this->$f, FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => $opt['pattern'])))) {
- if (isset($opt['message'])) {
- Flash::error($opt['message']);
- } else {
- $field = isset($opt['field']) ? $opt['field'] : $f;
- Flash::error("Formato erroneo para $field");
- }
- return false;
- }
- }
- }
- /*
- * Validacion validates_date
- *
- */
- foreach ($this->_validates['date_in'] as $f => $opt) {
- if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
- if (!filter_var($this->$f, FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => "/^\d{4}[-\/](0[1-9]|1[012])[-\/](0[1-9]|[12][0-9]|3[01])$/")))) {
- if (isset($opt['message'])) {
- Flash::error($opt['message']);
- } else {
- $field = isset($opt['field']) ? $opt['field'] : $f;
- Flash::error("Formato de fecha erroneo para $field");
- }
- return false;
- }
- }
- }
- /*
- * Validacion validates_email
- *
- */
- foreach ($this->_validates['email_in'] as $f => $opt) {
- if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
- if (!filter_var($this->$f, FILTER_VALIDATE_EMAIL)) {
- if (isset($opt['message'])) {
- Flash::error($opt['message']);
- } else {
- $field = isset($opt['field']) ? $opt['field'] : $f;
- Flash::error("Formato de e-mail erroneo en el campo $field");
- }
- return false;
- }
- }
- }
- /**
- * Validacion validates_uniqueness.
- */
- // parche para que no tome encuenta el propio registro
- // al validar campos unicos, ya que si lo toma en cuenta
- // lanzará error de validacion porque ya existe un registro
- // con igual valor en el campo unico.
- $and_condition = $ex ? " AND {$this->primary_key[0]} != '{$this->{$this->primary_key[0]}}'" : '';
- foreach ($this->_validates['uniqueness_of'] as $f => $opt) {
- if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
- $result = $this->db->fetch_one("SELECT COUNT(*) FROM $table WHERE $f = {$this->db->add_quotes($this->$f)} $and_condition");
- if ($result[0]) {
- if (isset($opt['message'])) {
- Flash::error($opt['message']);
- } else {
- $field = isset($opt['field']) ? $opt['field'] : $f;
- Flash::error("El valor '{$this->$f}' ya existe para el campo $field");
- }
- return false;
- }
- }
- }
- //Run Validation Callbacks After
- if (!$ex) {
- if (method_exists($this, 'after_validation_on_create')) {
- if ($this->after_validation_on_create() == 'cancel') {
- return false;
- }
- } else {
- if (isset($this->after_validation_on_create)) {
- $method = $this->after_validation_on_create;
- if ($this->$method() == 'cancel') {
- return false;
- }
- }
- }
- }
- if ($ex) {
- if (method_exists($this, 'after_validation_on_update')) {
- if ($this->after_validati…
Large files files are truncated, but you can click here to view the full file