diff --git a/src/Libs/Database.php b/src/Libs/Database.php index f55f1bb..9ca9234 100644 --- a/src/Libs/Database.php +++ b/src/Libs/Database.php @@ -1,4 +1,16 @@ connect_errno) { - exit('No se ha podido conectar a la base de datos.'); + echo ''; + throw new \Exception('No se ha podido conectar a la base de datos.'); } } return self::$db; diff --git a/src/Libs/ModelMySQL.php b/src/Libs/ModelMySQL.php index 6c5551e..f5bd40d 100644 --- a/src/Libs/ModelMySQL.php +++ b/src/Libs/ModelMySQL.php @@ -1,4 +1,14 @@ '', 'limit' => '', ]; - + + /* + * Sirve para obtener la instancia de la base de datos + * + * @return mysqli + */ private static function db(){ if (is_null(self::$db)) self::$db = Database::getConnection(); @@ -34,20 +49,38 @@ class ModelMySQL { return self::$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. + */ private static function query($query){ $db = self::db(); $result = $db->query($query); if ($db->errno){ - echo 'Fallo al consultar la base de datos.
- Errno: ' . addslashes ($db->errno).'
- Error: ' . addslashes ($db->error).'
'.$query; - exit(); + echo ''; + throw new \Exception( + "\nFallo al consultar la base de datos\n" . + "Errno: $db->errno\n" . + "Error: $db->error\n" . + "Query: $query\n" + ); } return $result; } + /* + * Reinicia la configuración de la sentencia SQL. + */ private static function resetQuery(){ self::$querySelect = [ 'select' => '*', @@ -62,6 +95,13 @@ class ModelMySQL { ]; } + /* + * Construye la sentencia SQL a partir self::$querySelect y una vez + * construída, llama a resetQuery. + * + * @return string + * Contiene la sentencia SQL. + */ private static function buildQuery(){ $sql = 'SELECT '.self::$querySelect['select'].' FROM '.self::table(); @@ -98,7 +138,16 @@ class ModelMySQL { 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. + */ public function getInstance($elem = []){ $class = get_called_class(); $instace = new $class; @@ -110,7 +159,10 @@ class ModelMySQL { return $instace; } - + /* + * @return array + * Contiene los atributos indexados del objeto actual. + */ private function getVars(){ // Source: https://stackoverflow.com/questions/10009015/show-all-public-attributes-name-and-value-of-an-object $get_vars_proxy = create_function('$obj', 'return get_object_vars($obj);'); $result = $get_vars_proxy($this); @@ -125,19 +177,30 @@ class ModelMySQL { 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 self::$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 + */ private static function table(){ if (isset(self::$table)) return self::$table; return self::className().self::$tableSufix; } - + /* + * Actualiza los valores en la BD con los valores del objeto actual + */ private function update(){ $atts = $this->getVars(); @@ -153,7 +216,10 @@ class ModelMySQL { self::query($sql); } - + /* + * Inserta una nueva fila en la base de datos a partir del + * objeto actual. + */ private function add(){ $db = self::db(); $atts = $this->getVars(); @@ -171,7 +237,10 @@ class ModelMySQL { $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 = $this->primaryKey; if (isset($this->$pk)) @@ -180,7 +249,9 @@ class ModelMySQL { $this->add(); } - + /* + * Elimina el objeto actual de la base de datos. + */ public function delete(){ $atts = $this->getVars(); @@ -196,12 +267,17 @@ class ModelMySQL { self::query($sql); } - - public static function select($value){ + /* + * Define SELECT en la sentencia SQL. + * + * @param array $columns + * Columnas que se selecionarán en la consulta SQL. + */ + public static function select($columns){ $db = self::db(); $select = []; - foreach($value as $elem){ - $select[] = $db->real_escape_string($elem); + foreach($columns as $column){ + $select[] = $db->real_escape_string($column); } self::$querySelect['select'] = join(', ', $select); @@ -210,11 +286,21 @@ class ModelMySQL { } /* - * Sintaxis permitidas: - * - Class::where(columna, operador, valor) - * - Class::where(columna, valor) // Operador por defecto "=" + * 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. + * + * Sintaxis posibles: + * - self::where(columna, operador, valor) + * - self::where(columna, valor) // Operador por defecto "=" */ - public static function where($column, $operator, $value=null){ if (is_null($value)){ $value = $operator; @@ -228,7 +314,18 @@ class ModelMySQL { 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] = self::db()->real_escape_string($value); @@ -242,6 +339,25 @@ class ModelMySQL { 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: + * - self::leftJoin(tabla,columnaA, operador, columnB) + * - self::leftJoin(tabla,columnaA, columnB) // Operador por defecto "=" + */ public static function leftJoin($table, $columnA, $operator, $columnB = null){ if (is_null($columnB)){ $columnB = $operator; @@ -258,6 +374,25 @@ class ModelMySQL { 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: + * - self::rightJoin(tabla,columnaA, operador, columnB) + * - self::rightJoin(tabla,columnaA, columnB) // Operador por defecto "=" + */ public static function rightJoin($table, $columnA, $operator, $columnB = null){ if (is_null($columnB)){ $columnB = $operator; @@ -274,6 +409,23 @@ class ModelMySQL { 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. + * + * Sintaxis posibles: + * - self::and(columna, operador, valor) + * - self::and(columna, valor) // Operador por defecto "=" + * - self::and(columna, valor)->and(columna, valor)->and(columna, valor) // anidado + */ public static function and($column, $operator, $value=null){ if (is_null($value)){ $value = $operator; @@ -287,7 +439,23 @@ class ModelMySQL { 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. + * + * Sintaxis posibles: + * - self::or(columna, operador, valor) + * - self::or(columna, valor) // Operador por defecto "=" + * - self::or(columna, valor)->or(columna, valor)->or(columna, valor) // anidado + */ public static function or($column, $operator, $value=null){ if (is_null($value)){ $value = $operator; @@ -301,6 +469,12 @@ class ModelMySQL { return new static(); } + /* + * Define GROUP BY en la sentencia SQL + * + * @param array $arr + * Columnas por las que se agrupará. + */ public static function groupBy($arr){ self::$querySelect['groupBy'] = join(', ', $arr); return new static(); @@ -318,7 +492,16 @@ class ModelMySQL { 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"){ self::$querySelect['orderBy'] = 'RAND()'; @@ -335,7 +518,11 @@ class ModelMySQL { return new static(); } - + /* + * Retorna la cantidad de filas que hay en un query. + * + * @return int + */ public static function count(){ self::$querySelect['select'] = 'count(*) as quantity'; $sql = self::buildQuery(); @@ -343,18 +530,29 @@ class ModelMySQL { return $result['quantity']; } - + /* + * Obtiene una instancia según su id. + * + * @param int $id + * @return ModelMySQL + */ public static function getById($id){ return self::where('id', $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){ - $className = get_called_class(); - $objAtts = array_keys((new $className())->getVars()); - if ($in == null){ - $in = $objAtts; + $className = get_called_class(); + $objAtts = array_keys((new $className())->getVars()); } $db = self::db(); @@ -376,7 +574,11 @@ class ModelMySQL { return new static(); } - + /* + * Obtener los resultados de la consulta SQL. + * + * @return ModelMySQL[] + */ public static function get(){ // Devuelve array vacío si no encuentra nada $sql = self::buildQuery(); $result = self::query($sql); @@ -390,14 +592,23 @@ class ModelMySQL { return $instaces; } - + /* + * El primer elemento de la consulta SQL. + * + * @return mixed + * Puede retornar un objeto ModelMySQL o null. + */ public static function getFirst(){ // Devuelve null si no encuentra nada self::limit(1); $instaces = self::get(); return empty($instaces) ? null : $instaces[0]; } - + /* + * Obtener todos los elementos del la tabla de la instancia actual. + * + * @return ModelMySQL[] + */ public static function all(){ $sql = 'SELECT * FROM '.self::table(); diff --git a/src/Libs/Params.php b/src/Libs/Params.php index 3864afe..927b9f2 100644 --- a/src/Libs/Params.php +++ b/src/Libs/Params.php @@ -1,4 +1,20 @@ data[$index]) && $this->data[$index] != '') ? $this->data[$index] : null; + return (isset($this->data[$index]) && $this->data[$index] != '') + ? $this->data[$index] : null; } } diff --git a/src/Libs/Router.php b/src/Libs/Router.php index f23b5b5..1193fc9 100644 --- a/src/Libs/Router.php +++ b/src/Libs/Router.php @@ -1,19 +1,44 @@ get = new Params($_GET); $args->post = new Params($_POST); @@ -60,37 +110,120 @@ class Router{ return $args; } - private static function get_json(){ + /* + * @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"){ + if ($contentType === "application/json") { return json_decode(trim(file_get_contents("php://input"))); } return (object) ''; } - public static function post($path, $callback){ + /* + * 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) { + self::$get[] = self::parse($path, $callback); + self::$last = ['get', count(self::$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) { self::$post[] = self::parse($path, $callback); self::$last = ['post', count(self::$post)-1]; return new static(); } - public static function put($path, $callback){ + public static function put($path, $callback) { self::$put[] = self::parse($path, $callback); self::$last = ['put', count(self::$put)-1]; return new static(); } - public static function delete($path, $callback){ + /* + * 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) { self::$delete[] = self::parse($path, $callback); self::$last = ['delete', count(self::$put)-1]; return new static(); } - public static function apply(){ + /* + * 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, + * pueden suecedor los siguientes casos: + * + * 1. Tiene pseudovariables por URI + * + * 1.1. Tiene middleware, por lo que se llama al middleware enviándole los datos + * en el siguiente orden: + * - Función callback del router + * - Objeto que contiene $_POST, $_GET y el JSON recibido + * - Todas las pseudovariables (en el mismo orden que están escritas) + * + * 1.1. No tiene middleware, por lo que se llama a la función callback del router + * enviándole los datos en el siguiente orden: + * - Todas las pseudovariables (en el mismo orden que están escritas) + * - Objeto que contiene $_POST, $_GET y el JSON recibido + * + * 2. No tiene pseudovariables por URI + * + * 2.1. Tiene middleware, por lo que se llama al middleware enviándole los datos + * En el siguiente orden: + * - Función callback del router + * - Todas las pseudovariables (en el mismo orden que están escritas) + * + * 2.2. No tiene middleware, por lo que se llama a la función callback del router + * enviándole solo ún parámetro: + * - Objeto que contiene $_POST, $_GET y el JSON recibido + * + * Nota: Gracias a que se usa call_user_func_array, los callbacks no es necesario que reciban + * la misma cantidad de parámetros que se les envía. De ese modo, pueden recibir solo + * los parámetros que van a utilizar, con el detalle de que siempre se respeta el orden + * especificado anteriormente a la hora de recibirlos. + */ + public static function apply() { $uri = preg_replace('/'.preg_quote(self::baseURI(), '/').'/', '/', strtok($_SERVER['REQUEST_URI'], '?'), 1); $routers = []; - switch ($_SERVER['REQUEST_METHOD']){ + switch ($_SERVER['REQUEST_METHOD']){ // Según el método selecciona un arreglo de routers configurados case 'POST': $routers = self::$post; break; @@ -105,35 +238,35 @@ class Router{ break; } - foreach ($routers as $router){ + 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)){ unset($matches[0]); $args = self::params(); - if (isset($matches[1])){ + if (isset($matches[1])) { // Caso 1 - Con pseudovariables por URI $params = []; - foreach ($matches as $match){ + foreach ($matches as $match) { if (!empty($match)) $params[] = "$match[0]"; } - if (isset($router['middleware'])){ + if (isset($router['middleware'])) { // Caso 1.1 - Con middleware $middleware = explode('::',$router['middleware']); $data = call_user_func_array($middleware, [$router['callback'], $args, $params]); - } else { + } else { // Caso 1.2 - Sin middleware $params[] = $args; $data = call_user_func_array($router['callback'], $params); } - }else{ - if (isset($router['middleware'])){ + } else { // Caso 2 - Sin Pseudo variables por URI + if (isset($router['middleware'])) { // Caso 2.1 - Con middleware $middleware = explode('::',$router['middleware']); $data = call_user_func_array($middleware, [$router['callback'], $args]); - } else { + } else { // Caso 2.2 - Sin middleware $data = call_user_func_array($router['callback'], [$args]); } } - if (isset($data)){ + if (isset($data)) { header('Content-Type: application/json'); print(json_encode($data)); } @@ -141,7 +274,7 @@ class Router{ return; } } - header("HTTP/1.0 404 Not Found"); + header("HTTP/1.0 404 Not Found"); // Si no hay router que coincida, se devuelve error 404 echo '

Error 404 - Página no encontrada

'; } } diff --git a/src/Libs/View.php b/src/Libs/View.php index edd3a03..b034d52 100644 --- a/src/Libs/View.php +++ b/src/Libs/View.php @@ -1,13 +1,32 @@ index) + * + */ + public static function render($viewName, $params = []) { $view = new Params($params); unset($params); - include(ROOT_DIR.'src/Views/'.$viewName.'.php'); + include(ROOT_DIR.'/src/Views/'.$viewName.'.php'); } } ?>