diff --git a/index.php b/index.php index 5d91b3a..d095f10 100644 --- a/index.php +++ b/index.php @@ -8,8 +8,8 @@ spl_autoload_register(function ($className) { $dir = dirname($fp); $file = ROOT_DIR.'/src/'.$dir.'/'.$name.'.php'; if (file_exists($file)) { - require_once $file; - return; + require_once $file; + return; } }); @@ -17,7 +17,7 @@ spl_autoload_register(function ($className) { $routers = glob(ROOT_DIR.'/src/Routers/*.php'); foreach($routers as $file){ - require_once($file); + require_once($file); } \Libs\Router::apply(); diff --git a/src/Libs/Database.php b/src/Libs/Database.php index 9ca9234..215bbb2 100644 --- a/src/Libs/Database.php +++ b/src/Libs/Database.php @@ -1,32 +1,32 @@ connect_errno) { - echo ''; - throw new \Exception('No se ha podido conectar a la base de datos.'); - } + static private $db; + + private function __construct() {} + + static public function getConnection() { + if (!isset(self::$db)) { + self::$db = new \mysqli(dbhost, dbuser, dbpass, dbname); + if (self::$db->connect_errno) { + echo ''; + throw new \Exception('No se ha podido conectar a la base de datos.'); + } + } + return self::$db; } - return self::$db; - } } ?> diff --git a/src/Libs/ModelMySQL.php b/src/Libs/ModelMySQL.php index 82576fd..0193b3b 100644 --- a/src/Libs/ModelMySQL.php +++ b/src/Libs/ModelMySQL.php @@ -1,14 +1,14 @@ ['*'], - 'where' => '', - 'from' => '', - 'leftJoin' => '', - 'rightJoin' => '', - 'innerJoin' => '', - 'AndOr' => '', - 'orderBy' => '', - 'groupBy' => '', - 'limit' => '', - 'sql_calc_found_rows' => false - ]; + static protected $primaryKey = 'id'; + static protected $ignoreSave = ['id']; + static protected $forceSave = []; + static protected $table; + static protected $tableSufix = 's'; + static protected $db; + static protected $querySelect = [ + 'select' => ['*'], + 'where' => '', + 'from' => '', + 'leftJoin' => '', + 'rightJoin' => '', + 'innerJoin' => '', + 'AndOr' => '', + 'orderBy' => '', + 'groupBy' => '', + 'limit' => '', + 'sql_calc_found_rows' => false + ]; - /* - * Sirve para obtener la instancia de la base de datos - * - * @return mysqli - */ - protected static function db() { - if (is_null(static::$db)) - static::$db = Database::getConnection(); + /* + * Sirve para obtener la instancia de la base de datos + * + * @return mysqli + */ + protected static function db() { + if (is_null(static::$db)) + static::$db = Database::getConnection(); - return static::$db; - } - - /* - * Ejecuta una sentencia SQL en la base de datos. - * - * @param string $query - * Contiene la sentencia SQL que se desea ejecutar - * - * @throws \Exception - * En caso de que la sentencia SQL falle, devolverá un error en pantalla. - * - * @return mysqli_result - * Contiene el resultado de la llamada SQL. - */ - protected static function query($query) { - $db = static::db(); - - $result = $db->query($query); - if ($db->errno) { - echo ''; - throw new \Exception( - "\nFallo al consultar la base de datos\n" . - "Errno: $db->errno\n" . - "Error: $db->error\n" . - "Query: $query\n" - ); + return static::$db; } - return $result; - } + /* + * Ejecuta una sentencia SQL en la base de datos. + * + * @param string $query + * Contiene la sentencia SQL que se desea ejecutar + * + * @throws \Exception + * En caso de que la sentencia SQL falle, devolverá un error en pantalla. + * + * @return mysqli_result + * Contiene el resultado de la llamada SQL. + */ + protected static function query($query) { + $db = static::db(); - /* - * Reinicia la configuración de la sentencia SQL. - */ - protected static function resetQuery() { - static::$querySelect = [ - 'select' => ['*'], - 'where' => '', - 'from' => '', - 'leftJoin' => '', - 'rightJoin' => '', - 'innerJoin' => '', - 'AndOr' => '', - 'orderBy' => '', - 'groupBy' => '', - 'limit' => '', - 'sql_calc_found_rows' => false - ]; - } + $result = $db->query($query); + if ($db->errno) { + echo ''; + throw new \Exception( + "\nFallo al consultar la base de datos\n" . + "Errno: $db->errno\n" . + "Error: $db->error\n" . + "Query: $query\n" + ); + } - /* - * Construye la sentencia SQL a partir static::$querySelect y una vez - * construída, llama a resetQuery. - * - * @param boolean $resetQuery - * Indica si el query debe reiniciarse o no (por defecto es true). - * - * @return string - * Contiene la sentencia SQL. - */ - protected static function buildQuery($resetQuery = true) { - if (static::$querySelect['sql_calc_found_rows']) - $sql = 'SELECT SQL_CALC_FOUND_ROWS '.join(', ', static::$querySelect['select']); - else - $sql = 'SELECT '.join(', ', static::$querySelect['select']); - - if (static::$querySelect['from'] != '') { - $sql .= ' FROM '.static::$querySelect['from']; - } else { - $sql .= ' FROM '.static::table(); + return $result; } - if(static::$querySelect['innerJoin'] != '') { - $sql .= static::$querySelect['innerJoin']; - } - - if (static::$querySelect['leftJoin'] != '') { - $sql .= static::$querySelect['leftJoin']; - } - - if(static::$querySelect['rightJoin'] != '') { - $sql .= static::$querySelect['rightJoin']; - } - - if (static::$querySelect['where'] != '') { - $sql .= ' WHERE '.static::$querySelect['where']; - - if (static::$querySelect['AndOr'] != '') { - $sql .= static::$querySelect['AndOr']; - } - } - - if (static::$querySelect['groupBy'] != '') { - $sql .= ' GROUP BY '.static::$querySelect['groupBy']; - } - - if (static::$querySelect['orderBy'] != '') { - $sql .= ' ORDER BY '.static::$querySelect['orderBy']; - } - - if (static::$querySelect['limit'] != '') { - $sql .= ' LIMIT '.static::$querySelect['limit']; - } - - if ($resetQuery) - static::resetQuery(); - - return $sql; - } - - /* - * Crea una instancia del objeto actual a partir de un arreglo. - * - * @param mixed $elem - * Puede recibir un arreglo o un objeto que contiene los valores - * que tendrán sus atributos. - * - * @return ModelMySQL - * Retorna un objeto de la clase actual. - */ - protected static function getInstance($elem = []) { - $class = get_called_class(); - $instance = new $class; - - foreach ($elem as $key => $value) { - $instance->$key = $value; - } - - return $instance; - } - - /* - * @return array - * Contiene los atributos indexados del objeto actual. - */ - protected function getVars() { - $reflection = new \ReflectionClass($this); - $properties = $reflection->getProperties(\ReflectionProperty::IS_PUBLIC); - $result = []; - - foreach($properties as $property) { - $att = $property->name; - $result[$att] = $this->$att; - } - - foreach (static::$ignoreSave as $del) { - unset($result[$del]); - } - - foreach (static::$forceSave as $value) { - $result[$value] = $this->$value; - } - - return $result; - } - - /* - * @return string - * Devuelve el nombre de la clase actual - */ - public static function className() { - return strtolower(substr(strrchr(get_called_class(), '\\'), 1)); - } - - /* - * Construye (a partir del nombre de la clase y el sufijo en static::$tableSufix) - * y/o develve el nombre de la tabla de la BD en la que se alojará o - * se aloja el objeto actual. - * - * @return string - */ - protected static function table() { - if (isset(static::$table)) - return static::$table; - return static::className().static::$tableSufix; - } - - /* - * Actualiza los valores en la BD con los valores del objeto actual - */ - protected function update() { - $atts = $this->getVars(); - - foreach ($atts as $key => $value) { - if (isset($value)) { - $value = static::db()->real_escape_string($value); - if (in_array($key, $this->toNull)) - $set[]="$key=NULL"; - else - $set[]="$key='$value'"; - } else { - if (in_array($key, $this->toNull)) - $set[]="$key=NULL"; - } - } - - $table = static::table(); - $pk = static::$primaryKey; - $pkv = $this->$pk; - $sql = "UPDATE $table SET ".join(', ', $set)." WHERE $pk='$pkv'"; - static::query($sql); - } - - /* - * Inserta una nueva fila en la base de datos a partir del - * objeto actual. - */ - protected function add() { - $db = static::db(); - $atts = $this->getVars(); - - foreach ($atts as $key => $value) { - if (isset($value)) { - $into[] = "`$key`"; - $values[] = "'".$db->real_escape_string($value)."'"; - } - } - - $table = static::table(); - $sql = "INSERT INTO $table (".join(', ', $into).") VALUES (".join(', ', $values).")"; - static::query($sql); - - $pk = static::$primaryKey; - $this->$pk = $db->insert_id; - } - - /* - * Revisa si el objeto a guardar es nuevo o no y según el resultado - * llama a update para actualizar o add para insertar una nueva fila. - */ - public function save() { - $pk = static::$primaryKey; - if (isset($this->$pk)) - $this->update(); - else - $this->add(); - } - - /* - * Elimina el objeto actual de la base de datos. - */ - public function delete() { - $atts = $this->getVars(); - - foreach ($atts as $key => $value) { - $value = static::db()->real_escape_string($value); - $set[]="$key='$value'"; - } - - $table = static::table(); - $pk = static::$primaryKey; - $pkv = $this->$pk; - $sql = "DELETE FROM $table WHERE $pk='$pkv'"; - static::query($sql); - } - - /* - * Define SELECT en la sentencia SQL. - * - * @param array $columns - * Columnas que se selecionarán en la consulta SQL. - */ - public static function select($columns) { - $db = static::db(); - $select = []; - foreach($columns as $column) { - $select[] = $db->real_escape_string($column); - } - - static::$querySelect['select'] = $select; - - return new static(); - } - - /* - * Define FROM en la sentencia SQL. - * - * @param array $tables - * Tablas que se selecionarán en la consulta SQL. - */ - public static function from($tables) { - $db = static::db(); - $from = []; - foreach($tables as $table) { - $from[] = $db->real_escape_string($table); - } - - static::$querySelect['from'] = join(', ', $from); - - return new static(); - } - - /* - * Define el WHERE en la sentencia SQL. - * - * @param string $column - * La columna a comparar. - * - * @param string $operador - * El operador o el valor a comparar en la columna en caso de que eloperador sea "=". - * - * @param string $value - * El valor el valor a comparar en la columna. - * - * @param $no_quote - * Se usa cuando $value es una columna o un valor que no requiere comillas - * - * Sintaxis posibles: - * - static::where(columna, operador, valor) - * - static::where(columna, valor) // Operador por defecto "=" - */ - public static function where($column, $operator, $value=null, $no_quote = false) { - if (is_null($value)) { - $value = $operator; - $operator = '='; - } - - $value = static::db()->real_escape_string($value); - - if ($no_quote) - static::$querySelect['where'] = "$column$operator$value"; - else - static::$querySelect['where'] = "$column$operator'$value'"; - - return new static(); - } - - /* - * Define WHERE usando IN en la sentencia SQL. - * - * @param string $column - * La columna a comparar. - * - * @param array $arr - * Arreglo con todos los valores a comparar con la columna. - * - * @param boolean $in - * Define si se usará IN o NOT IN en la sentencia SQL. - */ - public static function where_in($column,$arr, $in = true) { - foreach($arr as $index => $value) { - $arr[$index] = static::db()->real_escape_string($value); - } - - if ($in) - static::$querySelect['where'] = "$column IN (".join(', ',$arr).")"; - else - static::$querySelect['where'] = "$column NOT IN (".join(', ',$arr).")"; - - return new static(); - } - - /* - * Define LEFT JOIN en la sentencia SQL. - * - * @param string $table - * Tabla que se va a juntar a la del objeto actual. - * - * @param string $columnA - * Columna a comparar para hacer el join. - * - * @param string $operador - * Operador o columna a comparar para hacer el join en caso de que el operador sea "=". - * - * @param string $columnB - * Columna a comparar para hacer el join. - * - * Sintaxis posibles: - * - static::leftJoin(tabla,columnaA, operador, columnB) - * - static::leftJoin(tabla,columnaA, columnB) // Operador por defecto "=" - */ - public static function leftJoin($table, $columnA, $operator, $columnB = null) { - if (is_null($columnB)) { - $columnB = $operator; - $operator = '='; - } - - $columnA = static::db()->real_escape_string($columnA); - $columnB = static::db()->real_escape_string($columnB); - - static::$querySelect['leftJoin'] .= ' LEFT JOIN ' . $table . ' ON ' . "$columnA$operator$columnB"; - - - return new static(); - } - - /* - * Define RIGHT JOIN en la sentencia SQL. - * - * @param string $table - * Tabla que se va a juntar a la del objeto actual. - * - * @param string $columnA - * Columna a comparar para hacer el join. - * - * @param string $operador - * Operador o columna a comparar para hacer el join en caso de que el operador sea "=". - * - * @param string $columnB - * Columna a comparar para hacer el join. - * - * Sintaxis posibles: - * - static::rightJoin(tabla,columnaA, operador, columnB) - * - static::rightJoin(tabla,columnaA, columnB) // Operador por defecto "=" - */ - public static function rightJoin($table, $columnA, $operator, $columnB = null) { - if (is_null($columnB)) { - $columnB = $operator; - $operator = '='; - } - - $columnA = static::db()->real_escape_string($columnA); - $columnB = static::db()->real_escape_string($columnB); - - static::$querySelect['rightJoin'] .= ' RIGHT JOIN ' . $table . ' ON ' . "$columnA$operator$columnB"; - - return new static(); - } - - /* - * Define INNER JOIN en la sentencia SQL. - * - * @param string $table - * Tabla que se va a juntar a la del objeto actual. - * - * @param string $columnA - * Columna a comparar para hacer el join. - * - * @param string $operador - * Operador o columna a comparar para hacer el join en caso de que el operador sea "=". - * - * @param string $columnB - * Columna a comparar para hacer el join. - * - * Sintaxis posibles: - * - static::innerJoin(tabla,columnaA, operador, columnB) - * - static::innerJoin(tabla,columnaA, columnB) // Operador por defecto "=" - */ - public static function innerJoin($table, $columnA, $operator, $columnB = null) { - if (is_null($columnB)) { - $columnB = $operator; - $operator = '='; - } - - $columnA = static::db()->real_escape_string($columnA); - $columnB = static::db()->real_escape_string($columnB); - - static::$querySelect['innerJoin'] .= ' INNER JOIN ' . $table . ' ON ' . "$columnA$operator$columnB"; - - return new static(); - } - - /* - * Define AND en la sentencia SQL (se puede anidar). - * - * @param string $column - * La columna a comparar. - * - * @param string $operador - * El operador o el valor a comparar en la columna en caso de que eloperador sea "=". - * - * @param string $value - * El valor el valor a comparar en la columna. - * - * @param $no_quote - * Se usa cuando $value es una columna o un valor que no requiere comillas - * - * Sintaxis posibles: - * - static::and(columna, operador, valor) - * - static::and(columna, valor) // Operador por defecto "=" - * - static::and(columna, valor)->and(columna, valor)->and(columna, valor) // anidado - */ - public static function and($column, $operator, $value=null, $no_quote = false) { - if (is_null($value)) { - $value = $operator; - $operator = '='; - } - - $value = static::db()->real_escape_string($value); - - if ($no_quote) - static::$querySelect['AndOr'] .= " AND $column$operator$value"; - else - static::$querySelect['AndOr'] .= " AND $column$operator'$value'"; - - return new static(); - } - - /* - * Define OR en la sentencia SQL (se puede anidar). - * - * @param string $column - * La columna a comparar. - * - * @param string $operador - * El operador o el valor a comparar en la columna en caso de que eloperador sea "=". - * - * @param string $value - * El valor el valor a comparar en la columna. - * - * @param $no_quote - * Se usa cuando $value es una columna o un valor que no requiere comillas - * - * Sintaxis posibles: - * - static::or(columna, operador, valor) - * - static::or(columna, valor) // Operador por defecto "=" - * - static::or(columna, valor)->or(columna, valor)->or(columna, valor) // anidado - */ - public static function or($column, $operator, $value=null, $no_quote = false) { - if (is_null($value)) { - $value = $operator; - $operator = '='; - } - - $value = static::db()->real_escape_string($value); - - if ($no_quote) - static::$querySelect['AndOr'] .= " OR $column$operator$value"; - else - static::$querySelect['AndOr'] .= " OR $column$operator'$value'"; - - return new static(); - } - - /* - * Define GROUP BY en la sentencia SQL - * - * @param array $arr - * Columnas por las que se agrupará. - */ - public static function groupBy($arr) { - static::$querySelect['groupBy'] = join(', ', $arr); - return new static(); - } - - public static function limit($initial, $final = 0) { - $initial = (int)$initial; - $final = (int)$final; - - if ($final==0) - static::$querySelect['limit'] = $initial; - else - static::$querySelect['limit'] = $initial.', '.$final; - - return new static(); - } - - /* - * Define ORDER BY en la sentencia SQL - * - * @param string $value - * Columna por la que se ordenará. - * - * @param string $order - * Define si el orden será de manera ascendente (ASC), - * descendente (DESC) o aleatorio (RAND). - */ - public static function orderBy($value, $order = 'ASC') { - if ($value == "RAND") { - static::$querySelect['orderBy'] = 'RAND()'; - return new static(); - } - - $value = static::db()->real_escape_string($value); - - if (!(strtoupper($order) == 'ASC' || strtoupper($order) == 'DESC')) - $order = 'ASC'; - - static::$querySelect['orderBy'] = $value.' '.$order; - - return new static(); - } - - /* - * Retorna la cantidad de filas que hay en un query. - * - * @param boolean $resetQuery - * Indica si el query debe reiniciarse o no (por defecto es true). - * - * @param boolean $useLimit - * Permite usar limit para estabecer un máximo inical y final para contar. Requiere que se haya definido antes el límite. - * - * @return int - */ - public static function count($resetQuery = true, $useLimit = false) { - if (!$resetQuery) - $backup = [ - 'select' => static::$querySelect['select'], - 'sql_calc_found_rows' => static::$querySelect['sql_calc_found_rows'], - 'limit' => static::$querySelect['limit'], - 'orderBy' => static::$querySelect['orderBy'] + /* + * Reinicia la configuración de la sentencia SQL. + */ + protected static function resetQuery() { + static::$querySelect = [ + 'select' => ['*'], + 'where' => '', + 'from' => '', + 'leftJoin' => '', + 'rightJoin' => '', + 'innerJoin' => '', + 'AndOr' => '', + 'orderBy' => '', + 'groupBy' => '', + 'limit' => '', + 'sql_calc_found_rows' => false ]; - - if ($useLimit && static::$querySelect['limit'] != '') { - static::$querySelect['select'] = ['1']; - static::$querySelect['sql_calc_found_rows'] = false; - static::$querySelect['orderBy'] = ''; - - $sql = 'SELECT COUNT(1) AS quantity FROM ('.static::buildQuery($resetQuery).') AS counted'; - $queryResult = static::query($sql)->fetch_assoc(); - $result = $queryResult['quantity']; - } else { - static::$querySelect['select'] = ['1']; - static::$querySelect['sql_calc_found_rows'] = true; - static::$querySelect['limit'] = '1'; - static::$querySelect['orderBy'] = ''; - - $sql = static::buildQuery($resetQuery); - static::query($sql); - $result = static::found_row(); } - if (!$resetQuery) { - static::$querySelect['select'] = $backup['select']; - static::$querySelect['sql_calc_found_rows'] = $backup['sql_calc_found_rows']; - static::$querySelect['limit'] = $backup['limit']; - static::$querySelect['orderBy'] = $backup['orderBy']; + /* + * Construye la sentencia SQL a partir static::$querySelect y una vez + * construída, llama a resetQuery. + * + * @param boolean $resetQuery + * Indica si el query debe reiniciarse o no (por defecto es true). + * + * @return string + * Contiene la sentencia SQL. + */ + protected static function buildQuery($resetQuery = true) { + if (static::$querySelect['sql_calc_found_rows']) + $sql = 'SELECT SQL_CALC_FOUND_ROWS '.join(', ', static::$querySelect['select']); + else + $sql = 'SELECT '.join(', ', static::$querySelect['select']); + + if (static::$querySelect['from'] != '') { + $sql .= ' FROM '.static::$querySelect['from']; + } else { + $sql .= ' FROM '.static::table(); + } + + if(static::$querySelect['innerJoin'] != '') { + $sql .= static::$querySelect['innerJoin']; + } + + if (static::$querySelect['leftJoin'] != '') { + $sql .= static::$querySelect['leftJoin']; + } + + if(static::$querySelect['rightJoin'] != '') { + $sql .= static::$querySelect['rightJoin']; + } + + if (static::$querySelect['where'] != '') { + $sql .= ' WHERE '.static::$querySelect['where']; + + if (static::$querySelect['AndOr'] != '') { + $sql .= static::$querySelect['AndOr']; + } + } + + if (static::$querySelect['groupBy'] != '') { + $sql .= ' GROUP BY '.static::$querySelect['groupBy']; + } + + if (static::$querySelect['orderBy'] != '') { + $sql .= ' ORDER BY '.static::$querySelect['orderBy']; + } + + if (static::$querySelect['limit'] != '') { + $sql .= ' LIMIT '.static::$querySelect['limit']; + } + + if ($resetQuery) + static::resetQuery(); + + return $sql; } - return $result; - } + /* + * Crea una instancia del objeto actual a partir de un arreglo. + * + * @param mixed $elem + * Puede recibir un arreglo o un objeto que contiene los valores + * que tendrán sus atributos. + * + * @return ModelMySQL + * Retorna un objeto de la clase actual. + */ + protected static function getInstance($elem = []) { + $class = get_called_class(); + $instance = new $class; - /* - * Retorna las filas contadas en el último query. - * - * @return int - */ - public static function found_row() { - $result = static::query('SELECT FOUND_ROWS() AS quantity')->fetch_assoc(); - return $result['quantity']; - } + foreach ($elem as $key => $value) { + $instance->$key = $value; + } - /* - * Habilita el conteo de todos las coincidencias posibles incluso usando limit. - * - * @return ModelMySQL - */ - public static function sql_calc_found_rows() { - static::$querySelect['sql_calc_found_rows'] = true; - return new static(); - } - - /* - * Obtiene una instancia según su primary key (generalmente id). - * - * @param mixed $id - * @return ModelMySQL - */ - public static function getById($id) { - return static::where(static::$primaryKey, $id)->getFirst(); - } - - /* - * Realiza una búsqueda en la tabla de la instancia actual. - * - * @param string $search - * Contenido a buscar. - * - * @param array $in - * Columnas en las que se va a buscar (null para buscar en todas) - */ - public static function search($search, $in = null) { - if ($in == null) { - $className = get_called_class(); - $in = array_keys((new $className())->getVars()); + return $instance; } - $db = static::db(); + /* + * @return array + * Contiene los atributos indexados del objeto actual. + */ + protected function getVars() { + $reflection = new \ReflectionClass($this); + $properties = $reflection->getProperties(\ReflectionProperty::IS_PUBLIC); + $result = []; - $search = $db->real_escape_string($search); + foreach($properties as $property) { + $att = $property->name; + $result[$att] = $this->$att; + } - $where = []; + foreach (static::$ignoreSave as $del) { + unset($result[$del]); + } - foreach($in as $row) { - $where[] = "$row LIKE '%$search%'"; + foreach (static::$forceSave as $value) { + $result[$value] = $this->$value; + } + + return $result; } - if (static::$querySelect['where']=='') - static::$querySelect['where'] = join(' OR ', $where); - else - static::$querySelect['where'] = static::$querySelect['where'] .' AND ('.join(' OR ', $where).')'; - - return new static(); - } - - /* - * Obtener los resultados de la consulta SQL. - * - * @param boolean $resetQuery - * Indica si el query debe reiniciarse o no (por defecto es true). - * - * @return ModelMySQL[] - */ - public static function get($resetQuery = true) { // Devuelve array vacío si no encuentra nada - $sql = static::buildQuery($resetQuery); - $result = static::query($sql); - - $instances = []; - - while ($row = $result->fetch_assoc()) { - $instances[] = static::getInstance($row); + /* + * @return string + * Devuelve el nombre de la clase actual + */ + public static function className() { + return strtolower(substr(strrchr(get_called_class(), '\\'), 1)); } - return $instances; - } - - /* - * El primer elemento de la consulta SQL. - * - * @param boolean $resetQuery - * Indica si el query debe reiniciarse o no (por defecto es true). - * - * @return mixed - * Puede retornar un objeto ModelMySQL o null. - */ - public static function getFirst($resetQuery = true) { // Devuelve null si no encuentra nada - static::limit(1); - $instances = static::get($resetQuery); - return empty($instances) ? null : $instances[0]; - } - - /* - * Obtener todos los elementos del la tabla de la instancia actual. - * - * @return ModelMySQL[] - */ - public static function all() { - $sql = 'SELECT * FROM '.static::table(); - - $result = static::query($sql); - - $instances = []; - - while ($row = $result->fetch_assoc()) { - $instances[] = static::getInstance($row); + /* + * Construye (a partir del nombre de la clase y el sufijo en static::$tableSufix) + * y/o develve el nombre de la tabla de la BD en la que se alojará o + * se aloja el objeto actual. + * + * @return string + */ + protected static function table() { + if (isset(static::$table)) + return static::$table; + return static::className().static::$tableSufix; } - return $instances; - } + /* + * Actualiza los valores en la BD con los valores del objeto actual + */ + protected function update() { + $atts = $this->getVars(); - /* - * Permite definir como nulo el valor de un atributo. - * Sólo funciona para actualizar un elemento de la BD, no para insertar. - * - * @param array $atts - */ - public function setNull($atts) { - if (!isset($this->id)) - throw new \Exception( - "\nEl método setNull sólo funciona para actualizar, no al insertar." - ); + foreach ($atts as $key => $value) { + if (isset($value)) { + $value = static::db()->real_escape_string($value); + if (in_array($key, $this->toNull)) + $set[]="$key=NULL"; + else + $set[]="$key='$value'"; + } else { + if (in_array($key, $this->toNull)) + $set[]="$key=NULL"; + } + } - foreach ($atts as $att) { - if (!in_array($att, $this->toNull)) - $this->toNull[] = $att; + $table = static::table(); + $pk = static::$primaryKey; + $pkv = $this->$pk; + $sql = "UPDATE $table SET ".join(', ', $set)." WHERE $pk='$pkv'"; + static::query($sql); + } + + /* + * Inserta una nueva fila en la base de datos a partir del + * objeto actual. + */ + protected function add() { + $db = static::db(); + $atts = $this->getVars(); + + foreach ($atts as $key => $value) { + if (isset($value)) { + $into[] = "`$key`"; + $values[] = "'".$db->real_escape_string($value)."'"; + } + } + + $table = static::table(); + $sql = "INSERT INTO $table (".join(', ', $into).") VALUES (".join(', ', $values).")"; + static::query($sql); + + $pk = static::$primaryKey; + $this->$pk = $db->insert_id; + } + + /* + * Revisa si el objeto a guardar es nuevo o no y según el resultado + * llama a update para actualizar o add para insertar una nueva fila. + */ + public function save() { + $pk = static::$primaryKey; + if (isset($this->$pk)) + $this->update(); + else + $this->add(); + } + + /* + * Elimina el objeto actual de la base de datos. + */ + public function delete() { + $atts = $this->getVars(); + + foreach ($atts as $key => $value) { + $value = static::db()->real_escape_string($value); + $set[]="$key='$value'"; + } + + $table = static::table(); + $pk = static::$primaryKey; + $pkv = $this->$pk; + $sql = "DELETE FROM $table WHERE $pk='$pkv'"; + static::query($sql); + } + + /* + * Define SELECT en la sentencia SQL. + * + * @param array $columns + * Columnas que se selecionarán en la consulta SQL. + */ + public static function select($columns) { + $db = static::db(); + $select = []; + foreach($columns as $column) { + $select[] = $db->real_escape_string($column); + } + + static::$querySelect['select'] = $select; + + return new static(); + } + + /* + * Define FROM en la sentencia SQL. + * + * @param array $tables + * Tablas que se selecionarán en la consulta SQL. + */ + public static function from($tables) { + $db = static::db(); + $from = []; + foreach($tables as $table) { + $from[] = $db->real_escape_string($table); + } + + static::$querySelect['from'] = join(', ', $from); + + return new static(); + } + + /* + * Define el WHERE en la sentencia SQL. + * + * @param string $column + * La columna a comparar. + * + * @param string $operador + * El operador o el valor a comparar en la columna en caso de que eloperador sea "=". + * + * @param string $value + * El valor el valor a comparar en la columna. + * + * @param $no_quote + * Se usa cuando $value es una columna o un valor que no requiere comillas + * + * Sintaxis posibles: + * - static::where(columna, operador, valor) + * - static::where(columna, valor) // Operador por defecto "=" + */ + public static function where($column, $operator, $value=null, $no_quote = false) { + if (is_null($value)) { + $value = $operator; + $operator = '='; + } + + $value = static::db()->real_escape_string($value); + + if ($no_quote) + static::$querySelect['where'] = "$column$operator$value"; + else + static::$querySelect['where'] = "$column$operator'$value'"; + + return new static(); + } + + /* + * Define WHERE usando IN en la sentencia SQL. + * + * @param string $column + * La columna a comparar. + * + * @param array $arr + * Arreglo con todos los valores a comparar con la columna. + * + * @param boolean $in + * Define si se usará IN o NOT IN en la sentencia SQL. + */ + public static function where_in($column,$arr, $in = true) { + foreach($arr as $index => $value) { + $arr[$index] = static::db()->real_escape_string($value); + } + + if ($in) + static::$querySelect['where'] = "$column IN (".join(', ',$arr).")"; + else + static::$querySelect['where'] = "$column NOT IN (".join(', ',$arr).")"; + + return new static(); + } + + /* + * Define LEFT JOIN en la sentencia SQL. + * + * @param string $table + * Tabla que se va a juntar a la del objeto actual. + * + * @param string $columnA + * Columna a comparar para hacer el join. + * + * @param string $operador + * Operador o columna a comparar para hacer el join en caso de que el operador sea "=". + * + * @param string $columnB + * Columna a comparar para hacer el join. + * + * Sintaxis posibles: + * - static::leftJoin(tabla,columnaA, operador, columnB) + * - static::leftJoin(tabla,columnaA, columnB) // Operador por defecto "=" + */ + public static function leftJoin($table, $columnA, $operator, $columnB = null) { + if (is_null($columnB)) { + $columnB = $operator; + $operator = '='; + } + + $columnA = static::db()->real_escape_string($columnA); + $columnB = static::db()->real_escape_string($columnB); + + static::$querySelect['leftJoin'] .= ' LEFT JOIN ' . $table . ' ON ' . "$columnA$operator$columnB"; + + + return new static(); + } + + /* + * Define RIGHT JOIN en la sentencia SQL. + * + * @param string $table + * Tabla que se va a juntar a la del objeto actual. + * + * @param string $columnA + * Columna a comparar para hacer el join. + * + * @param string $operador + * Operador o columna a comparar para hacer el join en caso de que el operador sea "=". + * + * @param string $columnB + * Columna a comparar para hacer el join. + * + * Sintaxis posibles: + * - static::rightJoin(tabla,columnaA, operador, columnB) + * - static::rightJoin(tabla,columnaA, columnB) // Operador por defecto "=" + */ + public static function rightJoin($table, $columnA, $operator, $columnB = null) { + if (is_null($columnB)) { + $columnB = $operator; + $operator = '='; + } + + $columnA = static::db()->real_escape_string($columnA); + $columnB = static::db()->real_escape_string($columnB); + + static::$querySelect['rightJoin'] .= ' RIGHT JOIN ' . $table . ' ON ' . "$columnA$operator$columnB"; + + return new static(); + } + + /* + * Define INNER JOIN en la sentencia SQL. + * + * @param string $table + * Tabla que se va a juntar a la del objeto actual. + * + * @param string $columnA + * Columna a comparar para hacer el join. + * + * @param string $operador + * Operador o columna a comparar para hacer el join en caso de que el operador sea "=". + * + * @param string $columnB + * Columna a comparar para hacer el join. + * + * Sintaxis posibles: + * - static::innerJoin(tabla,columnaA, operador, columnB) + * - static::innerJoin(tabla,columnaA, columnB) // Operador por defecto "=" + */ + public static function innerJoin($table, $columnA, $operator, $columnB = null) { + if (is_null($columnB)) { + $columnB = $operator; + $operator = '='; + } + + $columnA = static::db()->real_escape_string($columnA); + $columnB = static::db()->real_escape_string($columnB); + + static::$querySelect['innerJoin'] .= ' INNER JOIN ' . $table . ' ON ' . "$columnA$operator$columnB"; + + return new static(); + } + + /* + * Define AND en la sentencia SQL (se puede anidar). + * + * @param string $column + * La columna a comparar. + * + * @param string $operador + * El operador o el valor a comparar en la columna en caso de que eloperador sea "=". + * + * @param string $value + * El valor el valor a comparar en la columna. + * + * @param $no_quote + * Se usa cuando $value es una columna o un valor que no requiere comillas + * + * Sintaxis posibles: + * - static::and(columna, operador, valor) + * - static::and(columna, valor) // Operador por defecto "=" + * - static::and(columna, valor)->and(columna, valor)->and(columna, valor) // anidado + */ + public static function and($column, $operator, $value=null, $no_quote = false) { + if (is_null($value)) { + $value = $operator; + $operator = '='; + } + + $value = static::db()->real_escape_string($value); + + if ($no_quote) + static::$querySelect['AndOr'] .= " AND $column$operator$value"; + else + static::$querySelect['AndOr'] .= " AND $column$operator'$value'"; + + return new static(); + } + + /* + * Define OR en la sentencia SQL (se puede anidar). + * + * @param string $column + * La columna a comparar. + * + * @param string $operador + * El operador o el valor a comparar en la columna en caso de que eloperador sea "=". + * + * @param string $value + * El valor el valor a comparar en la columna. + * + * @param $no_quote + * Se usa cuando $value es una columna o un valor que no requiere comillas + * + * Sintaxis posibles: + * - static::or(columna, operador, valor) + * - static::or(columna, valor) // Operador por defecto "=" + * - static::or(columna, valor)->or(columna, valor)->or(columna, valor) // anidado + */ + public static function or($column, $operator, $value=null, $no_quote = false) { + if (is_null($value)) { + $value = $operator; + $operator = '='; + } + + $value = static::db()->real_escape_string($value); + + if ($no_quote) + static::$querySelect['AndOr'] .= " OR $column$operator$value"; + else + static::$querySelect['AndOr'] .= " OR $column$operator'$value'"; + + return new static(); + } + + /* + * Define GROUP BY en la sentencia SQL + * + * @param array $arr + * Columnas por las que se agrupará. + */ + public static function groupBy($arr) { + static::$querySelect['groupBy'] = join(', ', $arr); + return new static(); + } + + public static function limit($initial, $final = 0) { + $initial = (int)$initial; + $final = (int)$final; + + if ($final==0) + static::$querySelect['limit'] = $initial; + else + static::$querySelect['limit'] = $initial.', '.$final; + + return new static(); + } + + /* + * Define ORDER BY en la sentencia SQL + * + * @param string $value + * Columna por la que se ordenará. + * + * @param string $order + * Define si el orden será de manera ascendente (ASC), + * descendente (DESC) o aleatorio (RAND). + */ + public static function orderBy($value, $order = 'ASC') { + if ($value == "RAND") { + static::$querySelect['orderBy'] = 'RAND()'; + return new static(); + } + + $value = static::db()->real_escape_string($value); + + if (!(strtoupper($order) == 'ASC' || strtoupper($order) == 'DESC')) + $order = 'ASC'; + + static::$querySelect['orderBy'] = $value.' '.$order; + + return new static(); + } + + /* + * Retorna la cantidad de filas que hay en un query. + * + * @param boolean $resetQuery + * Indica si el query debe reiniciarse o no (por defecto es true). + * + * @param boolean $useLimit + * Permite usar limit para estabecer un máximo inical y final para contar. Requiere que se haya definido antes el límite. + * + * @return int + */ + public static function count($resetQuery = true, $useLimit = false) { + if (!$resetQuery) + $backup = [ + 'select' => static::$querySelect['select'], + 'sql_calc_found_rows' => static::$querySelect['sql_calc_found_rows'], + 'limit' => static::$querySelect['limit'], + 'orderBy' => static::$querySelect['orderBy'] + ]; + + if ($useLimit && static::$querySelect['limit'] != '') { + static::$querySelect['select'] = ['1']; + static::$querySelect['sql_calc_found_rows'] = false; + static::$querySelect['orderBy'] = ''; + + $sql = 'SELECT COUNT(1) AS quantity FROM ('.static::buildQuery($resetQuery).') AS counted'; + $queryResult = static::query($sql)->fetch_assoc(); + $result = $queryResult['quantity']; + } else { + static::$querySelect['select'] = ['1']; + static::$querySelect['sql_calc_found_rows'] = true; + static::$querySelect['limit'] = '1'; + static::$querySelect['orderBy'] = ''; + + $sql = static::buildQuery($resetQuery); + static::query($sql); + $result = static::found_row(); + } + + if (!$resetQuery) { + static::$querySelect['select'] = $backup['select']; + static::$querySelect['sql_calc_found_rows'] = $backup['sql_calc_found_rows']; + static::$querySelect['limit'] = $backup['limit']; + static::$querySelect['orderBy'] = $backup['orderBy']; + } + + return $result; + } + + /* + * Retorna las filas contadas en el último query. + * + * @return int + */ + public static function found_row() { + $result = static::query('SELECT FOUND_ROWS() AS quantity')->fetch_assoc(); + return $result['quantity']; + } + + /* + * Habilita el conteo de todos las coincidencias posibles incluso usando limit. + * + * @return ModelMySQL + */ + public static function sql_calc_found_rows() { + static::$querySelect['sql_calc_found_rows'] = true; + return new static(); + } + + /* + * Obtiene una instancia según su primary key (generalmente id). + * + * @param mixed $id + * @return ModelMySQL + */ + public static function getById($id) { + return static::where(static::$primaryKey, $id)->getFirst(); + } + + /* + * Realiza una búsqueda en la tabla de la instancia actual. + * + * @param string $search + * Contenido a buscar. + * + * @param array $in + * Columnas en las que se va a buscar (null para buscar en todas) + */ + public static function search($search, $in = null) { + if ($in == null) { + $className = get_called_class(); + $in = array_keys((new $className())->getVars()); + } + + $db = static::db(); + + $search = $db->real_escape_string($search); + + $where = []; + + foreach($in as $row) { + $where[] = "$row LIKE '%$search%'"; + } + + if (static::$querySelect['where']=='') + static::$querySelect['where'] = join(' OR ', $where); + else + static::$querySelect['where'] = static::$querySelect['where'] .' AND ('.join(' OR ', $where).')'; + + return new static(); + } + + /* + * Obtener los resultados de la consulta SQL. + * + * @param boolean $resetQuery + * Indica si el query debe reiniciarse o no (por defecto es true). + * + * @return ModelMySQL[] + */ + public static function get($resetQuery = true) { // Devuelve array vacío si no encuentra nada + $sql = static::buildQuery($resetQuery); + $result = static::query($sql); + + $instances = []; + + while ($row = $result->fetch_assoc()) { + $instances[] = static::getInstance($row); + } + + return $instances; + } + + /* + * El primer elemento de la consulta SQL. + * + * @param boolean $resetQuery + * Indica si el query debe reiniciarse o no (por defecto es true). + * + * @return mixed + * Puede retornar un objeto ModelMySQL o null. + */ + public static function getFirst($resetQuery = true) { // Devuelve null si no encuentra nada + static::limit(1); + $instances = static::get($resetQuery); + return empty($instances) ? null : $instances[0]; + } + + /* + * Obtener todos los elementos del la tabla de la instancia actual. + * + * @return ModelMySQL[] + */ + public static function all() { + $sql = 'SELECT * FROM '.static::table(); + + $result = static::query($sql); + + $instances = []; + + while ($row = $result->fetch_assoc()) { + $instances[] = static::getInstance($row); + } + + return $instances; + } + + /* + * Permite definir como nulo el valor de un atributo. + * Sólo funciona para actualizar un elemento de la BD, no para insertar. + * + * @param array $atts + */ + public function setNull($atts) { + if (!isset($this->id)) + throw new \Exception( + "\nEl método setNull sólo funciona para actualizar, no al insertar." + ); + + foreach ($atts as $att) { + if (!in_array($att, $this->toNull)) + $this->toNull[] = $att; + } } - } } ?> diff --git a/src/Libs/Neuron.php b/src/Libs/Neuron.php index 9129bcb..46414d7 100644 --- a/src/Libs/Neuron.php +++ b/src/Libs/Neuron.php @@ -1,20 +1,20 @@ data = (array) $data; + $this->data = (array) $data; } public function __isset($index) { - return isset($this->data[$index]); + return isset($this->data[$index]); } public function __get($index){ return (isset($this->data[$index]) && $this->data[$index] != '') - ? $this->data[$index] : null; + ? $this->data[$index] : null; } } diff --git a/src/Libs/Router.php b/src/Libs/Router.php index 20ee6de..3c9e6dc 100644 --- a/src/Libs/Router.php +++ b/src/Libs/Router.php @@ -1,299 +1,299 @@ Error 404 - Página no encontrada'; - }; + private static $get = []; + private static $post = []; + private static $put = []; + private static $delete = []; + private static $last; + public static $notFoundCallBack = function () { + header("HTTP/1.0 404 Not Found"); + echo '

Error 404 - Página no encontrada

'; + }; - private function __construct() {} + private function __construct() {} - /* - * Parsea para deectar las pseudovariables (ej: {variable}) - * - * @param string $path - * URI con pseudovariables - * - * @param function $callback - * Función en formato Clase::Método que será llamada cuando la url y el método coincidan - * - * @return array - * Arreglo con 2 índices: - * path - Contiene la URI con la pseudovariables reeplazadas por expresiones regulares - * callback - Contiene el callback en formato Namespace\Clase::Método - */ - private static function parse($path, $callback) { - preg_match_all('/{([\w-]+)}/s', $path, $matches, PREG_PATTERN_ORDER); - $paramNames = $matches[1]; + /* + * Parsea para deectar las pseudovariables (ej: {variable}) + * + * @param string $path + * URI con pseudovariables + * + * @param function $callback + * Función en formato Clase::Método que será llamada cuando la url y el método coincidan + * + * @return array + * Arreglo con 2 índices: + * path - Contiene la URI con la pseudovariables reeplazadas por expresiones regulares + * callback - Contiene el callback en formato Namespace\Clase::Método + */ + private static function parse($path, $callback) { + preg_match_all('/{([\w-]+)}/s', $path, $matches, PREG_PATTERN_ORDER); + $paramNames = $matches[1]; - $path = preg_quote($path, '/'); - $path = preg_replace( + $path = preg_quote($path, '/'); + $path = preg_replace( ['/\\\{[\w-]+\\\}/s'], ['([^\/]+)'], $path); - if (!is_callable($callback)) { - $callback = 'Controllers\\'.$callback; - } - - return [ - 'path' => $path, - 'callback' => $callback, - 'paramNames' => $paramNames - ]; - } - - - /* - * Devuelve el path o URI base sobre la que trabajará el router. - * - * Ej: Si la url del sistema está en "https://ejemplo.com/duckbrain" - * entonces la URI base sería "/duckbrain" - */ - public static function baseURI() { - if (defined('SITE_URL')) - return parse_url(SITE_URL, PHP_URL_PATH); - return str_replace($_SERVER['DOCUMENT_ROOT'], '/', ROOT_DIR); - } - - /* - * Redirije a una ruta relativa interna. - * - * @param string $uri - * La URI relativa a la URI base. - * - * Ej: Si nuesto sistema está en "https://ejemplo.com/duckbrain" - * llamamos a Router::redirect('/docs'), entonces seremos - * redirigidos a "https://ejemplo.com/duckbrain/docs". - */ - public static function redirect($uri) { - header('Location: '.static::baseURI().substr($uri,1)); - } - - /* - * Añade un middleware a la última ruta usada. - * Solo se puede usar un middleware a la vez. - * - * @param string $callback - * - * @return static - * Devuelve un enlace estático - */ - public static function middleware($callback){ - if (!isset(static::$last)) - return; - - $method = static::$last[0]; - $index = static::$last[1]; - - if (!is_callable($callback)) { - $callback = 'Middlewares\\'.$callback; - } - - static::$$method[$index]['middleware'] = $callback; - - return new static(); - } - - /* - * @return object - * Devuelve un objeto que contiene los atributos: - * post - Donde se encuentran los valores enviados por $_POST - * get - Donde se encuentran los valores enviados por $_GET - * json - Donde se encuentran los valores JSON enviados en el body - * - */ - private static function getReq() { - $req = (object) ''; - $req->get = new Neuron($_GET); - $req->post = new Neuron($_POST); - $req->json = new Neuron(static::get_json()); - $req->params = new Neuron(); - $req->path = static::URIPath(); - return $req; - } - - /* - * @return object - * Devuelve un objeto con los datos recibidos en JSON - */ - private static function get_json() { - $contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : ''; - if ($contentType === "application/json") { - return json_decode(trim(file_get_contents("php://input"))); - } - return (object) ''; - } - - /* - * Define los routers para el método GET. - * - * @param string $path - * URI con pseudovariables - * - * @param function $callback - * Función en formato Clase::Método que será llamada cuando la url y el método coincidan - * - * @return static - * Devuelve un enlace estático - */ - public static function get($path, $callback) { - static::$get[] = static::parse($path, $callback); - static::$last = ['get', count(static::$get)-1]; - return new static(); - } - - /* - * Define los routers para el método POST. - * - * @param string $path - * URI con pseudovariables - * - * @param function $callback - * Función en formato Clase::Método que será llamada cuando la url y el método coincidan - * - * @return static - * Devuelve un enlace estático - */ - public static function post($path, $callback) { - static::$post[] = static::parse($path, $callback); - static::$last = ['post', count(static::$post)-1]; - return new static(); - } - - /* - * Define los routers para el método PUT. - * - * @param string $path - * URI con pseudovariables - * - * @param function $callback - * Función en formato Clase::Método que será llamada cuando la url y el método coincidan - * - * @return static - * Devuelve un enlace estático - */ - - public static function put($path, $callback) { - static::$put[] = static::parse($path, $callback); - static::$last = ['put', count(static::$put)-1]; - return new static(); - } - - /* - * Define los routers para el método DELETE. - * - * @param string $path - * URI con pseudovariables - * - * @param function $callback - * Función en formato Clase::Método que será llamada cuando la url y el método coincidan - * - * @return static - * Devuelve un enlace estático - */ - public static function delete($path, $callback) { - static::$delete[] = static::parse($path, $callback); - static::$last = ['delete', count(static::$delete)-1]; - return new static(); - } - - /* - * Devuelve el URI path actual - */ - public static function URIPath() { - return preg_replace('/'.preg_quote(static::baseURI(), '/').'/', - '/', strtok($_SERVER['REQUEST_URI'], '?'), 1); - } - - /* - * Aplica los routers. - * - * Este método ha de ser llamado luego de que todos los routers hayan sido configurados. - * - * En caso que la URI actual coincida con un router configurado, se comprueba si hay middleware; Si hay - * middleware, se enviará el callback y los datos de la petición como un Neuron. Caso contrario, se enviarán - * los datos directamente al callback. - * - * Con middleware: - * $middleware($callback, $req) - * - * Sin middleware: - * $callback($req) - * - * $req es una instancia de Neuron que tiene los datos de la petición. - * - * Si no la uri no coincide con ninguna de las - */ - public static function apply() { - $uri = static::URIPath(); - $routers = []; - switch ($_SERVER['REQUEST_METHOD']){ // Según el método selecciona un arreglo de routers configurados - case 'POST': - $routers = static::$post; - break; - case 'PUT': - $routers = static::$put; - break; - case 'DELETE': - $routers = static::$delete; - break; - default: - $routers = static::$get; - break; - } - - $args = static::getReq(); - - foreach ($routers as $router) { // revisa todos los routers para ver si coinciden con la URI actual - if (preg_match_all('/^'.$router['path'].'\/?$/si',$uri, $matches, PREG_PATTERN_ORDER)) { - unset($matches[0]); - - // Comprobando pseudo variables en URI - if (isset($matches[1])) { - foreach ($matches as $index => $match) { - $paramName = $router['paramNames'][$index-1]; - $args->params->$paramName = urldecode($match[0]); - } + if (!is_callable($callback)) { + $callback = 'Controllers\\'.$callback; } - // Comprobando si hay middleware - if (isset($router['middleware'])) { - //$middleware = explode('::',$router['middleware']); - $data = call_user_func_array($router['middleware'], [$router['callback'], $args]); - } else { - $data = call_user_func_array($router['callback'], [$args]); - } - - if (isset($data)) { - header('Content-Type: application/json'); - print(json_encode($data)); - } - - return; - } + return [ + 'path' => $path, + 'callback' => $callback, + 'paramNames' => $paramNames + ]; } - // Si no hay router que coincida llamamos a $notFoundCallBack - call_user_func_array(static::$notFoundCallBack, [$args]); - } + + /* + * Devuelve el path o URI base sobre la que trabajará el router. + * + * Ej: Si la url del sistema está en "https://ejemplo.com/duckbrain" + * entonces la URI base sería "/duckbrain" + */ + public static function baseURI() { + if (defined('SITE_URL')) + return parse_url(SITE_URL, PHP_URL_PATH); + return str_replace($_SERVER['DOCUMENT_ROOT'], '/', ROOT_DIR); + } + + /* + * Redirije a una ruta relativa interna. + * + * @param string $uri + * La URI relativa a la URI base. + * + * Ej: Si nuesto sistema está en "https://ejemplo.com/duckbrain" + * llamamos a Router::redirect('/docs'), entonces seremos + * redirigidos a "https://ejemplo.com/duckbrain/docs". + */ + public static function redirect($uri) { + header('Location: '.static::baseURI().substr($uri,1)); + } + + /* + * Añade un middleware a la última ruta usada. + * Solo se puede usar un middleware a la vez. + * + * @param string $callback + * + * @return static + * Devuelve un enlace estático + */ + public static function middleware($callback){ + if (!isset(static::$last)) + return; + + $method = static::$last[0]; + $index = static::$last[1]; + + if (!is_callable($callback)) { + $callback = 'Middlewares\\'.$callback; + } + + static::$$method[$index]['middleware'] = $callback; + + return new static(); + } + + /* + * @return object + * Devuelve un objeto que contiene los atributos: + * post - Donde se encuentran los valores enviados por $_POST + * get - Donde se encuentran los valores enviados por $_GET + * json - Donde se encuentran los valores JSON enviados en el body + * + */ + private static function getReq() { + $req = (object) ''; + $req->get = new Neuron($_GET); + $req->post = new Neuron($_POST); + $req->json = new Neuron(static::get_json()); + $req->params = new Neuron(); + $req->path = static::URIPath(); + return $req; + } + + /* + * @return object + * Devuelve un objeto con los datos recibidos en JSON + */ + private static function get_json() { + $contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : ''; + if ($contentType === "application/json") { + return json_decode(trim(file_get_contents("php://input"))); + } + return (object) ''; + } + + /* + * Define los routers para el método GET. + * + * @param string $path + * URI con pseudovariables + * + * @param function $callback + * Función en formato Clase::Método que será llamada cuando la url y el método coincidan + * + * @return static + * Devuelve un enlace estático + */ + public static function get($path, $callback) { + static::$get[] = static::parse($path, $callback); + static::$last = ['get', count(static::$get)-1]; + return new static(); + } + + /* + * Define los routers para el método POST. + * + * @param string $path + * URI con pseudovariables + * + * @param function $callback + * Función en formato Clase::Método que será llamada cuando la url y el método coincidan + * + * @return static + * Devuelve un enlace estático + */ + public static function post($path, $callback) { + static::$post[] = static::parse($path, $callback); + static::$last = ['post', count(static::$post)-1]; + return new static(); + } + + /* + * Define los routers para el método PUT. + * + * @param string $path + * URI con pseudovariables + * + * @param function $callback + * Función en formato Clase::Método que será llamada cuando la url y el método coincidan + * + * @return static + * Devuelve un enlace estático + */ + + public static function put($path, $callback) { + static::$put[] = static::parse($path, $callback); + static::$last = ['put', count(static::$put)-1]; + return new static(); + } + + /* + * Define los routers para el método DELETE. + * + * @param string $path + * URI con pseudovariables + * + * @param function $callback + * Función en formato Clase::Método que será llamada cuando la url y el método coincidan + * + * @return static + * Devuelve un enlace estático + */ + public static function delete($path, $callback) { + static::$delete[] = static::parse($path, $callback); + static::$last = ['delete', count(static::$delete)-1]; + return new static(); + } + + /* + * Devuelve el URI path actual + */ + public static function URIPath() { + return preg_replace('/'.preg_quote(static::baseURI(), '/').'/', + '/', strtok($_SERVER['REQUEST_URI'], '?'), 1); + } + + /* + * Aplica los routers. + * + * Este método ha de ser llamado luego de que todos los routers hayan sido configurados. + * + * En caso que la URI actual coincida con un router configurado, se comprueba si hay middleware; Si hay + * middleware, se enviará el callback y los datos de la petición como un Neuron. Caso contrario, se enviarán + * los datos directamente al callback. + * + * Con middleware: + * $middleware($callback, $req) + * + * Sin middleware: + * $callback($req) + * + * $req es una instancia de Neuron que tiene los datos de la petición. + * + * Si no la uri no coincide con ninguna de las + */ + public static function apply() { + $uri = static::URIPath(); + $routers = []; + switch ($_SERVER['REQUEST_METHOD']){ // Según el método selecciona un arreglo de routers configurados + case 'POST': + $routers = static::$post; + break; + case 'PUT': + $routers = static::$put; + break; + case 'DELETE': + $routers = static::$delete; + break; + default: + $routers = static::$get; + break; + } + + $args = static::getReq(); + + foreach ($routers as $router) { // revisa todos los routers para ver si coinciden con la URI actual + if (preg_match_all('/^'.$router['path'].'\/?$/si',$uri, $matches, PREG_PATTERN_ORDER)) { + unset($matches[0]); + + // Comprobando pseudo variables en URI + if (isset($matches[1])) { + foreach ($matches as $index => $match) { + $paramName = $router['paramNames'][$index-1]; + $args->params->$paramName = urldecode($match[0]); + } + } + + // Comprobando si hay middleware + if (isset($router['middleware'])) { + //$middleware = explode('::',$router['middleware']); + $data = call_user_func_array($router['middleware'], [$router['callback'], $args]); + } else { + $data = call_user_func_array($router['callback'], [$args]); + } + + if (isset($data)) { + header('Content-Type: application/json'); + print(json_encode($data)); + } + + return; + } + } + + // Si no hay router que coincida llamamos a $notFoundCallBack + call_user_func_array(static::$notFoundCallBack, [$args]); + } } ?> diff --git a/src/Libs/View.php b/src/Libs/View.php index ff4674a..8d4935b 100644 --- a/src/Libs/View.php +++ b/src/Libs/View.php @@ -1,38 +1,38 @@ index) - * - * @param string $viewPath - * Ruta donde se encuentra la vista. En caso de que la vista no se encuentre en esa ruta, se usará la ruta por defecto "src/Views/". - */ - public static function render($viewName, $params = [], $viewPath = null) { - $view = new Neuron($params); - unset($params); - - if (isset($viewPath) && file_exists($viewPath.$viewName.'.php')) - return include($viewPath.$viewName.'.php'); - - include(ROOT_DIR.'/src/Views/'.$viewName.'.php'); - } + /* + * Función que "renderizar" las vistas + * + * @param string $viewName + * Ruta relativa y el nommbre sin extensión del archivo ubicado en src/Views + * + * @param array $params + * Arreglo que podrá ser usado en la vista mediante $view ($param['index'] se usaría así: $view->index) + * + * @param string $viewPath + * Ruta donde se encuentra la vista. En caso de que la vista no se encuentre en esa ruta, se usará la ruta por defecto "src/Views/". + */ + public static function render($viewName, $params = [], $viewPath = null) { + $view = new Neuron($params); + unset($params); + + if (isset($viewPath) && file_exists($viewPath.$viewName.'.php')) + return include($viewPath.$viewName.'.php'); + + include(ROOT_DIR.'/src/Views/'.$viewName.'.php'); + } } ?>