Compare commits
101 Commits
b8b1a1c8f9
...
master
Author | SHA1 | Date | |
---|---|---|---|
e9126e7cde | |||
7169d2cae3 | |||
66b2bc0d91 | |||
c8ab2aa2cc | |||
1e302a9ea7 | |||
d0d0d4dc76 | |||
595e9c1316 | |||
45abea5301 | |||
d441f001ec | |||
19da122e05 | |||
1a0164c8ed | |||
ad9f8ec67d | |||
31c5c63952 | |||
6aef212350 | |||
c600688725 | |||
3e27b1b7af | |||
73b7b8f72a | |||
7baad428ec | |||
3d2a607768 | |||
df424ffab5 | |||
daf7250882 | |||
05cd83fd10 | |||
6b470a181d | |||
7beb161d2b | |||
701caae7eb | |||
100bdfe006 | |||
f1b79fdbc0 | |||
406f9a10a1 | |||
cc3cb6be41 | |||
59fff2a586 | |||
cd1685d2e7 | |||
b85fb7e034 | |||
a10308a8f6 | |||
9a1e5a2379 | |||
fa60ec5bb4 | |||
b294b1d627 | |||
27b5ed6015 | |||
f9b2c678f7 | |||
e294eaa7af | |||
186d640c8f | |||
c65fcd27c8 | |||
f5f803dde2 | |||
6a1085b224 | |||
64bcfa0b00 | |||
cd01ab9e72 | |||
e2094ccb4a | |||
341d7837a1 | |||
030e1079dc | |||
af673a68b8 | |||
eff0b86762 | |||
39a1f9d85a | |||
d48f24ed98 | |||
b326c8e1d0 | |||
1267d6b2f6 | |||
a8d95cb55b | |||
06b7907f7c | |||
4d052efba6 | |||
11141a0eee | |||
3c8a21161f | |||
1bde430251 | |||
7d3d1615d5 | |||
404bd59569 | |||
53bdc92344 | |||
ad7b78f427 | |||
38d2a90318 | |||
2411704662 | |||
08d92a2b81 | |||
8d47e10d7a | |||
2c7938a0c3 | |||
c57ca191b1 | |||
f4eabe6d54 | |||
83d9ba55aa | |||
bad7bf0e65 | |||
5284be2b1b | |||
0db6e4a021 | |||
afb4c914a1 | |||
4c4fe6f1f7 | |||
b6555ee039 | |||
a641248453 | |||
eb27acf68e | |||
f38c6610fb | |||
4003a88f66 | |||
01927ec946 | |||
a23e795f5c | |||
9689ac82cf | |||
fe61d14713 | |||
97380b2bee | |||
4cd7850931 | |||
4240b048fd | |||
f43028d72d | |||
503efef448 | |||
81f2d4d665 | |||
4b1585fe6d | |||
83c9d28526 | |||
a303f28f88 | |||
94f1857653 | |||
6fbf9a2a72 | |||
790930771f | |||
184d03a214 | |||
e1fe93a04c | |||
68bc906ecf |
14
config.php
14
config.php
@ -1,10 +1,14 @@
|
||||
<?php
|
||||
define('dbhost', 'localhost');
|
||||
define('dbname', '');
|
||||
define('dbuser', '');
|
||||
define('dbpass', '');
|
||||
// Configuración de la base de datos
|
||||
define('DB_TYPE', 'mysql');
|
||||
define('DB_HOST', 'localhost');
|
||||
define('DB_NAME', '');
|
||||
define('DB_USER', '');
|
||||
define('DB_PASS', '');
|
||||
|
||||
// Configuración del sitio
|
||||
define('SITE_URL', '');
|
||||
|
||||
// Configuración avanzada
|
||||
define('ROOT_DIR', __DIR__);
|
||||
?>
|
||||
define('ROOT_CORE', ROOT_DIR.'/src');
|
||||
|
16
index.php
16
index.php
@ -6,19 +6,15 @@ spl_autoload_register(function ($className) {
|
||||
$fp = str_replace('\\','/',$className);
|
||||
$name = basename($fp);
|
||||
$dir = dirname($fp);
|
||||
$file = ROOT_DIR.'/src/'.$dir.'/'.$name.'.php';
|
||||
if (file_exists($file)) {
|
||||
require_once $file;
|
||||
return;
|
||||
}
|
||||
$file = ROOT_CORE.'/'.$dir.'/'.$name.'.php';
|
||||
if (file_exists($file))
|
||||
require_once $file;
|
||||
});
|
||||
|
||||
// Incluir routers
|
||||
$routers = glob(ROOT_DIR.'/src/Routers/*.php');
|
||||
$routers = glob(ROOT_CORE.'/Routers/*.php');
|
||||
|
||||
foreach($routers as $file){
|
||||
require_once($file);
|
||||
}
|
||||
foreach($routers as $file)
|
||||
require_once($file);
|
||||
|
||||
\Libs\Router::apply();
|
||||
?>
|
||||
|
21
readme.md
21
readme.md
@ -1,21 +0,0 @@
|
||||
# DuckBrain - PHP Microframework
|
||||
|
||||
Este microframework PHP tiene el objetivo de presentar un framework sencillo y potente que sea válido especialmente para proyectos pequeños o simples, sin limitar demasiado, ni depender de cosas que agranden innecesariamente proyectos.
|
||||
|
||||
Esto no es un intento de mi parte de reinventar la rueda, de hecho los primeros commits los hice con la intención de que solo fuera una prueba de concepto, pero me gustó como quedó y decidí darle continuidad hasta hacerlo usable para mi mismo.
|
||||
|
||||
Tuve la idea de hacer este conjunto de librerías/microframework luego de ver como proyectos relativamente pequeños eran hechos con frameworks demasiado pesados para lo que eran.
|
||||
|
||||
También revisé otros microframeworks y si bien me parecen una buena opción para hacer los trabajos, aún me quedaba la espinita de que seguían siendo muy grandes para que un programador novato pudiera leerlo, entenderlo y modificarlo, así que intenté hacer algo más simple aún y de ahí salió este proyecto.
|
||||
|
||||
Lo ideal sería mantener el código sencillo, lo suficiente como para que cualquier novato que sepa POO y PHP pueda leerlo rápido, entenderlo y modificarlo a gusto. Por este motivo también he decidido desligarlo en lo posible de composer o cualquier cosa similar, ya que no pocos programadores en etapa de aprendizaje al encontrarse con frameworks más complicados o con herramientas como composer, terminan trabajando con cosas que no comprenden ni pueden arreglar por si mismos en caso de fallo, ya que son completamente dependientes de dichas herramientas.
|
||||
|
||||
El código no es perfecto, pero lo iré perfeccionando, ya que es algo que estoy usando actualmente para mis trabajos, de modo que puedo ir viendo que cosas se pueden ir mejorando.
|
||||
|
||||
## Uso / Documentación
|
||||
|
||||
Queda pendiente, si quieres usarlo ya mismo, puedes leer los comeentarios que he colocado en el código.
|
||||
|
||||
## Contacto
|
||||
|
||||
Puedes encontrame en telegram como [@keyjay](https://telegram.me/keyjay) o contactarme mi correo: webmaster@outcontrol.net
|
107
readme.org
Normal file
107
readme.org
Normal file
@ -0,0 +1,107 @@
|
||||
#+TITLE: Duckbrain
|
||||
#+AUTHOR: KJ
|
||||
#+OPTIONS: toc:nil
|
||||
|
||||
Este conjunto de librerías (microframework, si así lo prefieres) tiene el objetivo de presentar una herramienta de trabajo sencilla y potente que sea válida especialmente para proyectos pequeños o simples, sin limitar, ni depender de cosas que agranden innecesariamente dichos proyectos.
|
||||
|
||||
Tuve la idea de hacer esta herramienta luego de ver como proyectos relativamente pequeños eran hechos con frameworks demasiado grandes para la envergadura de dichos proyectos. No es bueno matar moscas a cañonazos.
|
||||
|
||||
Del mismo modo revisé algunos microframeworks y si bien me parecen una buena opción, aún me quedaba la espinita de que seguían siendo muy complejos para que un programador novato pudiera leer su código, entenderlo y modificarlo, así que intenté hacer algo más simple
|
||||
|
||||
Lo que busco es mantener un código sencillo, lo suficiente como para que cualquier novato que sepa POO y PHP pueda leer su código rápidamente, entenderlo y modificarlo a gusto. Por este motivo también he decidido desligarlo en lo posible de composer o cualquier cosa similar, ya que no pocos programadores en etapa de aprendizaje al encontrarse con frameworks más complicados o con herramientas como composer, terminan trabajando con cosas que no comprenden ni pueden arreglar por si mismos en caso de fallo, llevándolos a la dependencia total de dichas herramientas.
|
||||
|
||||
* Uso / Documentación
|
||||
|
||||
Actualmente existe un manual de inicio que puedes leerlo desde [[https://tools.kj2.me/duckbrain/starting-manual.html][aquí]] o desde [[https://git.kj2.me/kj/duckbrain-docs/src/branch/master/starting-manual.org][aquí]].
|
||||
|
||||
Si quieres leer el manual de inicio en su formato original (necesitas un editor/lector que soporte org-mode) puedes descargarla desde [[https://tools.kj2.me/duckbrain/starting-manual.org][aquí]].
|
||||
|
||||
Para el resto de la documentación, el código usa [[https://www.phpdoc.org/][phpdoc]], por lo que puedes tener toda la documentación API desde tu IDE o Editor de texto preferido siempre que soporte dicha característica. O si lo prefieres, puedes generarla en unbonito htm local usando la herramienta [[https://docs.phpdoc.org/3.0/packages/phpDocumentor.html][phpDocumentor]].
|
||||
|
||||
* Soporte de gestores de bases de datos
|
||||
|
||||
En la siguiente tabla se encuentra la lista de estados de los gestores de bases de datos que he probado o que planeo probar para asegurarme de que sean realmente compatibles.
|
||||
|
||||
*Entiéndase*:
|
||||
|
||||
+ *ok* como que ha sido probado y funciona.
|
||||
+ *En blanco* como que no ha sido probado aún.
|
||||
+ *error* como que fue probado, no funciona y no ha sido aún arreglado.
|
||||
+ *not supported* como no soportado por el gestor de bases de datos.
|
||||
+ *fixed* para aquello que no existe en el gestor de DB, pero la librería lo traduce a un equivalente.
|
||||
|
||||
|------------------+---------------+---------------+------------|
|
||||
| method | MySQL/MariaDB | sqlite3 | postgreSQL |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| db | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| query | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| resetQuery | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| buildQuery | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| getInstance | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| getVars | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| className | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| table | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| update | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| beginTransaction | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| rollBack | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| commit | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| add | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| save | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| delete | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| select | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| from | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| where | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| where_in | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| leftJoin | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| rightJoin | ok | not supported | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| innerJoin | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| and | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| or | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| groupBy | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| limit | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| orderBy | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| count | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| getById | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| search | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| get | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| getFirst | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| all | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
| setNull | ok | ok | |
|
||||
|------------------+---------------+---------------+------------|
|
||||
|
||||
* Contacto
|
||||
|
||||
Puedes encontrame en telegram como [[https://telegram.me/keyjay][@keyjay]] o contactarme mi correo: webmaster@outcontrol.net
|
0
src/Controllers/.keep
Normal file
0
src/Controllers/.keep
Normal file
@ -1,32 +1,58 @@
|
||||
<?php
|
||||
/*
|
||||
* DuckBrain - Microframework
|
||||
*
|
||||
* Clase diseñada para crear y devolver una única instancia mysqli.
|
||||
* Depende de manera forzada de que estén definidas las constantes:
|
||||
* dbhost, dbname, dbpass y dbuser
|
||||
*
|
||||
* Autor: KJ
|
||||
* Web: https://kj2.me
|
||||
* Licencia: MIT
|
||||
*/
|
||||
/**
|
||||
* Database - DuckBrain
|
||||
*
|
||||
* Clase diseñada para crear y devolver una única instancia PDO (database).
|
||||
*
|
||||
* @author KJ
|
||||
* @website https://kj2.me
|
||||
* @licence MIT
|
||||
*/
|
||||
|
||||
namespace Libs;
|
||||
|
||||
class Database {
|
||||
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 '<style>body{white-space: pre-line;}</style>';
|
||||
throw new \Exception('No se ha podido conectar a la base de datos.');
|
||||
}
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Exception;
|
||||
|
||||
class Database extends PDO {
|
||||
static private array $databases = [];
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
/**
|
||||
* Devuelve una instancia homogénea (singlenton) de la base de datos (PDO).
|
||||
*
|
||||
* @return PDO
|
||||
*/
|
||||
static public function getInstance(
|
||||
string $type = 'mysql',
|
||||
string $host = 'localhost',
|
||||
string $name = '',
|
||||
string $user = '',
|
||||
string $pass = '',
|
||||
): PDO
|
||||
{
|
||||
$key = $type.'/'.$host.'/'.$name.'/'.$user;
|
||||
if (empty(static::$databases[$key])) {
|
||||
|
||||
if ($type == 'sqlite') {
|
||||
$dsn = $type .':'. $name;
|
||||
} else
|
||||
$dsn = $type.':dbname='.$name.';host='.$host;
|
||||
|
||||
try {
|
||||
static::$databases[$key] = new PDO($dsn, $user, $pass);
|
||||
} catch (PDOException $e) {
|
||||
throw new Exception(
|
||||
'Error at connect to database: ' . $e->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
static::$databases[$key]->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
static::$databases[$key]->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||
}
|
||||
return static::$databases[$key];
|
||||
}
|
||||
return self::$db;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
28
src/Libs/Middleware.php
Normal file
28
src/Libs/Middleware.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Middleware - DuckBrain
|
||||
*
|
||||
* Librería base para middlewares.
|
||||
*
|
||||
* @author KJ
|
||||
* @website https://kj2.me
|
||||
* @licence MIT
|
||||
*/
|
||||
|
||||
namespace Libs;
|
||||
|
||||
class Middleware {
|
||||
|
||||
/**
|
||||
* Llama al siguiente callback.
|
||||
*
|
||||
* @param Neuron $req
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function next(Neuron $req): mixed
|
||||
{
|
||||
$next = array_pop($req->next);
|
||||
return call_user_func_array($next, [$req]);
|
||||
}
|
||||
}
|
937
src/Libs/Model.php
Normal file
937
src/Libs/Model.php
Normal file
@ -0,0 +1,937 @@
|
||||
<?php
|
||||
/**
|
||||
* Model - DuckBrain
|
||||
*
|
||||
* Modelo ORM para objetos que hagan uso de una base de datos.
|
||||
* Depende de Libs\Database y hace uso de las constantes
|
||||
* DB_TYPE, DB_HOST, DB_NAME, DB_USER y DB_PASS.
|
||||
*
|
||||
* @author KJ
|
||||
* @website https://kj2.me
|
||||
* @licence MIT
|
||||
*/
|
||||
|
||||
namespace Libs;
|
||||
|
||||
use Libs\Database;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Exception;
|
||||
use ReflectionClass;
|
||||
use ReflectionProperty;
|
||||
use AllowDynamicProperties;
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class Model {
|
||||
|
||||
public ?int $id = null;
|
||||
protected array $toNull = [];
|
||||
static protected string $primaryKey = 'id';
|
||||
static protected array $ignoreSave = ['id'];
|
||||
static protected array $forceSave = [];
|
||||
static protected string $table;
|
||||
static protected string $tableSufix = 's';
|
||||
static protected array $queryVars = [];
|
||||
static protected array $querySelect = [
|
||||
'select' => ['*'],
|
||||
'where' => '',
|
||||
'from' => '',
|
||||
'leftJoin' => '',
|
||||
'rightJoin' => '',
|
||||
'innerJoin' => '',
|
||||
'orderBy' => '',
|
||||
'groupBy' => '',
|
||||
'limit' => ''
|
||||
];
|
||||
|
||||
/**
|
||||
* Sirve para obtener la instancia de la base de datos.
|
||||
*
|
||||
* @return PDO
|
||||
*/
|
||||
protected static function db(): PDO
|
||||
{
|
||||
if (DB_TYPE == 'sqlite')
|
||||
return Database::getInstance(
|
||||
type: DB_TYPE,
|
||||
name: DB_NAME
|
||||
);
|
||||
else
|
||||
return Database::getInstance(
|
||||
DB_TYPE,
|
||||
DB_HOST,
|
||||
DB_NAME,
|
||||
DB_USER,
|
||||
DB_PASS
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ejecuta PDO::beginTransaction para iniciar una transacción.
|
||||
* Más info: https://www.php.net/manual/es/pdo.begintransaction.php
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function beginTransaction(): bool
|
||||
{
|
||||
return static::db()->beginTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ejecuta PDO::rollBack para deshacher los cambios de una transacción.
|
||||
* Más info: https://www.php.net/manual/es/pdo.rollback.php
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function rollBack(): bool
|
||||
{
|
||||
if ( static::db()->inTransaction())
|
||||
return static::db()->rollBack();
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ejecuta PDO::commit para consignar una transacción.
|
||||
* Más info: https://www.php.net/manual/es/pdo.commit.php
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function commit(): bool
|
||||
{
|
||||
if (static::db()->inTransaction())
|
||||
return static::db()->commit();
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 y hará rolllback en caso de estar dentro de una
|
||||
* transacción (ver método beginTransacction).
|
||||
*
|
||||
* @param bool $resetQuery
|
||||
* Indica si el query debe reiniciarse o no (por defecto es true).
|
||||
*
|
||||
* @return array
|
||||
* Contiene el resultado de la llamada SQL .
|
||||
*/
|
||||
protected static function query(string $query, bool $resetQuery = true): array
|
||||
{
|
||||
$db = static::db();
|
||||
|
||||
try {
|
||||
$prepared = $db->prepare($query);
|
||||
$prepared->execute(static::$queryVars);
|
||||
} catch (PDOException $e) {
|
||||
if ($db->inTransaction())
|
||||
$db->rollBack();
|
||||
|
||||
$vars = json_encode(static::$queryVars);
|
||||
|
||||
throw new Exception(
|
||||
"\nError at query to database.\n" .
|
||||
"Query: $query\n" .
|
||||
"Vars: $vars\n" .
|
||||
"Error:\n" . $e->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
$result = $prepared->fetchAll();
|
||||
|
||||
if ($resetQuery)
|
||||
static::resetQuery();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reinicia la configuración de la sentencia SQL.
|
||||
* @return void
|
||||
*/
|
||||
protected static function resetQuery(): void
|
||||
{
|
||||
static::$querySelect = [
|
||||
'select' => ['*'],
|
||||
'where' => '',
|
||||
'from' => '',
|
||||
'leftJoin' => '',
|
||||
'rightJoin' => '',
|
||||
'innerJoin' => '',
|
||||
'orderBy' => '',
|
||||
'groupBy' => '',
|
||||
'limit' => ''
|
||||
];
|
||||
static::$queryVars = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Construye la sentencia SQL a partir static::$querySelect y una vez
|
||||
* construída, llama a resetQuery.
|
||||
*
|
||||
* @return string
|
||||
* Contiene la sentencia SQL.
|
||||
*/
|
||||
protected static function buildQuery(): string
|
||||
{
|
||||
$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['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'];
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configura $queryVars para vincular un valor a un
|
||||
* parámetro de sustitución y devuelve este último.
|
||||
*
|
||||
* @param string $value
|
||||
* Valor a vincular.
|
||||
*
|
||||
* @return string
|
||||
* Parámetro de sustitución.
|
||||
*/
|
||||
private static function bindValue(string $value): string
|
||||
{
|
||||
$index = ':v_'.count(static::$queryVars);
|
||||
static::$queryVars[$index] = $value;
|
||||
return $index;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 static
|
||||
* Retorna un objeto de la clase actual.
|
||||
*/
|
||||
protected static function getInstance(array $elem = []): static
|
||||
{
|
||||
$class = get_called_class();
|
||||
$instance = new $class;
|
||||
$reflection = new ReflectionClass($instance);
|
||||
$properties = $reflection->getProperties();
|
||||
$propertyNames = array_column($properties, 'name');
|
||||
|
||||
foreach ($elem as $key => $value) {
|
||||
$index = array_search($key, $propertyNames);
|
||||
if (is_numeric($index) && enum_exists($properties[$index]->getType()->getName()))
|
||||
$instance->$key = $properties[$index]->getType()->getName()::tryfrom($value);
|
||||
else
|
||||
$instance->$key = $value;
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve los atributos a guardar de la case actual.
|
||||
* Los atributos serán aquellos que seran public y
|
||||
* no esten excluidos en static::$ignoresave y aquellos
|
||||
* que sean private o protected pero estén en static::$forceSave.
|
||||
*
|
||||
* @return array
|
||||
* Contiene los atributos indexados del objeto actual.
|
||||
*/
|
||||
protected function getVars(): array
|
||||
{
|
||||
$reflection = new ReflectionClass($this);
|
||||
$properties = $reflection->getProperties(ReflectionProperty::IS_PUBLIC);
|
||||
$result = [];
|
||||
|
||||
foreach($properties as $property)
|
||||
$result[$property->name] = isset($this->{$property->name})
|
||||
? $this->{$property->name} : null;
|
||||
|
||||
foreach (static::$ignoreSave as $del)
|
||||
unset($result[$del]);
|
||||
|
||||
foreach (static::$forceSave as $value)
|
||||
$result[$value] = isset($this->$value)
|
||||
? $this->$value: null;
|
||||
|
||||
foreach ($result as $i => $property) {
|
||||
if (gettype($property) == 'boolean')
|
||||
$result[$i] = $property ? '1' : '0';
|
||||
|
||||
if ($property instanceof \UnitEnum)
|
||||
$result[$i] = $property->value;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve el nombre de la clase actual aunque sea una clase extendida.
|
||||
*
|
||||
* @return string
|
||||
* Devuelve el nombre de la clase actual.
|
||||
*/
|
||||
public static function className(): string
|
||||
{
|
||||
return 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(): string
|
||||
{
|
||||
if (isset(static::$table))
|
||||
return static::$table;
|
||||
|
||||
return strtolower(
|
||||
preg_replace(
|
||||
'/(?<!^)[A-Z]/', '_$0',
|
||||
static::className()
|
||||
)
|
||||
).static::$tableSufix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actualiza los valores en la BD con los valores del objeto actual.
|
||||
* @return void
|
||||
*/
|
||||
protected function update(): void
|
||||
{
|
||||
$atts = $this->getVars();
|
||||
|
||||
foreach ($atts as $key => $value) {
|
||||
if (isset($value)) {
|
||||
if (in_array($key, $this->toNull))
|
||||
$set[]="$key=NULL";
|
||||
else {
|
||||
$set[]="$key=:$key";
|
||||
static::$queryVars[':'.$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.
|
||||
* @return void
|
||||
*/
|
||||
protected function add(): void
|
||||
{
|
||||
$db = static::db();
|
||||
$atts = $this->getVars();
|
||||
|
||||
foreach ($atts as $key => $value) {
|
||||
if (isset($value)) {
|
||||
$into[] = "`$key`";
|
||||
$values[] = ":$key";
|
||||
static::$queryVars[":$key"] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$table = static::table();
|
||||
$sql = "INSERT INTO $table (".join(', ', $into).") VALUES (".join(', ', $values).")";
|
||||
static::query($sql);
|
||||
|
||||
$pk = static::$primaryKey;
|
||||
$this->$pk = $db->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return void
|
||||
*/
|
||||
public function save(): void
|
||||
{
|
||||
$pk = static::$primaryKey;
|
||||
if (isset($this->$pk))
|
||||
$this->update();
|
||||
else
|
||||
$this->add();
|
||||
}
|
||||
|
||||
/**
|
||||
* Elimina el objeto actual de la base de datos.
|
||||
* @return void
|
||||
*/
|
||||
public function delete(): void {
|
||||
$table = static::table();
|
||||
$pk = static::$primaryKey;
|
||||
$sql = "DELETE FROM $table WHERE $pk=:$pk";
|
||||
|
||||
static::$queryVars[":$pk"] = $this->$pk;
|
||||
static::query($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define SELECT en la sentencia SQL.
|
||||
*
|
||||
* @param array $columns
|
||||
* Columnas que se selecionarán en la consulta SQL.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function select(array $columns): static
|
||||
{
|
||||
static::$querySelect['select'] = $columns;
|
||||
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define FROM en la sentencia SQL.
|
||||
*
|
||||
* @param array $tables
|
||||
* Tablas que se selecionarán en la consulta SQL.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function from(array $tables): static
|
||||
{
|
||||
static::$querySelect['from'] = join(', ', $tables);
|
||||
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define el WHERE en la sentencia SQL.
|
||||
*
|
||||
* @param string $column
|
||||
* La columna a comparar.
|
||||
*
|
||||
* @param string $operatorOrValue
|
||||
* El operador o el valor a comparar como igual en caso de que $value no se defina.
|
||||
*
|
||||
* @param string|null $value
|
||||
* (opcional) El valor a comparar en la columna.
|
||||
*
|
||||
* @param bool $no_filter
|
||||
* (opcional) Se usa cuando $value es una columna o un valor que no requiere filtros
|
||||
* contra ataques SQLI (por defeco es false).
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function where(
|
||||
string $column,
|
||||
string $operatorOrValue,
|
||||
?string $value = null,
|
||||
bool $no_filter = false
|
||||
): static
|
||||
{
|
||||
return static::and(
|
||||
$column,
|
||||
$operatorOrValue,
|
||||
$value,
|
||||
$no_filter
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define AND en la sentencia SQL (se puede anidar).
|
||||
*
|
||||
* @param string $column
|
||||
* La columna a comparar.
|
||||
*
|
||||
* @param string $operatorOrValue
|
||||
* El operador o el valor a comparar como igual en caso de que $value no se defina.
|
||||
*
|
||||
* @param string|null $value
|
||||
* (opcional) El valor el valor a comparar en la columna.
|
||||
*
|
||||
* @param bool $no_filter
|
||||
* (opcional) Se usa cuando $value es una columna o un valor que no requiere filtros
|
||||
* contra ataques SQLI (por defecto es false).
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function and(
|
||||
string $column,
|
||||
string $operatorOrValue,
|
||||
?string $value = null,
|
||||
bool $no_filter = false
|
||||
): static
|
||||
{
|
||||
if (is_null($value)) {
|
||||
$value = $operatorOrValue;
|
||||
$operatorOrValue = '=';
|
||||
}
|
||||
|
||||
if (!$no_filter)
|
||||
$value = static::bindValue($value);
|
||||
|
||||
if (static::$querySelect['where'] == '')
|
||||
static::$querySelect['where'] = "$column $operatorOrValue $value";
|
||||
else
|
||||
static::$querySelect['where'] .= " AND $column $operatorOrValue $value";
|
||||
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define OR en la sentencia SQL (se puede anidar).
|
||||
*
|
||||
* @param string $column
|
||||
* La columna a comparar.
|
||||
*
|
||||
* @param string $operatorOrValue
|
||||
* El operador o el valor a comparar como igual en caso de que $value no se defina.
|
||||
*
|
||||
* @param string|null $value
|
||||
* (opcional) El valor el valor a comparar en la columna.
|
||||
*
|
||||
* @param bool $no_filter
|
||||
* (opcional) Se usa cuando $value es una columna o un valor que no requiere filtros
|
||||
* contra ataques SQLI (por defecto es false).
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function or(
|
||||
string $column,
|
||||
string $operatorOrValue,
|
||||
?string $value = null,
|
||||
bool $no_filter = false
|
||||
): static
|
||||
{
|
||||
if (is_null($value)) {
|
||||
$value = $operatorOrValue;
|
||||
$operatorOrValue = '=';
|
||||
}
|
||||
|
||||
if (!$no_filter)
|
||||
$value = static::bindValue($value);
|
||||
|
||||
if (static::$querySelect['where'] == '')
|
||||
static::$querySelect['where'] = "$column $operatorOrValue $value";
|
||||
else
|
||||
static::$querySelect['where'] .= " OR $column $operatorOrValue $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 bool $in
|
||||
* Define si se tienen que comprobar negativa o positivamente.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function where_in(
|
||||
string $column,
|
||||
array $arr,
|
||||
bool $in = true
|
||||
): static
|
||||
{
|
||||
$arrIn = [];
|
||||
foreach($arr as $value) {
|
||||
$arrIn[] = static::bindValue($value);
|
||||
}
|
||||
|
||||
if ($in)
|
||||
$where_in = "$column IN (".join(', ', $arrIn).")";
|
||||
else
|
||||
$where_in = "$column NOT IN (".join(', ', $arrIn).")";
|
||||
|
||||
if (static::$querySelect['where'] == '')
|
||||
static::$querySelect['where'] = $where_in;
|
||||
else
|
||||
static::$querySelect['where'] .= " AND $where_in";
|
||||
|
||||
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 $operatorOrColumnB
|
||||
* Operador o columna a comparar como igual para hacer el join en caso de que $columnB no se defina.
|
||||
*
|
||||
* @param string|null $columnB
|
||||
* (opcional) Columna a comparar para hacer el join.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function leftJoin(
|
||||
string $table,
|
||||
string $columnA,
|
||||
string $operatorOrColumnB,
|
||||
?string $columnB = null
|
||||
): static
|
||||
{
|
||||
if (is_null($columnB)) {
|
||||
$columnB = $operatorOrColumnB;
|
||||
$operatorOrColumnB = '=';
|
||||
}
|
||||
|
||||
static::$querySelect['leftJoin'] .= ' LEFT JOIN ' . $table . ' ON ' . "$columnA$operatorOrColumnB$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 $operatorOrColumnB
|
||||
* Operador o columna a comparar como igual para hacer el join en caso de que $columnB no se defina.
|
||||
*
|
||||
* @param string|null $columnB
|
||||
* (opcional) Columna a comparar para hacer el join.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function rightJoin(
|
||||
string $table,
|
||||
string $columnA,
|
||||
string $operatorOrColumnB,
|
||||
?string $columnB = null
|
||||
): static
|
||||
{
|
||||
if (is_null($columnB)) {
|
||||
$columnB = $operatorOrColumnB;
|
||||
$operatorOrColumnB = '=';
|
||||
}
|
||||
|
||||
static::$querySelect['rightJoin'] .= ' RIGHT JOIN ' . $table . ' ON ' . "$columnA$operatorOrColumnB$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 $operatorOrColumnB
|
||||
* Operador o columna a comparar como igual para hacer el join en caso de que $columnB no se defina.
|
||||
*
|
||||
* @param string|null $columnB
|
||||
* (opcional) Columna a comparar para hacer el join.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function innerJoin(
|
||||
string $table,
|
||||
string $columnA,
|
||||
string $operatorOrColumnB,
|
||||
?string $columnB = null
|
||||
): static
|
||||
{
|
||||
if (is_null($columnB)) {
|
||||
$columnB = $operatorOrColumnB;
|
||||
$operatorOrColumnB = '=';
|
||||
}
|
||||
|
||||
static::$querySelect['innerJoin'] .= ' INNER JOIN ' . $table . ' ON ' . "$columnA$operatorOrColumnB$columnB";
|
||||
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define GROUP BY en la sentencia SQL.
|
||||
*
|
||||
* @param array $arr
|
||||
* Columnas por las que se agrupará.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function groupBy(array $arr): static
|
||||
{
|
||||
static::$querySelect['groupBy'] = join(', ', $arr);
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define LIMIT en la sentencia SQL.
|
||||
*
|
||||
* @param int $offsetOrQuantity
|
||||
* Define el las filas a ignorar o la cantidad a tomar en
|
||||
* caso de que $quantity no esté definido.
|
||||
* @param int $quantity
|
||||
* Define la cantidad máxima de filas a tomar.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function limit(int $offsetOrQuantity, ?int $quantity = null): static
|
||||
{
|
||||
if (is_null($quantity))
|
||||
static::$querySelect['limit'] = $offsetOrQuantity;
|
||||
else
|
||||
static::$querySelect['limit'] = $offsetOrQuantity.', '.$quantity;
|
||||
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define ORDER BY en la sentencia SQL.
|
||||
*
|
||||
* @param string $value
|
||||
* Columna por la que se ordenará.
|
||||
*
|
||||
* @param string $order
|
||||
* (opcional) Define si el orden será de manera ascendente (ASC),
|
||||
* descendente (DESC) o aleatorio (RAND).
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function orderBy(string $value, string $order = 'ASC'): static
|
||||
{
|
||||
if ($value == "RAND") {
|
||||
static::$querySelect['orderBy'] = 'RAND()';
|
||||
return new static();
|
||||
}
|
||||
|
||||
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 bool $resetQuery
|
||||
* (opcional) Indica si el query debe reiniciarse o no (por defecto es true).
|
||||
*
|
||||
* @param bool $useLimit
|
||||
* (opcional) Permite usar limit para estabecer un máximo inical y final para contar.
|
||||
* Requiere que se haya definido antes el límite (por defecto en false).
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function count(bool $resetQuery = true, bool $useLimit = false): int
|
||||
{
|
||||
if (!$resetQuery)
|
||||
$backup = [
|
||||
'select' => static::$querySelect['select'],
|
||||
'limit' => static::$querySelect['limit'],
|
||||
'orderBy' => static::$querySelect['orderBy']
|
||||
];
|
||||
|
||||
if ($useLimit && static::$querySelect['limit'] != '') {
|
||||
static::$querySelect['select'] = ['1'];
|
||||
static::$querySelect['orderBy'] = '';
|
||||
|
||||
$sql = 'SELECT COUNT(1) AS quantity FROM ('.static::buildQuery().') AS counted';
|
||||
$queryResult = static::query($sql, $resetQuery);
|
||||
$result = $queryResult[0]['quantity'];
|
||||
} else {
|
||||
static::$querySelect['select'] = ["COUNT(".static::table().".".static::$primaryKey.") as quantity"];
|
||||
static::$querySelect['limit'] = '1';
|
||||
static::$querySelect['orderBy'] = '';
|
||||
|
||||
$sql = static::buildQuery();
|
||||
$queryResult = static::query($sql, $resetQuery);
|
||||
$result = $queryResult[0]['quantity'];
|
||||
}
|
||||
|
||||
if (!$resetQuery) {
|
||||
static::$querySelect['select'] = $backup['select'];
|
||||
static::$querySelect['limit'] = $backup['limit'];
|
||||
static::$querySelect['orderBy'] = $backup['orderBy'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene una instancia según su primary key (generalmente id).
|
||||
* Si no encuentra una instancia, devuelve nulo.
|
||||
*
|
||||
* @param mixed $id
|
||||
*
|
||||
* @return static|null
|
||||
*/
|
||||
public static function getById(mixed $id): ?static
|
||||
{
|
||||
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|null $in
|
||||
* (opcional) Columnas en las que se va a buscar (null para buscar en todas).
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function search(string $search, ?array $in = null): static
|
||||
{
|
||||
if ($in == null) {
|
||||
$className = get_called_class();
|
||||
$in = array_keys((new $className())->getVars());
|
||||
}
|
||||
|
||||
$search = static::bindValue($search);
|
||||
$where = [];
|
||||
|
||||
if (DB_TYPE == 'sqlite')
|
||||
foreach($in as $row)
|
||||
$where[] = "$row LIKE '%' || $search || '%'";
|
||||
else
|
||||
foreach($in as $row)
|
||||
$where[] = "$row LIKE CONCAT('%', $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 bool $resetQuery
|
||||
* (opcional) Indica si el query debe reiniciarse o no (por defecto es true).
|
||||
*
|
||||
* @return array<static>
|
||||
* Arreglo con instancias del la clase actual resultantes del query.
|
||||
*/
|
||||
public static function get(bool $resetQuery = true): array
|
||||
{
|
||||
$sql = static::buildQuery();
|
||||
$result = static::query($sql, $resetQuery);
|
||||
|
||||
$instances = [];
|
||||
|
||||
foreach ($result as $row) {
|
||||
$instances[] = static::getInstance($row);
|
||||
}
|
||||
|
||||
return $instances;
|
||||
}
|
||||
|
||||
/**
|
||||
* El primer elemento de la consulta SQL.
|
||||
*
|
||||
* @param bool $resetQuery
|
||||
* (opcional) Indica si el query debe reiniciarse o no (por defecto es true).
|
||||
*
|
||||
* @return static|null
|
||||
* Puede retornar una instancia de la clase actual o null.
|
||||
*/
|
||||
public static function getFirst(bool $resetQuery = true): ?static
|
||||
{
|
||||
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 array<static>
|
||||
* Contiene un arreglo de instancias de la clase actual.
|
||||
*/
|
||||
public static function all(): array
|
||||
{
|
||||
$sql = 'SELECT * FROM '.static::table();
|
||||
$result = static::query($sql);
|
||||
|
||||
$instances = [];
|
||||
|
||||
foreach ($result as $row)
|
||||
$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 string|array $atts
|
||||
* Atributo o arreglo de atributos que se definirán como nulos.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setNull(string|array $atts): void
|
||||
{
|
||||
if (is_array($atts)) {
|
||||
foreach ($atts as $att)
|
||||
if (!in_array($att, $this->toNull))
|
||||
$this->toNull[] = $att;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!in_array($atts, $this->toNull))
|
||||
$this->toNull[] = $atts;
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,810 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* DuckBrain - Microframework
|
||||
*
|
||||
* Modelo ORM para objetos que hagan uso de una base de datos MySQL.
|
||||
* Depende de que exista Libs\Database para poder funcionar.
|
||||
*
|
||||
* Autor: KJ
|
||||
* Web: https://kj2.me
|
||||
* Licencia: MIT
|
||||
*/
|
||||
|
||||
namespace Libs;
|
||||
|
||||
use Libs\Database;
|
||||
|
||||
class ModelMySQL {
|
||||
|
||||
public $id;
|
||||
protected $toNull = [];
|
||||
|
||||
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();
|
||||
|
||||
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 '<style>body{white-space: pre-line;}</style>';
|
||||
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.
|
||||
*/
|
||||
protected static function resetQuery() {
|
||||
static::$querySelect = [
|
||||
'select' => ['*'],
|
||||
'where' => '',
|
||||
'from' => '',
|
||||
'leftJoin' => '',
|
||||
'rightJoin' => '',
|
||||
'innerJoin' => '',
|
||||
'AndOr' => '',
|
||||
'orderBy' => '',
|
||||
'groupBy' => '',
|
||||
'limit' => '',
|
||||
'sql_calc_found_rows' => false
|
||||
];
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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']
|
||||
];
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,38 +1,53 @@
|
||||
<?php
|
||||
/*
|
||||
* DuckBrain - Microframework
|
||||
*
|
||||
* Neuron, sirve para crear un objeto que alojará valores, pero
|
||||
* además tiene la característica especial de que al intentar
|
||||
* acceder a un atributo que no está definido devolerá nulo en
|
||||
* lugar de generar un error php notice que indica que se está
|
||||
* intentando acceder a un valor no definido.
|
||||
*
|
||||
* El constructor recibe un objeto o arreglo con los valores que
|
||||
* sí estarán definidos.
|
||||
*
|
||||
* Autor: KJ
|
||||
* Web: https://kj2.me
|
||||
* Licencia: MIT
|
||||
*/
|
||||
/**
|
||||
* Neuron - DuckBrain
|
||||
*
|
||||
* Neuron, sirve para crear un objeto que alojará valores, pero
|
||||
* además tiene la característica especial de que al intentar
|
||||
* acceder a un atributo que no está definido devolerá nulo en
|
||||
* lugar de generar un error php notice que indica que se está
|
||||
* intentando acceder a un valor no definido.
|
||||
*
|
||||
* El constructor recibe un objeto o arreglo con los valores que
|
||||
* sí estarán definidos.
|
||||
*
|
||||
* @author KJ
|
||||
* @website https://kj2.me
|
||||
* @licence MIT
|
||||
*/
|
||||
|
||||
namespace Libs;
|
||||
use AllowDynamicProperties;
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class Neuron {
|
||||
|
||||
private $data;
|
||||
|
||||
public function __construct($data){
|
||||
$this->data = (array) $data;
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct(...$data)
|
||||
{
|
||||
if (count($data) === 1 &&
|
||||
isset($data[0]) &&
|
||||
(is_array($data[0]) ||
|
||||
is_object($data[0])))
|
||||
$data = $data[0];
|
||||
|
||||
foreach($data as $key => $value)
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
|
||||
public function __isset($index) {
|
||||
return isset($this->data[$index]);
|
||||
}
|
||||
|
||||
public function __get($index){
|
||||
return (isset($this->data[$index]) && $this->data[$index] != '')
|
||||
? $this->data[$index] : null;
|
||||
|
||||
/**
|
||||
* __get
|
||||
*
|
||||
* @param string $index
|
||||
* @return null
|
||||
*/
|
||||
public function __get(string $index): null
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
149
src/Libs/Request.php
Normal file
149
src/Libs/Request.php
Normal file
@ -0,0 +1,149 @@
|
||||
<?php
|
||||
/**
|
||||
* Request - DuckBrain
|
||||
*
|
||||
* Libería complementaria de la libería Router.
|
||||
* Contiene el cuerpo básico de la petición http (POST, GET, JSON, etc).
|
||||
*
|
||||
* @author KJ
|
||||
* @website https://kj2.me
|
||||
* @licence MIT
|
||||
*/
|
||||
|
||||
namespace Libs;
|
||||
|
||||
class Request extends Neuron {
|
||||
public Neuron $get;
|
||||
public Neuron $post;
|
||||
public Neuron $put;
|
||||
public Neuron $patch;
|
||||
public Neuron $delete;
|
||||
public Neuron $json;
|
||||
public Neuron $params;
|
||||
public string $path;
|
||||
public string $error;
|
||||
public string $body;
|
||||
public array $next;
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param string $path Ruta actual tomando como raíz la instalación de DuckBrain.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->path = Router::currentPath();
|
||||
$this->get = new Neuron($_GET);
|
||||
$this->post = new Neuron($_POST);
|
||||
$this->put = new Neuron();
|
||||
$this->patch = new Neuron();
|
||||
$this->delete = new Neuron();
|
||||
$this->body = file_get_contents("php://input");
|
||||
|
||||
$contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : '';
|
||||
if ($contentType === "application/json")
|
||||
$this->json = new Neuron(
|
||||
(object) json_decode(trim($this->body), false)
|
||||
);
|
||||
else {
|
||||
$this->json = new Neuron();
|
||||
if (in_array($_SERVER['REQUEST_METHOD'], ['PUT', 'PATCH', 'DELETE']) &&
|
||||
preg_match('/^[^;?\/:@&=+$,]{1,255}[=]/', $this->body, $matches)) { // Con la expresión regular verificamos que sea un http query string válido y evitamos errores de memoria en caso de que el body tenga algo más grande que eso.
|
||||
parse_str($this->body, $input_vars);
|
||||
$this->{strtolower($_SERVER['REQUEST_METHOD'])} = new Neuron($input_vars);
|
||||
}
|
||||
}
|
||||
|
||||
$this->params = new Neuron();
|
||||
}
|
||||
|
||||
/**
|
||||
* Corre las validaciones e intenta continuar con la pila de callbacks.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(): mixed
|
||||
{
|
||||
if ($this->validate())
|
||||
return Middleware::next($this);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inicia la validación que se haya configurado.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validate(): bool
|
||||
{
|
||||
$actual = match($_SERVER['REQUEST_METHOD']) {
|
||||
'POST', 'PUT', 'PATCH', 'DELETE' => $this->{strtolower($_SERVER['REQUEST_METHOD'])},
|
||||
default => $this->get
|
||||
};
|
||||
|
||||
if (Validator::validateList(static::paramRules(), $this->params) &&
|
||||
Validator::validateList(static::getRules(), $this->get ) &&
|
||||
Validator::validateList(static::rules(), $actual))
|
||||
return true;
|
||||
|
||||
if (isset(static::messages()[Validator::$lastFailed]))
|
||||
$error = static::messages()[Validator::$lastFailed];
|
||||
else {
|
||||
|
||||
$error = 'Error: validation failed of '.preg_replace('/\./', ' as ', Validator::$lastFailed, 1);
|
||||
}
|
||||
|
||||
static::onInvalid($error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reglas para el método actual.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reglas para los parámetros por URL.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function paramRules(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reglas para los parámetros GET.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRules(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Mensajes de error en caso de fallar una validación.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function messages(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Función a ejecutar cuando se ha detectado un valor no válido.
|
||||
*
|
||||
* @param string $error
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onInvalid(string $error): void
|
||||
{
|
||||
http_response_code(422);
|
||||
print($error);
|
||||
}
|
||||
}
|
@ -1,306 +1,374 @@
|
||||
<?php
|
||||
/*
|
||||
* DuckBrain - Microframework
|
||||
*
|
||||
* Librería de Enrrutador.
|
||||
* Depende de manera forzada de que la constante ROOT_DIR esté definida
|
||||
* y de manera optativa de que la constante SITE_URL lo esté también.
|
||||
*
|
||||
* Autor: KJ
|
||||
* Web: https://kj2.me
|
||||
* Licencia: MIT
|
||||
*/
|
||||
/**
|
||||
* Router - DuckBrain
|
||||
*
|
||||
* Librería de Enrrutador.
|
||||
* Depende de manera forzada de que la constante ROOT_DIR esté definida
|
||||
* y de manera optativa de que la constante SITE_URL lo esté también.
|
||||
*
|
||||
* @author KJ
|
||||
* @website https://kj2.me
|
||||
* @licence MIT
|
||||
*/
|
||||
|
||||
namespace Libs;
|
||||
|
||||
class Router {
|
||||
private static $get = [];
|
||||
private static $post = [];
|
||||
private static $put = [];
|
||||
private static $delete = [];
|
||||
private static $last;
|
||||
public static $notFoundCallBack;
|
||||
private static $get = [];
|
||||
private static $post = [];
|
||||
private static $put = [];
|
||||
private static $patch = [];
|
||||
private static $delete = [];
|
||||
private static $last;
|
||||
public static $notFoundCallback = 'Libs\Router::defaultNotFound';
|
||||
|
||||
private function __construct() {}
|
||||
/**
|
||||
* Función callback por defectio para cuando
|
||||
* no se encuentra configurada la ruta.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function defaultNotFound (): void
|
||||
{
|
||||
header("HTTP/1.0 404 Not Found");
|
||||
echo '<h2 style="text-align: center;margin: 25px 0px;">Error 404 - Página no encontrada</h2>';
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
$path = preg_quote($path, '/');
|
||||
$path = preg_replace(
|
||||
['/\\\{[\w-]+\\\}/s'],
|
||||
/**
|
||||
* __construct
|
||||
*/
|
||||
private function __construct() {}
|
||||
|
||||
/**
|
||||
* Parsea para deectar las pseudovariables (ej: {variable})
|
||||
*
|
||||
* @param string $path
|
||||
* Ruta con pseudovariables.
|
||||
*
|
||||
* @param callable $callback
|
||||
* Callback que será llamado cuando la ruta configurada en $path coincida.
|
||||
*
|
||||
* @return array
|
||||
* Arreglo con 2 índices:
|
||||
* path - Contiene la ruta con las pseudovariables reeplazadas por expresiones regulares.
|
||||
* callback - Contiene el callback en formato Namespace\Clase::Método.
|
||||
*/
|
||||
private static function parse(string $path, callable $callback): array
|
||||
{
|
||||
preg_match_all('/{(\w+)}/s', $path, $matches, PREG_PATTERN_ORDER);
|
||||
$paramNames = $matches[1];
|
||||
|
||||
$path = preg_quote($path, '/');
|
||||
$path = preg_replace(
|
||||
['/\\\{\w+\\\}/s'],
|
||||
['([^\/]+)'],
|
||||
$path);
|
||||
|
||||
if (!is_callable($callback)) {
|
||||
$callback = 'Controllers\\'.$callback;
|
||||
}
|
||||
return [
|
||||
'path'=> $path,
|
||||
'callback' => $callback
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
public static function middleware($middleware){
|
||||
if (!isset(static::$last)) return;
|
||||
|
||||
$method = static::$last[0];
|
||||
$index = static::$last[1];
|
||||
|
||||
static::$$method[$index]['middleware'] = 'Middlewares\\'.$middleware;
|
||||
}
|
||||
|
||||
/*
|
||||
* @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->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,
|
||||
* 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 = 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;
|
||||
return [
|
||||
'path' => $path,
|
||||
'callback' => [$callback],
|
||||
'paramNames' => $paramNames
|
||||
];
|
||||
}
|
||||
|
||||
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 = static::getReq();
|
||||
|
||||
if (isset($matches[1])) { // Caso 1 - Con pseudovariables por URI
|
||||
$params = [];
|
||||
foreach ($matches as $match) {
|
||||
if (!empty($match))
|
||||
$params[] = "$match[0]";
|
||||
}
|
||||
/**
|
||||
* Devuelve el ruta base o raiz del proyecto sobre la que trabajará el router.
|
||||
*
|
||||
* Ej: Si la url del sistema está en "https://ejemplo.com/duckbrain"
|
||||
* entonces la ruta base sería "/duckbrain"
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function basePath(): string
|
||||
{
|
||||
if (defined('SITE_URL') && !empty(SITE_URL))
|
||||
return rtrim(parse_url(SITE_URL, PHP_URL_PATH), '/').'/';
|
||||
return str_replace($_SERVER['DOCUMENT_ROOT'], '/', ROOT_DIR);
|
||||
}
|
||||
|
||||
if (isset($router['middleware'])) { // Caso 1.1 - Con middleware
|
||||
$middleware = explode('::',$router['middleware']);
|
||||
$data = call_user_func_array($middleware, [$router['callback'], $args, $params]);
|
||||
} else { // Caso 1.2 - Sin middleware
|
||||
$params[] = $args;
|
||||
$data = call_user_func_array($router['callback'], $params);
|
||||
}
|
||||
} 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 { // Caso 2.2 - Sin middleware
|
||||
$data = call_user_func_array($router['callback'], [$args]);
|
||||
}
|
||||
/**
|
||||
* Redirije a una ruta relativa interna.
|
||||
*
|
||||
* @param string $path
|
||||
* La ruta relativa a la ruta 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".
|
||||
* @return void
|
||||
*/
|
||||
public static function redirect(string $path): void
|
||||
{
|
||||
header('Location: '.static::basePath().ltrim($path, '/'));
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Añade un middleware a la última ruta usada.
|
||||
* Solo se puede usar un middleware a la vez.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param int $prioriry
|
||||
*
|
||||
* @return static
|
||||
* Devuelve la instancia actual.
|
||||
*/
|
||||
public static function middleware(callable $callback, ?int $priority = null): static
|
||||
{
|
||||
if (!isset(static::$last))
|
||||
return new static();
|
||||
|
||||
$method = static::$last[0];
|
||||
$index = static::$last[1];
|
||||
|
||||
if (isset($priority) && $priority <= 0)
|
||||
$priority = 1;
|
||||
|
||||
if (is_null($priority) || $priority >= count(static::$$method[$index]['callback']))
|
||||
static::$$method[$index]['callback'][] = $callback;
|
||||
else {
|
||||
static::$$method[$index]['callback'] = array_merge(
|
||||
array_slice(static::$$method[$index]['callback'], 0, $priority),
|
||||
[$callback],
|
||||
array_slice(static::$$method[$index]['callback'], $priority)
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($data)) {
|
||||
header('Content-Type: application/json');
|
||||
print(json_encode($data));
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconfigura el callback final de la última ruta.
|
||||
*
|
||||
* @param callable $callback
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function reconfigure(callable $callback): static
|
||||
{
|
||||
if (empty(static::$last))
|
||||
return new static();
|
||||
|
||||
$method = static::$last[0];
|
||||
$index = static::$last[1];
|
||||
|
||||
static::$$method[$index]['callback'][0] = $callback;
|
||||
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configura calquier método para todas las rutas.
|
||||
*
|
||||
* En caso de no recibir un callback, busca la ruta actual
|
||||
* solo configura la ruta como la última configurada
|
||||
* siempre y cuando la misma haya sido configurada previamente.
|
||||
*
|
||||
* @param string $method
|
||||
* Método http.
|
||||
* @param string $path
|
||||
* Ruta con pseudovariables.
|
||||
* @param callable|null $callback
|
||||
*
|
||||
* @return
|
||||
* Devuelve la instancia actual.
|
||||
*/
|
||||
public static function configure(string $method, string $path, ?callable $callback = null): static
|
||||
{
|
||||
if (is_null($callback)) {
|
||||
$path = preg_quote($path, '/');
|
||||
$path = preg_replace(
|
||||
['/\\\{\w+\\\}/s'],
|
||||
['([^\/]+)'],
|
||||
$path);
|
||||
|
||||
foreach(static::$$method as $index => $router)
|
||||
if ($router['path'] == $path) {
|
||||
static::$last = [$method, $index];
|
||||
break;
|
||||
}
|
||||
|
||||
return new static();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
static::$$method[] = static::parse($path, $callback);
|
||||
static::$last = [$method, count(static::$$method)-1];
|
||||
return new static();
|
||||
}
|
||||
|
||||
if (isset(static::$notFoundCallBack))
|
||||
call_user_func_array(static::$notFoundCallBack, []);
|
||||
else {
|
||||
header("HTTP/1.0 404 Not Found"); // Si no hay router que coincida, se devuelve error 404
|
||||
echo '<h2 style="text-align: center;margin: 25px 0px;">Error 404 - Página no encontrada</h2>';
|
||||
/**
|
||||
* Define los routers para el método GET.
|
||||
*
|
||||
* @param string $path
|
||||
* Ruta con pseudovariables.
|
||||
* @param callable|null $callback
|
||||
* Callback que será llamado cuando la ruta configurada en $path coincida.
|
||||
*
|
||||
* @return static
|
||||
* Devuelve la instancia actual.
|
||||
*/
|
||||
public static function get(string $path, ?callable $callback = null): static
|
||||
{
|
||||
return static::configure('get', $path, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define los routers para el método POST.
|
||||
*
|
||||
* @param string $path
|
||||
* Ruta con pseudovariables.
|
||||
* @param callable|null $callback
|
||||
* Callback que será llamado cuando la ruta configurada en $path coincida.
|
||||
*
|
||||
* @return static
|
||||
* Devuelve la instancia actual.
|
||||
*/
|
||||
public static function post(string $path, ?callable $callback = null): static
|
||||
{
|
||||
return static::configure('post', $path, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define los routers para el método PUT.
|
||||
*
|
||||
* @param string $path
|
||||
* Ruta con pseudovariables.
|
||||
* @param callable|null $callback
|
||||
* Callback que será llamado cuando la ruta configurada en $path coincida.
|
||||
*
|
||||
* @return static
|
||||
* Devuelve la instancia actual
|
||||
*/
|
||||
|
||||
public static function put(string $path, ?callable $callback = null): static
|
||||
{
|
||||
return static::configure('put', $path, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define los routers para el método PATCH.
|
||||
*
|
||||
* @param string $path
|
||||
* Ruta con pseudovariables.
|
||||
* @param callable|null $callback
|
||||
* Callback que será llamado cuando la ruta configurada en $path coincida.
|
||||
*
|
||||
* @return static
|
||||
* Devuelve la instancia actual
|
||||
*/
|
||||
public static function patch(string $path, ?callable $callback = null): static
|
||||
{
|
||||
return static::configure('patch', $path, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define los routers para el método DELETE.
|
||||
*
|
||||
* @param string $path
|
||||
* Ruta con pseudovariables
|
||||
* @param callable|null $callback
|
||||
* Callback que será llamado cuando la ruta configurada en $path coincida.
|
||||
*
|
||||
* @return static
|
||||
* Devuelve la instancia actual
|
||||
*/
|
||||
public static function delete(string $path, ?callable $callback = null): static
|
||||
{
|
||||
return static::configure('delete', $path, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve la ruta actual tomando como raíz la ruta de instalación de DuckBrain.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function currentPath() : string
|
||||
{
|
||||
return preg_replace('/'.preg_quote(static::basePath(), '/').'/',
|
||||
'/', strtok($_SERVER['REQUEST_URI'], '?'), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aplica la configuración de rutas.
|
||||
*
|
||||
* @param string|null $path (opcional) Ruta a usar. Si no se define, detecta la ruta actual.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function apply(?string $path = null): void
|
||||
{
|
||||
$path = $path ?? static::currentPath();
|
||||
$routers = match($_SERVER['REQUEST_METHOD']) { // Según el método selecciona un arreglo de routers
|
||||
'POST' => static::$post,
|
||||
'PUT' => static::$put,
|
||||
'PATCH' => static::$patch,
|
||||
'DELETE' => static::$delete,
|
||||
default => static::$get
|
||||
};
|
||||
|
||||
foreach ($routers as $router) { // revisa todos los routers para ver si coinciden con la ruta actual
|
||||
if (preg_match_all('/^'.$router['path'].'\/?$/si',$path, $matches, PREG_PATTERN_ORDER)) {
|
||||
unset($matches[0]);
|
||||
|
||||
// Objtener un reflection del callback
|
||||
$lastCallback = $router['callback'][0];
|
||||
if ($lastCallback instanceof \Closure) { // si es función anónima
|
||||
$reflectionCallback = new \ReflectionFunction($lastCallback);
|
||||
} else {
|
||||
if (is_string($lastCallback))
|
||||
$lastCallback = preg_split('/::/', $lastCallback);
|
||||
|
||||
// Revisamos su es un método o solo una función
|
||||
if (count($lastCallback) == 2)
|
||||
$reflectionCallback = new \ReflectionMethod($lastCallback[0], $lastCallback[1]);
|
||||
else
|
||||
$reflectionCallback = new \ReflectionFunction($lastCallback[0]);
|
||||
}
|
||||
|
||||
// Obtener los parámetros
|
||||
$arguments = $reflectionCallback->getParameters();
|
||||
if (isset($arguments[0])) {
|
||||
// Obtenemos la clase del primer parámetro
|
||||
$argumentClass = strval($arguments[0]->getType());
|
||||
|
||||
// Verificamos si la clase está o no tipada
|
||||
if (empty($argumentClass)) {
|
||||
$request = new Request;
|
||||
} else {
|
||||
$request = new $argumentClass;
|
||||
|
||||
// Verificamos que sea instancia de Request (requerimiento)
|
||||
if (!($request instanceof Request))
|
||||
throw new \Exception('Bad argument type on router callback.');
|
||||
}
|
||||
} else {
|
||||
$request = new Request;
|
||||
}
|
||||
|
||||
// Comprobando y guardando los parámetros variables de la ruta
|
||||
if (isset($matches[1])) {
|
||||
foreach ($matches as $index => $match) {
|
||||
$paramName = $router['paramNames'][$index-1];
|
||||
$request->params->$paramName = urldecode($match[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Llama a la validación y luego procesa la cola de callbacks
|
||||
$request->next = $router['callback'];
|
||||
$data = $request->handle();
|
||||
|
||||
// Por defecto imprime como JSON si se retorna algo
|
||||
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, [new Request]);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
201
src/Libs/Validator.php
Normal file
201
src/Libs/Validator.php
Normal file
@ -0,0 +1,201 @@
|
||||
<?php
|
||||
/**
|
||||
* Validator - DuckBrain
|
||||
*
|
||||
* Libería complementaria de la libería Request.
|
||||
* Sirve para simplpificar la verificación de valores.
|
||||
*
|
||||
* Tiene la posibilida de verificar tanto reglas individuales como en lote.
|
||||
*
|
||||
* |----------+--------------------------------------------------------|
|
||||
* | Regla | Descripción |
|
||||
* |----------+--------------------------------------------------------|
|
||||
* | not | Niega la siguiente regla. Ej: not:float |
|
||||
* | exists | Es requerido; debe estar definido y puede estar vacío |
|
||||
* | required | Es requerido; debe estar definido y no vacío |
|
||||
* | number | Es numérico |
|
||||
* | int | Es entero |
|
||||
* | float | Es un float |
|
||||
* | bool | Es booleano |
|
||||
* | email | Es un correo |
|
||||
* | enum | Esta en un lista ve valores. Ej: enum:admin,user,guest |
|
||||
* | url | Es una url válida |
|
||||
* |----------+--------------------------------------------------------|
|
||||
*
|
||||
* Las listas de reglas están separadas por |, Ej: required|email
|
||||
*
|
||||
* @author KJ
|
||||
* @website https://kj2.me
|
||||
* @licence MIT
|
||||
*/
|
||||
|
||||
namespace Libs;
|
||||
|
||||
class Validator {
|
||||
public static string $lastFailed = '';
|
||||
|
||||
/**
|
||||
* Validar lista de reglas sobre las propiedades de un objeto.
|
||||
*
|
||||
* @param array $rulesList Lista de reglas.
|
||||
* @param Neuron $haystack Objeto al que se le verificarán las reglas.
|
||||
*
|
||||
* @return bool Retorna true solo si todas las reglas se cumplen y false en cuanto una falle.
|
||||
*/
|
||||
public static function validateList(array $rulesList, Neuron $haystack): bool
|
||||
{
|
||||
foreach ($rulesList as $target => $rules) {
|
||||
$rules = preg_split('/\|/', $rules);
|
||||
foreach ($rules as $rule) {
|
||||
if (static::checkRule($haystack->{$target}, $rule))
|
||||
continue;
|
||||
static::$lastFailed = $target.'.'.$rule;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revisa si una regla se cumple.
|
||||
*
|
||||
* @param mixed $subject Lo que se va a verfificar.
|
||||
* @param string $rule La regla a probar.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function checkRule(mixed $subject, string $rule): bool
|
||||
{
|
||||
$arguments = preg_split('/[:,]/', $rule);
|
||||
$rule = [static::class, $arguments[0]];
|
||||
$arguments[0] = $subject;
|
||||
|
||||
if (is_callable($rule))
|
||||
return call_user_func_array($rule, $arguments);
|
||||
|
||||
throw new \Exception('Bad rule: "'.preg_split('/::/', $rule)[1].'"' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica la regla de manera negativa.
|
||||
*
|
||||
* @param mixed $subject Lo que se va a verfificar.
|
||||
* @param mixed $rule La regla a probar.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function not(mixed $subject, ...$rule): bool
|
||||
{
|
||||
return !static::checkRule($subject, join(':', $rule));
|
||||
}
|
||||
|
||||
/**
|
||||
* Comprueba que que esté definido/exista.
|
||||
*
|
||||
* @param mixed $subject
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function exists(mixed $subject): bool
|
||||
{
|
||||
return isset($subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Comprueba que que esté definido y no esté vacío.
|
||||
*
|
||||
* @param mixed $subject
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function required(mixed $subject): bool
|
||||
{
|
||||
return isset($subject) && !empty($subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* number
|
||||
*
|
||||
* @param mixed $subject
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function number(mixed $subject): bool
|
||||
{
|
||||
return is_numeric($subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* int
|
||||
*
|
||||
* @param mixed $subject
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function int(mixed $subject): bool
|
||||
{
|
||||
return filter_var($subject, FILTER_VALIDATE_INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* float
|
||||
*
|
||||
* @param mixed $subject
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function float(mixed $subject): bool
|
||||
{
|
||||
return filter_var($subject, FILTER_VALIDATE_FLOAT);
|
||||
}
|
||||
|
||||
/**
|
||||
* bool
|
||||
*
|
||||
* @param mixed $subject
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function bool(mixed $subject): bool
|
||||
{
|
||||
return filter_var($subject, FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
|
||||
/**
|
||||
* email
|
||||
*
|
||||
* @param mixed $subject
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function email(mixed $subject): bool
|
||||
{
|
||||
return filter_var($subject, FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
/**
|
||||
* url
|
||||
*
|
||||
* @param mixed $subject
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function url(mixed $subject): bool
|
||||
{
|
||||
return filter_var($subject, FILTER_VALIDATE_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* enum
|
||||
*
|
||||
* @param mixed $subject
|
||||
* @param mixed $values
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function enum(mixed $subject, ...$values): bool
|
||||
{
|
||||
return in_array($subject, $values);
|
||||
}
|
||||
}
|
@ -1,38 +1,164 @@
|
||||
<?php
|
||||
/*
|
||||
* DuckBrain - Microframework
|
||||
*
|
||||
* Manejador de vistas simplificado.
|
||||
*
|
||||
* Autor: KJ
|
||||
* Web: https://kj2.me
|
||||
* Licencia: MIT
|
||||
/**
|
||||
* View - DuckBrain
|
||||
*
|
||||
* Manejador de vistas simplificado.
|
||||
*
|
||||
* @author KJ
|
||||
* @website https://kj2.me
|
||||
* @licence MIT
|
||||
*/
|
||||
|
||||
namespace Libs;
|
||||
|
||||
class View {
|
||||
class View extends Neuron {
|
||||
|
||||
/*
|
||||
* 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');
|
||||
}
|
||||
/**
|
||||
* Incluye el archivo.
|
||||
*
|
||||
* @param string $viewName Ruta relativa y el nommbre sin extensión del archivo.
|
||||
* @param string|null $viewPath (opcional) Ruta donde se encuentra la vista.
|
||||
* @param string $extension (opcional) Extensión del archivo.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function include(
|
||||
string $viewName,
|
||||
?string $viewPath = null,
|
||||
string $extension = 'php'
|
||||
): void
|
||||
{
|
||||
$view = $this;
|
||||
|
||||
if (isset($viewPath) &&
|
||||
file_exists("$viewPath$viewName.$extension")) {
|
||||
include("$viewPath$viewName.$extension");
|
||||
return;
|
||||
}
|
||||
|
||||
include(ROOT_CORE."/Views/$viewName.$extension");
|
||||
}
|
||||
|
||||
/**
|
||||
* Función que "renderiza" las vistas
|
||||
*
|
||||
* @param string $viewName Ruta relativa y el nommbre sin extensión del archivo.
|
||||
* @param array|Neuron $params (opcional) Arreglo que podrá ser usado en la vista mediante $view ($param['index'] se usaría así: $view->index)
|
||||
* @param string|null $viewPath (opcional) 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/".
|
||||
* @param string $extension (opcional) Extensión del archivo.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function render(
|
||||
string $viewName,
|
||||
array|Neuron $params = [],
|
||||
?string $viewPath = null,
|
||||
string $extension = 'php'
|
||||
): void
|
||||
{
|
||||
$instance = new View($params);
|
||||
$instance->html($viewName, $viewPath, $extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderiza las vistas HTML
|
||||
*
|
||||
* @param string $viewName Ruta relativa y el nommbre sin extensión del archivo ubicado en src/Views
|
||||
* @param string|null $viewPath (opcional) 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/".
|
||||
* @param string $extension (opcional) Extensión del archivo.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function html(
|
||||
string $viewName,
|
||||
?string $viewPath = null,
|
||||
string $extension = 'php'
|
||||
): void
|
||||
{
|
||||
$this->include(
|
||||
$viewName,
|
||||
$viewPath,
|
||||
$extension
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderiza código CSS.
|
||||
*
|
||||
* @param string $viewName Ruta relativa y el nommbre sin extensión del archivo ubicado en src/Views
|
||||
* @param string|null $viewPath (opcional) 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/".
|
||||
* @param string $extension (opcional) Extensión del archivo.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function css(
|
||||
string $viewName,
|
||||
?string $viewPath = null,
|
||||
string $extension = 'css'
|
||||
): void
|
||||
{
|
||||
header("Content-type: text/css");
|
||||
$this->include($viewName, $viewPath, $extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderiza código Javascript.
|
||||
*
|
||||
* @param string $viewName Ruta relativa y el nommbre sin extensión del archivo ubicado en src/Views
|
||||
* @param string|null $viewPath (opcional) 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/".
|
||||
* @param string $extension (opcional) Extensión del archivo.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function js(
|
||||
string $viewName,
|
||||
?string $viewPath = null,
|
||||
string $extension = 'js'
|
||||
): void
|
||||
{
|
||||
header("Content-type: application/javascript");
|
||||
$this->include($viewName, $viewPath, $extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Imprime los datos en Json.
|
||||
*
|
||||
* @param object|array $data Objeto o array que se imprimirá a JSON.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function json(object|array $data): void
|
||||
{
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
print(json_encode($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Imprime los datos en texto plano
|
||||
*
|
||||
* @param string $txt Contenido de texto.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function text(string $txt): void
|
||||
{
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
print($txt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intenta devolver la url absoluta a partir de una ruta relativa.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function route(string $path = '/'): string
|
||||
{
|
||||
if (defined('SITE_URL') && !empty(SITE_URL))
|
||||
return rtrim(SITE_URL, '/').'/'.ltrim($path, '/');
|
||||
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
0
src/Middlewares/.keep
Normal file
0
src/Middlewares/.keep
Normal file
0
src/Models/.keep
Normal file
0
src/Models/.keep
Normal file
0
src/Routers/.keep
Normal file
0
src/Routers/.keep
Normal file
0
src/Views/.keep
Normal file
0
src/Views/.keep
Normal file
Reference in New Issue
Block a user