Este es un framework PHP completamente desarrollado desde cero que sigue el patrón MVC (Modelo-Vista-Controlador). Incluye funcionalidades avanzadas como generación automática de código, sistema de autenticación, enrutamiento dinámico y componentes reutilizables.
- Estructura del Proyecto
- Configuración Inicial
- Generación de Código con Framecode
- Sistema de Rutas
- Controladores
- Modelos
- Vistas y Layouts
- Sistema de Autenticación
- Componentes y Helpers
- Base de Datos
- Migraciones
php-base/
├── framecode # CLI para generación de código
├── index.php # Punto de entrada
├── config/ # Configuración del proyecto
│ ├── config.php # Configuración principal
│ ├── menu.php # Configuración del menú
│ └── routes.php # Definición de rutas
├── core/ # Núcleo del framework
│ ├── classes/ # Clases principales
│ │ ├── Auth.php # Clase base de autenticación
│ │ ├── BaseController.php # Controlador base
│ │ ├── BaseModel.php # Modelo base
│ │ ├── Db.php # Gestión de base de datos
│ │ └── Routes.php # Sistema de rutas
│ ├── config/ # Configuración del core
│ ├── components/ # Componentes reutilizables
│ ├── generations/ # Sistema de generación de código
│ └── views/ # Plantillas del core
├── controllers/ # Controladores de la aplicación
├── models/ # Modelos de la aplicación
├── views/ # Vistas de la aplicación
├── auth/ # Clases de autenticación
└── public/ # Archivos públicos (CSS, JS)
Edita el archivo config/config.php:
<?php
define('BASE_URL', 'https://tu-dominio.com/');
define('HOME_URL', 'https://tu-dominio.com/dashboard/');
define('APP_NAME', 'Mi Aplicación');
define('DB_HOST', 'localhost');
define('DB_NAME', 'mi_base_datos');
define('DB_USER', 'usuario');
define('DB_PASS', 'contraseña');
define('IPS_ALLOW', ['127.0.0.1']); // IPs permitidas para rutas privadas
define('EXPIRED_SESSION', 3600 * 24); // Duración de la sesiónEl framework requiere que todas las peticiones se redirijan a index.php. Configura tu servidor web:
Apache (.htaccess):
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]Nginx:
location / {
try_files $uri $uri/ /index.php?$query_string;
}El framework incluye una potente herramienta CLI llamada framecode para generar código automáticamente.
php framecode <tipo> <nombre> [auth]php framecode model ProductCrea: models/Product.php
php framecode controller ProductCrea: controllers/ProductController.php
php framecode resource Product authUserCrea:
models/Product.phpcontrollers/ProductController.php- Rutas CRUD en
config/routes.php
Cuando generas un recurso completo, se crean automáticamente:
Rutas generadas:
GET /product/list- Listar productosGET /product/view/:id- Ver producto específicoGET /product/edit/:id- Formulario de ediciónPOST /product/update- Actualizar productoGET /product/new- Formulario de creaciónPOST /product/create- Crear producto
Las rutas se definen en config/routes.php:
<?php
// Sintaxis: $routes->método(ruta, controlador::método, autenticación, privado)
// Rutas públicas
$routes->get('', 'AuthController::login', false, false);
$routes->get('login', 'AuthController::login', false, false);
$routes->post('access', 'AuthController::access', false, false);
// Rutas protegidas
$routes->get('dashboard', 'DashboardController::index', 'authUser.admin.user', false);
// Rutas con parámetros
$routes->get('users/view/:id', 'UsersController::view', 'authUser.admin.user', false);
$routes->get('products/:category/:id', 'ProductsController::show', 'authUser', false);- GET - Para mostrar datos
- POST - Para crear/procesar datos
- PUT - Para actualizar (futuro)
- DELETE - Para eliminar (futuro)
:id- Parámetro dinámico- Los parámetros se pasan automáticamente al método del controlador
false- Sin autenticación'authUser'- Usuario autenticado'authUser.admin'- Usuario con rol admin'authUser.admin.user'- Usuario con rol admin o user
Las rutas marcadas como privadas (true) solo son accesibles desde IPs definidas en IPS_ALLOW.
Todos los controladores extienden BaseController:
<?php
namespace Controllers;
use core\classes\BaseController;
use Models\Product;
class ProductsController extends BaseController {
protected $model_name = 'Product';
public function __construct() {
parent::__construct();
}
}El BaseController proporciona métodos CRUD automáticos:
list($params)- Lista paginada con búsquedaview($params)- Ver registro específicoedit($params)- Formulario de ediciónupdate($params)- Procesar actualizaciónnew($params)- Formulario de creacióncreate($params)- Procesar creación
public function customMethod($params) {
// Tu lógica personalizada
$data = $this->model->customQuery();
charge_layout(
'general',
['custom/view'],
[
'title' => 'Título Personalizado',
'data' => $data
]
);
}Todos los modelos extienden BaseModel:
<?php
namespace Models;
use core\classes\BaseModel;
class Product extends BaseModel
{
public $table = 'products';
public $primaryKey = 'id';
public $timestamps = true;
// Campos para búsqueda
public $searchable = ['name', 'description'];
// Columnas para mostrar en listas
public $head = ['ID', 'Nombre', 'Precio', 'Categoría'];
// Etiquetas
public $label = 'Producto';
public $plural = 'Productos';
// Acciones disponibles
public $actions = [
'view' => ['Ver', '/products/view'],
'edit' => ['Editar', '/products/edit'],
'delete' => ['Eliminar', '/products/delete'],
];
public function columns(): array
{
return [
'id' => [
'type' => 'int',
'required' => true,
'show_in_list' => true,
'show_in_form' => false,
'auto_increment' => true,
'label' => 'ID'
],
'name' => [
'type' => 'varchar',
'max_length' => 100,
'required' => true,
'unique' => false,
'show_in_list' => true,
'show_in_form' => true,
'label' => 'Nombre',
'type_input' => 'text'
],
'price' => [
'type' => 'decimal',
'required' => true,
'show_in_list' => true,
'show_in_form' => true,
'label' => 'Precio',
'type_input' => 'number'
],
'category_id' => [
'type' => 'int',
'required' => true,
'show_in_list' => true,
'show_in_form' => true,
'label' => 'Categoría',
'type_input' => 'select',
'foreign_key' => [
'table' => 'categories',
'column' => 'id',
'display_column' => 'name'
]
],
'description' => [
'type' => 'text',
'required' => false,
'show_in_list' => false,
'show_in_form' => true,
'label' => 'Descripción',
'type_input' => 'text_area'
]
];
}
}type- Tipo de dato (int, varchar, text, etc.)required- Campo obligatoriounique- Valor único en la base de datosshow_in_list- Mostrar en listadosshow_in_form- Mostrar en formulariosauto_increment- Campo auto-incrementalencrypted- Encriptar valor (para contraseñas)label- Etiqueta para mostrartype_input- Tipo de input HTMLforeign_key- Relación con otra tablaselect_options- Opciones para selects
// Crear registro
$model->create($data);
// Buscar todos
$model->all();
// Buscar con condiciones
$model->find($where, $type, $order, $limit, $page);
// Buscar por campo
$model->findBy($field, $value);
// Actualizar
$model->update($id, $data);
// Eliminar
$model->delete($id);
// Consulta personalizada
$model->query($sql, $params);El framework usa un sistema de layouts para mantener consistencia:
charge_layout($layout, $views, $data);Parámetros:
$layout- Layout a usar ('general', 'nouser')$views- Array de vistas a cargar$data- Datos a pasar a las vistas
charge_layout(
'general',
['products/list'],
[
'title' => 'Lista de Productos',
'products' => $products,
'categories' => $categories
]
);- general - Layout con autenticación (header, sidebar)
- nouser - Layout sin autenticación (solo login)
Carga componentes reutilizables:
charge_partial('header', $data);
charge_partial('sidebar', $data);
charge_partial('footer', $data);El framework incluye plantillas automáticas para CRUD:
templates/list- Lista con paginación y búsquedatemplates/form- Formulario automático basado en el modelo
Crea clases de autenticación extendiendo Auth:
<?php
namespace Auth;
use core\classes\Auth;
class AuthUser extends Auth
{
public function canAccess($roles = []): bool
{
global $user;
// Verificar si el usuario está logueado
if (!$user) {
return false;
}
// Si no se especifican roles, cualquier usuario autenticado puede acceder
if (empty($roles)) {
return true;
}
// Verificar si el usuario tiene el rol requerido
return in_array($user['role'], $roles);
}
}El framework maneja automáticamente:
- Inicio de sesión
- Expiración de sesiones
- Verificación de permisos
- Redirecciones automáticas
El usuario autenticado está disponible globalmente:
global $user;
echo $user['name']; // Nombre del usuario
echo $user['role']; // Rol del usuario// Input de texto
input('text', 'name', 'Nombre', $value);
// Select
select('category', $options, 'Categoría', $selected);
// Textarea
textarea('description', $value, 'Descripción');
// Botón
button('submit', 'Guardar');// Debug
e($variable); // Muestra variable con información de debug
// URLs
current_url(); // URL actual completa
current_path(); // Ruta actual
current_segments(); // Segmentos de la URL como array
// HTTP
method_request(); // Método HTTP actual
// Archivos
get_files_in_directory($directory); // Lista archivos en directorioLa configuración se realiza en config/config.php:
define('DB_HOST', 'localhost');
define('DB_NAME', 'mi_base_datos');
define('DB_USER', 'usuario');
define('DB_PASS', 'contraseña');use core\classes\Db;
$db = new Db();
$conn = $db->getConnection(); // Retorna instancia PDO// En un modelo
public function customQuery() {
$sql = "SELECT * FROM products WHERE price > :price";
$params = [':price' => 100];
return $this->query($sql, $params);
}# Generar recurso completo
php framecode resource Product authUserEdita models/Product.php:
- Define las columnas en
columns() - Ejecuta las migraciones
- Configura propiedades del modelo
- Añade métodos personalizados si es necesario
Edita controllers/ProductController.php:
- Añade métodos personalizados
- Sobrescribe métodos CRUD si es necesario
Si necesitas vistas más allá de las plantillas automáticas:
- Crea archivos en
views/pages/products/ - Usa
charge_layout()en el controlador
Ajusta los permisos en las rutas según tus necesidades de autenticación.
- Usa el generador de código - Te ahorra tiempo y mantiene consistencia
- Define bien las columnas - El sistema automático depende de esta definición
- Mantén la estructura - No modifiques los archivos del core sin necesidad
- Usa las plantillas automáticas - Son responsive y funcionales
- Aprovecha los helpers - Facilitan tareas comunes
- Configura bien la autenticación - Define roles claros para tu aplicación
Este framework está diseñado para ser extensible. Puedes:
- Añadir nuevas clases en
core/classes/ - Crear componentes reutilizables en
core/components/ - Añadir helpers en
core/config/helpers.php - Crear nuevos layouts en
views/layouts/
Para soporte o contribuciones, revisa el código fuente y adapta según tus necesidades específicas.
El framework incluye un sistema completo de migraciones que permite gestionar la estructura de la base de datos y poblarla con datos iniciales.
El sistema proporciona tres endpoints principales para gestionar las migraciones:
GET /migrations
Ejecuta únicamente las migraciones pendientes, creando o modificando la estructura de las tablas.
GET /migrations/all
Ejecuta las migraciones pendientes y posteriormente ejecuta todos los seeders para poblar las tablas con datos iniciales.
GET /migrations/all/fresh
- Elimina todas las tablas existentes
- Recrea la estructura completa de la base de datos
- Ejecuta todas las migraciones desde cero
- Ejecuta todos los seeders
Las migraciones se generan automáticamente cuando creas un modelo con el comando framecode. El sistema:
- Analiza la estructura definida en el método
columns()del modelo - Genera las tablas con los tipos de datos correctos
- Crea índices automáticamente para claves foráneas
- Establece relaciones entre tablas
Cuando defines un modelo como este:
public function columns(): array
{
return [
'id' => [
'type' => 'int',
'auto_increment' => true,
'required' => true
],
'name' => [
'type' => 'varchar',
'max_length' => 100,
'required' => true,
'unique' => true
],
'category_id' => [
'type' => 'int',
'required' => true,
'foreign_key' => [
'table' => 'categories',
'column' => 'id'
]
]
];
}El sistema genera automáticamente:
- Tabla
productscon las columnas especificadas - Clave primaria en
id - Índice único en
name - Clave foránea referenciando
categories.id - Campos
created_atyupdated_atsi$timestamps = true
Los seeders permiten poblar las tablas con datos iniciales. Para crear un seeder:
- Crear archivo en la carpeta
seeders/con el mismo nombre del modelo - Definir variable
$seedque contenga un array con los datos a insertar
seeders/
├── Product.php
├── Category.php
├── User.php
└── ...
Archivo: seeders/Product.php
<?php
$seed = [
[
'name' => 'Producto Demo 1',
'price' => 99.99,
'category_id' => 1,
'description' => 'Descripción del producto demo'
],
[
'name' => 'Producto Demo 2',
'price' => 149.99,
'category_id' => 2,
'description' => 'Otro producto de ejemplo'
],
[
'name' => 'Producto Demo 3',
'price' => 79.99,
'category_id' => 1,
'description' => 'Tercer producto de la línea demo'
]
];Archivo: seeders/Category.php
<?php
$seed = [
[
'name' => 'Electrónicos',
'description' => 'Productos electrónicos y tecnología'
],
[
'name' => 'Ropa',
'description' => 'Vestimenta y accesorios'
],
[
'name' => 'Hogar',
'description' => 'Artículos para el hogar'
]
];Los seeders se ejecutan automáticamente respetando las dependencias de claves foráneas. En el ejemplo anterior, Category se ejecutaría antes que Product para garantizar que las categorías existan cuando se inserten los productos.
# 1. Generar modelo con estructura
php framecode resource Product authUser
# 2. Ejecutar migraciones para crear las tablas
GET /migrations
# 3. Poblar con datos de prueba
GET /migrations/all# Modificar estructura del modelo en columns()
# Ejecutar migraciones para aplicar cambios
GET /migrations# Cuando necesites empezar desde cero
GET /migrations/all/fresh# Solo ejecutar migraciones nuevas
GET /migrations- Seguridad: Las rutas de migración deben estar protegidas en producción
- Backups: Siempre respalda la base de datos antes de usar
/fresh - Orden: Las migraciones respetan las dependencias de claves foráneas
- Rollback: El sistema no incluye rollback automático - usa backups