Ejemplo PHP + POO + MVC

Seguro que algunos que hayan leído o visto algunos de los tutoriales o ejemplos que pongo sobre programación en PHP con y sin frameworks, pueden no estar de acuerdo conmigo en ciertos detalles, o incluso estar pensando «este chico no está programando verdaderamente orientado a objetos» o «no sigue el paradigma a rajatabla» (todo lo que explico lo hago desde mi punto de vista actual, nunca digo que sea la verdad absoluta o lo más correcto siempre).

Pues bien hoy voy a poner un ejemplo muy bueno de como programar realmente orientado a objetos en PHP puro con MVC. Lo que voy a mostrar hoy perfectamente podría ser la base para construirnos un pequeño framework propio, veremos incluso como hacer un controlador frontal, como crear objetos que representen entidades de la base de datos, etc, por lo tanto lo que voy a enseñar hoy es un ejemplo muy didáctico y muy completo.

Estructura de directorios

estructura ficheros php poo mvc

En nuestro «framework» tendremos varios directorios:

  • config: aquí irán los ficheros de configuración de la base de datos, globales, etc.
  • controller: como sabemos en la arquitectura MVC los controladores se encargarán de recibir y filtrar datos que le llegan de las vistas, llamar a los modelos y pasar los datos de estos a las vistas. Pues en este directorio colocaremos los controladores
  • core: aquí colocaremos las clases base de las que heredarán por ejemplo controladores y modelos, y también podríamos colocar más librerías hechas por nosotros o por terceros, esto sería el núcleo del framework.
  • model: aquí irán los modelos, para ser fieles al paradigma orientado objetos tenemos que tener una clase por cada tabla o entidad de la base de datos(excepto para las tablas pivote) y estas clases servirán para crear objetos de ese tipo de entidad(por ejemplo crear un objeto usuario para crear un usuario en la BD). También tendremos modelos de consulta a la BD que contendrán consultas más complejas que estén relacionadas con una o varias entidades.
  • view: aquí iran las vistas, es decir, donde se imprimirán los datos y lo que verá el usuario.
  • index.php será el controlador frontal por el que pasará absolutamente todo en la aplicación.

Aprende todo de PHP, MVC y sus frameworks aquí: Master en PHP: Aprende PHP, SQL, POO, MVC, Laravel, Symfony 4, WordPress y más

Crear ficheros de configuración

En el directorio config, crearemos un fichero database.php en el que irá la configuración de la base de datos. Este devuelve un array que posteriormente utilizaremos.

<?php
return array(
    "driver"    =>"mysql",
    "host"      =>"localhost",
    "user"      =>"root",
    "pass"      =>"",
    "database"  =>"pruebas",
    "charset"   =>"utf8"
);
?>

También podemos crearnos un fichero gobal.php en el que irán constantes que luego nos servirán por ejemplo para establecer controladores y acciones por defecto (y todo lo que queramos meterle).

<?php
define("CONTROLADOR_DEFECTO", "Usuarios");
define("ACCION_DEFECTO", "index");
//Más constantes de configuración
?>

Crear las clases del núcleo

Primero crearemos la clase Conectar que nos servirá para conectarnos a la base de datos utilizando el driver MySQLi que es el más rápido aunque muchos por ahí recomienden PDO, y también nos servirá para conectar a la base de datos un constructor de consultas que he incluido en el proyecto llamado Fluent Query Builder (el que utiliza Laravel).

<?php
class Conectar{
    private $driver;
    private $host, $user, $pass, $database, $charset;
  
    public function __construct() {
        $db_cfg = require_once 'config/database.php';
        $this->driver=$db_cfg["driver"];
        $this->host=$db_cfg["host"];
        $this->user=$db_cfg["user"];
        $this->pass=$db_cfg["pass"];
        $this->database=$db_cfg["database"];
        $this->charset=$db_cfg["charset"];
    }
    
    public function conexion(){
        
        if($this->driver=="mysql" || $this->driver==null){
            $con=new mysqli($this->host, $this->user, $this->pass, $this->database);
            $con->query("SET NAMES '".$this->charset."'");
        }
        
        return $con;
    }
    
    public function startFluent(){
        require_once "FluentPDO/FluentPDO.php";
        
        if($this->driver=="mysql" || $this->driver==null){
            $pdo = new PDO($this->driver.":dbname=".$this->database, $this->user, $this->pass);
            $fpdo = new FluentPDO($pdo);
        }
        
        return $fpdo;
    }
}
?>

Seguimos creando el fichero EntidadBase.php de esta clase heredarán los modelos que representen entidades, en el constructor le pasaremos el nombre de la tabla y tendremos tantos métodos como queramos para ayudarnos con las peticiones a la BD a través de los objetos que iremos creando. Lo bueno que tiene es que estos métodos pueden ser reutilizados en otras clases ya que le indicamos la tabla en el constructor.

<?php
class EntidadBase{
    private $table;
    private $db;
    private $conectar;

    public function __construct($table) {
        $this->table=(string) $table;
        
        require_once 'Conectar.php';
        $this->conectar=new Conectar();
        $this->db=$this->conectar->conexion();
    }
    
    public function getConetar(){
        return $this->conectar;
    }
    
    public function db(){
        return $this->db;
    }
    
    public function getAll(){
        $query=$this->db->query("SELECT * FROM $this->table ORDER BY id DESC");
         
        //Devolvemos el resultset en forma de array de objetos
        while ($row = $query->fetch_object()) {
           $resultSet[]=$row;
        }
        
        return $resultSet;
    }
    
    public function getById($id){
        $query=$this->db->query("SELECT * FROM $this->table WHERE id=$id");

        if($row = $query->fetch_object()) {
           $resultSet=$row;
        }
        
        return $resultSet;
    }
    
    public function getBy($column,$value){
        $query=$this->db->query("SELECT * FROM $this->table WHERE $column='$value'");

        while($row = $query->fetch_object()) {
           $resultSet[]=$row;
        }
        
        return $resultSet;
    }
    
    public function deleteById($id){
        $query=$this->db->query("DELETE FROM $this->table WHERE id=$id"); 
        return $query;
    }
    
    public function deleteBy($column,$value){
        $query=$this->db->query("DELETE FROM $this->table WHERE $column='$value'"); 
        return $query;
    }
    

    /*
     * Aquí podemos montarnos un montón de métodos que nos ayuden
     * a hacer operaciones con la base de datos de la entidad
     */
    
}
?>

Ahora crearemos la clase ModeloBase que heredará de la clase EntidadBase y a su vez será heredada por los modelos de consultas. La clase ModeloBase permitirá utilizar el constructor de consultas que hemos incluido y también los métodos de EntidadBase, así como otros métodos que programemos dentro de la clase, por ejemplo yo tengo un método para ejecutar consultas sql que directamente me devuelve el resultset en un array de objetos preparado para pasárselo a una vista, podríamos tener cientos para diferentes cosas.

<?php
class ModeloBase extends EntidadBase{
    private $table;
    private $fluent;
    
    public function __construct($table) {
        $this->table=(string) $table;
        parent::__construct($table);
        
        $this->fluent=$this->getConetar()->startFluent();
    }
    
    public function fluent(){
        return $this->fluent;
    }
    
    public function ejecutarSql($query){
        $query=$this->db()->query($query);
        if($query==true){
            if($query->num_rows>1){
                while($row = $query->fetch_object()) {
                   $resultSet[]=$row;
                }
            }elseif($query->num_rows==1){
                if($row = $query->fetch_object()) {
                    $resultSet=$row;
                }
            }else{
                $resultSet=true;
            }
        }else{
            $resultSet=false;
        }
        
        return $resultSet;
    }
    
    //Aqui podemos montarnos métodos para los modelos de consulta
    
}
?>

La siguiente clase que crearemos es ControladoresBase de la cual heredarán los controladores, esta clase carga EntidadesBase, ModelosBase, y todos los modelos creados dentro del directorio model.

<?php
class ControladorBase{

    public function __construct() {
        require_once 'EntidadBase.php';
        require_once 'ModeloBase.php';
        
        //Incluir todos los modelos
        foreach(glob("model/*.php") as $file){
            require_once $file;
        }
    }
    
    //Plugins y funcionalidades
    
/*
* Este método lo que hace es recibir los datos del controlador en forma de array
* los recorre y crea una variable dinámica con el indice asociativo y le da el 
* valor que contiene dicha posición del array, luego carga los helpers para las
* vistas y carga la vista que le llega como parámetro. En resumen un método para
* renderizar vistas.
*/
    public function view($vista,$datos){
        foreach ($datos as $id_assoc => $valor) {
            ${$id_assoc}=$valor; 
        }
        
        require_once 'core/AyudaVistas.php';
        $helper=new AyudaVistas();
    
        require_once 'view/'.$vista.'View.php';
    }
    
    public function redirect($controlador=CONTROLADOR_DEFECTO,$accion=ACCION_DEFECTO){
        header("Location:index.php?controller=".$controlador."&action=".$accion);
    }
    
    //Métodos para los controladores

}
?>

Ahora crearemos la clase AyudaVistas que puede contener diversos helpers (pequeños métodos que nos ayuden en pequeñas tareas dentro de las vistas).

<?php
class AyudaVistas{
    
    public function url($controlador=CONTROLADOR_DEFECTO,$accion=ACCION_DEFECTO){
        $urlString="index.php?controller=".$controlador."&action=".$accion;
        return $urlString;
    }
    
    //Helpers para las vistas
}
?>

Ahora crearemos el fichero ControladorFrontal.func.php que tiene las funciones que se encargan de cargar un controlador u otro y una acción u otra en función de lo que se le diga por la url.

<?php
//FUNCIONES PARA EL CONTROLADOR FRONTAL

function cargarControlador($controller){
    $controlador=ucwords($controller).'Controller';
    $strFileController='controller/'.$controlador.'.php';
    
    if(!is_file($strFileController)){
        $strFileController='controller/'.ucwords(CONTROLADOR_DEFECTO).'Controller.php';   
    }
    
    require_once $strFileController;
    $controllerObj=new $controlador();
    return $controllerObj;
}

function cargarAccion($controllerObj,$action){
    $accion=$action;
    $controllerObj->$accion();
}

function lanzarAccion($controllerObj){
    if(isset($_GET["action"]) && method_exists($controllerObj, $_GET["action"])){
        cargarAccion($controllerObj, $_GET["action"]);
    }else{
        cargarAccion($controllerObj, ACCION_DEFECTO);
    }
}

?>

El controlador frontal index.php

El controlador frontal es donde se cargan todos los ficheros de la aplicación y por tanto la única pagína que visita el usuario realmente es esta, en este caso index.php.

<?php
//Configuración global
require_once 'config/global.php';

//Base para los controladores
require_once 'core/ControladorBase.php';

//Funciones para el controlador frontal
require_once 'core/ControladorFrontal.func.php';

//Cargamos controladores y acciones
if(isset($_GET["controller"])){
    $controllerObj=cargarControlador($_GET["controller"]);
    lanzarAccion($controllerObj);
}else{
    $controllerObj=cargarControlador(CONTROLADOR_DEFECTO);
    lanzarAccion($controllerObj);
}
?>

Modelos y objetos

Si queremos seguir el paradigma de la programación orientada a objetos teóricamente deberíamos tener una clase por cada tabla de la base de datos(excepto tablas pivote) que haga referencia a un objeto de la vida real, en este caso el objeto que crearíamos seria «Usuario» y el usuario tendría un nombre, un apellido, un email, etc, pues bien eso serian los atributos del objeto y tendríamos un método get y set por cada atributo que servirán para establecer el valor de las propiedades y para conseguir el valor de cada atributo. Esta clase hereda de EntidadesBase y tiene un método save para guardar el usuario en la base de datos, podríamos tener otro método update que seria similar, etc.

Y te preguntarás ¿por que no lo haces así siempre? la respuesta es simple, en algunos proyectos en los que hay muchas tablas puede ser engorroso estar creando una clase por cada tabla solamente para tener un insert y un update(aunque tiene sus ventajas) aunque según este paradigma no sea del todo correcto omito esto y directamente creo modelos de consultas en los que tengo métodos que interaccionan con una tabla mayoritariamente o varias según las relaciones que tengan, por otra parte algunos frameworks cuentan con ORMs que nos ayudan con todo esto pero de igual forma cuando tienes muchas tablas relacionadas quizá el uso del ORM sin controlarlo muy bien puede dificultar la tarea, en cualquier caso lo más correcto es tener una clase por entidad aunque a veces no sea lo más practico o cómodo.

Truco: si usas NetBeans puedes generar los getters y setters desde el menú Source -> Insert Code

<?php
class Usuario extends EntidadBase{
    private $id;
    private $nombre;
    private $apellido;
    private $email;
    private $password;
    
    public function __construct() {
        $table="usuarios";
        parent::__construct($table);
    }
    
    public function getId() {
        return $this->id;
    }

    public function setId($id) {
        $this->id = $id;
    }
    
    public function getNombre() {
        return $this->nombre;
    }

    public function setNombre($nombre) {
        $this->nombre = $nombre;
    }

    public function getApellido() {
        return $this->apellido;
    }

    public function setApellido($apellido) {
        $this->apellido = $apellido;
    }

    public function getEmail() {
        return $this->email;
    }

    public function setEmail($email) {
        $this->email = $email;
    }

    public function getPassword() {
        return $this->password;
    }

    public function setPassword($password) {
        $this->password = $password;
    }

    public function save(){
        $query="INSERT INTO usuarios (id,nombre,apellido,email,password)
                VALUES(NULL,
                       '".$this->nombre."',
                       '".$this->apellido."',
                       '".$this->email."',
                       '".$this->password."');";
        $save=$this->db()->query($query);
        //$this->db()->error;
        return $save;
    }

}
?>

Aquí pondríamos las consultas completas, en lugar de utilizar los métodos que tenemos en el modelo de entidad, aunque también estarían accesibles desde este modelo.

<?php
class UsuariosModel extends ModeloBase{
    private $table;
    
    public function __construct(){
        $this->table="usuarios";
        parent::__construct($this->table);
    }
    
    //Metodos de consulta
    public function getUnUsuario(){
        $query="SELECT * FROM usuarios WHERE email='victor@victor.com'";
        $usuario=$this->ejecutarSql($query);
        return $usuario;
    }
}
?>

Los controladores

Los crearemos en el directorio controller, en este caso tengo creado UsuariosController.

<?php
class UsuariosController extends ControladorBase{
    
    public function __construct() {
        parent::__construct();
    }
    
    public function index(){
        
        //Creamos el objeto usuario
        $usuario=new Usuario();
        
        //Conseguimos todos los usuarios
        $allusers=$usuario->getAll();
       
        //Cargamos la vista index y le pasamos valores
        $this->view("index",array(
            "allusers"=>$allusers,
            "Hola"    =>"Soy Víctor Robles"
        ));
    }
    
    public function crear(){
        if(isset($_POST["nombre"])){
            
            //Creamos un usuario
            $usuario=new Usuario();
            $usuario->setNombre($_POST["nombre"]);
            $usuario->setApellido($_POST["apellido"]);
            $usuario->setEmail($_POST["email"]);
            $usuario->setPassword(sha1($_POST["password"]));
            $save=$usuario->save();
        }
        $this->redirect("Usuarios", "index");
    }
    
    public function borrar(){
        if(isset($_GET["id"])){ 
            $id=(int)$_GET["id"];
            
            $usuario=new Usuario();
            $usuario->deleteById($id); 
        }
        $this->redirect();
    }
    
    
    public function hola(){
        $usuarios=new UsuariosModel();
        $usu=$usuarios->getUnUsuario();
        var_dump($usu);
    }

}
?>

Las vistas

En este caso tengo la vista indexView.php creada.

<!DOCTYPE HTML>
<html lang="es">
    <head>
        <meta charset="utf-8"/>
        <title>Ejemplo PHP MySQLi POO MVC</title>
        <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
        <script type="text/javascript" src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
        <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
        <style>
            input{
                margin-top:5px;
                margin-bottom:5px;
            }
            .right{
                float:right;
            }
        </style>
    </head>
    <body>
        <form action="<?php echo $helper->url("usuarios","crear"); ?>" method="post" class="col-lg-5">
            <h3>Añadir usuario</h3>
            <hr/>
            Nombre: <input type="text" name="nombre" class="form-control"/>
            Apellido: <input type="text" name="apellido" class="form-control"/>
            Email: <input type="text" name="email" class="form-control"/>
            Contraseña: <input type="password" name="password" class="form-control"/>
            <input type="submit" value="enviar" class="btn btn-success"/>
        </form>
        
        <div class="col-lg-7">
            <h3>Usuarios</h3>
            <hr/>
        </div>
        <section class="col-lg-7 usuario" style="height:400px;overflow-y:scroll;">
            <?php foreach($allusers as $user) { //recorremos el array de objetos y obtenemos el valor de las propiedades ?>
                <?php echo $user->id; ?> -
                <?php echo $user->nombre; ?> -
                <?php echo $user->apellido; ?> -
                <?php echo $user->email; ?>
                <div class="right">
                    <a href="<?php echo $helper->url("usuarios","borrar"); ?>&id=<?php echo $user->id; ?>" class="btn btn-danger">Borrar</a>
                </div>
                <hr/>
            <?php } ?>
        </section>
        <footer class="col-lg-12">
            <hr/>
           Ejemplo PHP MySQLi POO MVC - Víctor Robles - <a href="http://victorroblesweb.es">victorroblesweb.es</a> - Copyright &copy; <?php echo  date("Y"); ?>
        </footer>
    </body>
</html>

El resultado «final», está cargando el controlador y acción por defecto.
php poo mvc

Para acceder a otros controladores y acciones por la url.
php poo mvc ruta

Este ejemplo está hecho en unas tres horas, podría trabajarse mucho más y construirnos un marco de trabajo muy bonito para nosotros y hecho por nosotros con lo cual tendríamos un control y conocimiento absoluto de lo que pasa por debajo. El siguiente paso seria limpiar la URL mediante un .htaccess, y seguir dándole funcionalidades, añadiendo nuestras propias librerías para toda clase de tareas o de terceros como por ejemplo PHPThumb, HTML2PDF, SwiftMailer, Twig si queremos un motor de plantillas poderoso o incluso Active Record ORM para darle más potencia.

¿Por que cuento todo esto? En primer lugar para disipar las dudas del que pueda creer que no entiendo la programación orientada a objetos, en segundo lugar para demostrar que un ex alumno de un ciclo formativo de administración de sistemas sabe programar igual que cualquier otro de ciclos específicos de programación, en tercer lugar para enseñar como se hace un programa en PHP utilizando POO y MVC con un controlador frontal, y para demostrar que aunque programemos en PHP puro no tenemos porque hacer las cosas mal y no cuesta trabajo hacerlas bien, he visto varios proyectos hechos actualmente que son un verdadero caos y un despropósito, con esto podemos empezar a hacer buenos programas aunque no usemos un framework de terceros.

Correcciones y mejoras (2016)

En el ejemplo tenemos un error a la hora de llamar a varias entidades, esto es porque estamos haciendo la conexión a la base de datos varias veces, lo cual es un error. Veamos como mejorar esto:

Primero modificaremos ControladorBase para hacer que el fichero Conectar.php esté disponible en todo el código.

<?php
class ControladorBase{

    public function __construct() {
	require_once 'Conectar.php';
        require_once 'EntidadBase.php';
        require_once 'ModeloBase.php';
        
        //Incluir todos los modelos
        foreach(glob("model/*.php") as $file){
            require_once $file;
        }
    }
    
    //Plugins y funcionalidades
    
    public function view($vista,$datos){
        foreach ($datos as $id_assoc => $valor) {
            ${$id_assoc}=$valor; 
        }
        
        require_once 'core/AyudaVistas.php';
        $helper=new AyudaVistas();
    
        require_once 'view/'.$vista.'View.php';
    }
    
    public function redirect($controlador=CONTROLADOR_DEFECTO,$accion=ACCION_DEFECTO){
        header("Location:index.php?controller=".$controlador."&action=".$accion);
    }
    
    //Métodos para los controladores

}
?>

Ahora lo que vamos a hacer es pasarle un parámetro al constructor de EntidadBase, le vamos a pasar directamente la conexión a la base de datos con el parametro $adapter:

<?php
class EntidadBase{
    private $table;
    private $db;
    private $conectar;

    public function __construct($table, $adapter) {
        $this->table=(string) $table;
        
		/*
        require_once 'Conectar.php';
        $this->conectar=new Conectar();
        $this->db=$this->conectar->conexion();
		 */
		$this->conectar = null;
		$this->db = $adapter;
    }
    
    public function getConetar(){
        return $this->conectar;
    }
    
    public function db(){
        return $this->db;
    }
    
    public function getAll(){
        $query=$this->db->query("SELECT * FROM $this->table ORDER BY id DESC");

        while ($row = $query->fetch_object()) {
           $resultSet[]=$row;
        }
        
        return $resultSet;
    }
    
    public function getById($id){
        $query=$this->db->query("SELECT * FROM $this->table WHERE id=$id");

        if($row = $query->fetch_object()) {
           $resultSet=$row;
        }
        
        return $resultSet;
    }
    
    public function getBy($column,$value){
        $query=$this->db->query("SELECT * FROM $this->table WHERE $column='$value'");

        while($row = $query->fetch_object()) {
           $resultSet[]=$row;
        }
        
        return $resultSet;
    }
    
    public function deleteById($id){
        $query=$this->db->query("DELETE FROM $this->table WHERE id=$id"); 
        return $query;
    }
    
    public function deleteBy($column,$value){
        $query=$this->db->query("DELETE FROM $this->table WHERE $column='$value'"); 
        return $query;
    }
    

    /*
     * Aqui podemos montarnos un monton de métodos que nos ayuden
     * a hacer operaciones con la base de datos de la entidad
     */
    
}
?>

Ahora modificaremos también modelo base para pasarle la conexión:

<?php
class ModeloBase extends EntidadBase{
    private $table;
    private $fluent;
    
    public function __construct($table, $adapter) {
        $this->table=(string) $table;
        parent::__construct($table, $adapter);
        
        $this->fluent=$this->getConetar()->startFluent();
    }
    
    public function fluent(){
        return $this->fluent;
    }
    
    public function ejecutarSql($query){
        $query=$this->db()->query($query);
        if($query==true){
            if($query->num_rows>1){
                while($row = $query->fetch_object()) {
                   $resultSet[]=$row;
                }
            }elseif($query->num_rows==1){
                if($row = $query->fetch_object()) {
                    $resultSet=$row;
                }
            }else{
                $resultSet=true;
            }
        }else{
            $resultSet=false;
        }
        
        return $resultSet;
    }
    
    //Aqui podemos montarnos metodos para los modelos de consulta
    
}
?>

Ahora nuestras entidades recibirán la conexión como parametro, de esta forma:

<?php
class Usuario extends EntidadBase{
    private $id;
    private $nombre;
    private $apellido;
    private $email;
    private $password;
    
    public function __construct($adapter) {
        $table="usuarios";
        parent::__construct($table, $adapter);
    }
    
    public function getId() {
        return $this->id;
    }

    public function setId($id) {
        $this->id = $id;
    }
    
    public function getNombre() {
        return $this->nombre;
    }

    public function setNombre($nombre) {
        $this->nombre = $nombre;
    }

    public function getApellido() {
        return $this->apellido;
    }

    public function setApellido($apellido) {
        $this->apellido = $apellido;
    }

    public function getEmail() {
        return $this->email;
    }

    public function setEmail($email) {
        $this->email = $email;
    }

    public function getPassword() {
        return $this->password;
    }

    public function setPassword($password) {
        $this->password = $password;
    }

    public function save(){
        $query="INSERT INTO usuarios (id,nombre,apellido,email,password)
                VALUES(NULL,
                       '".$this->nombre."',
                       '".$this->apellido."',
                       '".$this->email."',
                       '".$this->password."');";
        $save=$this->db()->query($query);
        //$this->db()->error;
        return $save;
    }

}
?>

He creado una nueva entidad Producto para ver como se usa:

<?php
class Producto extends EntidadBase{
    private $id;
    private $nombre;
    private $precio;
    private $marca;
    
    public function __construct($adapter) {
        $table="productos";
        parent::__construct($table,$adapter);
    }
    
    public function getId() {
        return $this->id;
    }

    public function setId($id) {
        $this->id = $id;
    }
    
    public function getNombre() {
        return $this->nombre;
    }

    public function setNombre($nombre) {
        $this->nombre = $nombre;
    }

    public function getPrecio() {
        return $this->precio;
    }

    public function setPrecio($precio) {
        $this->precio = $precio;
    }

    public function getMarca() {
        return $this->marca;
    }

    public function setMarca($marca) {
        $this->marca = $marca;
    }

    public function save(){
        $query="INSERT INTO productos (id,nombre,precio,marca)
                VALUES(NULL,
                       '".$this->nombre."',
                       '".$this->precio."',
                       '".$this->marca."');";
        $save=$this->db()->query($query);
        //$this->db()->error;
        return $save;
    }

}
?>

Además también tenemos que modificar nuestros modelos de consulta para que reciban el «adaptador» de la conexión:

<?php
class UsuariosModel extends ModeloBase{
    private $table;
    
    public function __construct($adapter){
        $this->table="usuarios";
        parent::__construct($this->table, $adapter);
    }
    
    //Metodos de consulta
    public function getUnUsuario(){
        $query="SELECT * FROM usuarios WHERE email='victor@victor.com'";
        $usuario=$this->ejecutarSql($query);
        return $usuario;
    }
}
?>

Ahora en el constuctor de cada uno de nuestros controladores tendremos que instanciar la conexión y utilizar la propiedad adapter para pasar la conexión a los modelos y entidades:

<?php
class UsuariosController extends ControladorBase{
    public $conectar;
	public $adapter;
	
    public function __construct() {
        parent::__construct();
		 
        $this->conectar=new Conectar();
        $this->adapter=$this->conectar->conexion();
    }
    
    public function index(){
        
        //Creamos el objeto usuario
        $usuario=new Usuario($this->adapter);
        
        //Conseguimos todos los usuarios
        $allusers=$usuario->getAll();

		//Producto
        $producto=new Producto($this->adapter);
		$allproducts=$producto->getAll();
       
        //Cargamos la vista index y le pasamos valores
        $this->view("index",array(
            "allusers"=>$allusers,
			"allproducts" => $allproducts,
            "Hola"    =>"Soy Víctor Robles"
        ));
    }
    
    public function crear(){
        if(isset($_POST["nombre"])){
            
            //Creamos un usuario
            $usuario=new Usuario($this->adapter);
            $usuario->setNombre($_POST["nombre"]);
            $usuario->setApellido($_POST["apellido"]);
            $usuario->setEmail($_POST["email"]);
            $usuario->setPassword(sha1($_POST["password"]));
            $save=$usuario->save();
        }
        $this->redirect("Usuarios", "index");
    }
    
    public function borrar(){
        if(isset($_GET["id"])){ 
            $id=(int)$_GET["id"];
            
            $usuario=new Usuario($this->adapter);
            $usuario->deleteById($id); 
        }
        $this->redirect();
    }
    
    
    public function hola(){
        $usuarios=new UsuariosModel($this->adapter);
        $usu=$usuarios->getUnUsuario();
        var_dump($usu);
    }

}
?>

A la vista le podemos añadir lo siguiente para que muestre el listado de productos:

 <?php if(isset($allproducts) && count($allproducts)>=1) {?>
		<div class="col-lg-7">
            <h3>Productos</h3>
            <hr/>
        </div>
		 <section class="col-lg-7 producto" style="height:400px;overflow-y:scroll;">
            <?php foreach($allproducts as $product) {?>
                <?php echo $product->id; ?> -
                <?php echo $product->nombre; ?> -
                <?php echo $product->precio; ?> -
                <?php echo $product->marca; ?>
                <hr/>
            <?php } ?>
        </section>
<?php } ?>

Y con esto ya tenemos el mini framework funcionando para empezar a trabajar mejor con el. Este ejemplo está muy bien, pero para crear tus proyectos reales te recomiendo que uses un framework estandar con una comunidad de detrás y que te permita hacer aplicaciones web más robustas como por ejemplo Symfony o Laravel.

Código fuente de este ejemplo
Base de datos del ejemplo

Vídeo tutorial paso por paso:

Puedes aprender mas sobre PHP, el MVC y los frameworks en mi curso gratuito de introducción a los framworks para PHP.

Víctor Robles WEB

Autor: Victor

Desarrollador web - Formador online - Blogger

Compartir este post

85 Comentarios

  1. Muchas gracias! es precisamente lo que estaba buscando. Gran trabajo!

    Responder
  2. Muy buen tutorial, gracias.
    Pero tengo una duda. Digamos que de una consulta obtengo información de dos tablas. Que entidad se ha de llamar/crear?
    Por ejemplo, tengo la tabla usuarios y la tabla direcciones. Se relacionan por una FK usuario_id presente en direcciones. Cuando realizo una consulta de ambas tablas y obtengo las diferentes columnas, ya no podré crear una entidad Usuario ya que recibo información también de la otra tabla. Que hacer en estos casos?

    Responder
    • En este ejemplo te puedes hacer un método que sea getUserDirections en la entidad de usuario por ejemplo y dentro de este se hacen las consultas o lo que sea. Lo que se hace en los ORM como Doctrine o Eloquent es establecer relaciones en este caso seria una relación OneToOne ya que se supone que hay una dirección por usuario.

      Responder
    • Hola, has podido adaptar el ejemplo de la manera que decias? Si es asi te agradeceria si lo compartes…saludos!

      Responder
    • disculpa pero creo que eso dependeria de como hagas el llamdo a la base de datos, si no me equivoco seria por un join in o algo asi (en mysql) ojo solo digo no estoy 100% seguro asi que suete en la investigacion

      Responder
  3. Gracias!! execelente turorial, muchas gracias por el tiempo tomado en publicarlo y enseñarnos el modo correcto de trabajar.

    Responder
  4. Estimado Victor, estoy totalmente de acuerdo con cada uno de los motivos por los cuales desarrollaste este ejemplo. Quiero compartir un punto que me pareció importante que lo nombraras, y es que el lenguaje no hace al programador, ni el programador al lenguaje, si no que el programador puede programar bien o mal dependiendo de su buena o mala decisión. Digo esto porque me ha tocado compartir, sobre todo con programadores de Python, que sacan en cara de PHP que se presta para muchos «caos y despropósitos» como dices tú en tu publicación. Pero así también Basic, C, Pascal, Python, C# y etc etc etc, pueden prestarse para caos y despropósitos en la medida que el programador no pueda ordenar una lógica.
    Si bien es cierto, hoy el mercado busca que los desarrollos sean orientados a objetos, esto no quiere decir que toda solución deba obedecer al paradigma orientado a objetos. Por lo tanto, estoy en total desacuerdo con aquellos que reclaman en contra de PHP por ser ocupado muchas veces en forma estructurada dentro de páginas html/php. Es decisión del programador el seguir un patrón MVC o nó y no es cosa del lenguaje pedirle al programador, por tema de buenas prácticas o lo que se quiera, seguir tal o cual modelo de desarrollo.
    Por último, si PHP fuera un lenguaje malo, no creo que proyectos como MySQL quisieran adoptarlo como futuro lenguaje para sus scripts (esto según una noticia que alcancé a leer su título pero no su contenido) ni tampoco creo que Facebook se haya desarrollado en PHP durante tanto tiempo (¿aún se desarrolla en PHP?).
    Espero haber sido defensor durante algunos instantes de un excelente lenguaje de programación que se ha «robado la película» durante tantos años.

    Responder
  5. Hola Victor,

    agradecerte el esfuerzo e interés en compartir un articulo tan bueno, muy buena ayuda de verdad.

    Un saludo 😉

    Responder
  6. Excelente explicación , preciso, documentado y bien explicado. Muchas gracias por tomarte tu tiempo a compartir algo valioso.

    Responder
  7. Voy revisando el código y si muy bueno, pero quisiera hacerte una pregunta sin ofender claro, en la clase ControladorBase() el constructor abstrae todos los modelo, considerando ya un proyecto lgo mas grande con el:

    foreach(glob(«model/*.php») as $file){
    require_once $file;
    }

    estarías llamando a todos los modelos pese a que alguno de ellos no se usarán, leí por ahí que en PHP5 se mejoró esta parte, con una autocarga de clases, es posible mejorar esta parte?

    Gracias por el proyecto es muy buen aporte.

    Responder
    • Claro que es posible mejorarlo todo, esto fue un ejemplo rápido, pero se puede hacer muchísimo mejor por supuesto.

      Responder
  8. Qué tal bro, me tengo igualito el proyecto que has explicado, el problemas es que al momento de correr el proyecto, me sale error en la siguiente línea de la EntidadBase.php, Fatal error: Call to a member function query() on a non-object in C:\…\Source Files\core\EntidadBase.php on line 24

    La verdad no sé a qué se debe ése error, si me pudieras explicar alguna solución te lo agradecería, o quizás se me está pasando alguna mala instrucción..

    Saludos.

    Responder
    • Tienes mal la base de datos, prueba con esta:

      CREATE DATABASE pruebas;
      use pruebas;

      CREATE TABLE IF NOT EXISTS `usuarios` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
      `password` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
      `nombre` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
      `apellido` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
      PRIMARY KEY (`id`)
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

      Responder
  9. Buenas tardes amigo

    La verdad muy bueno tu articulo, felicidades. Por otra parte quisiera que me ayudaras en algo, estoy intentando llamar dos modelos en un controlador pero me sale error:Fatal error: Call to a member function fetch_object() on boolean in C:\xampp\htdocs\mvc\core\EntidadBase.php on line 26.
    Yo en el controlador instanceo las dos clases del modelo y llamo al metodo getAll() para hacer un select de los dos; pero sale el error de arriba.
    Como haria para utilizar mas de un modelo en un controlador y poder realizar acciones con ellos: listar cursos y usuarios.

    Espero me puedas ayudar.

    Responder
    • Puede ser que no tengas bien configurada la base de datos y que por eso no te devuelve bien el resultset mysqli y por tanto fectch_object falle. Puedes ver como hago un ejemplo en el vídeo que hay en el artículo.

      Un saludo.

      Responder
  10. Buenas tardes.
    Buen Ejemplo y aporte, se puede llamar a procedimientos almacenados si esk creo uno en la base de datos pruebas, como x ejemplo autenticar()…como seria en tal caso??? o es mejor hacer mi metodo autenticar en la clase? en el paradigma de desarrollo de MVC se tiene como premisa usar solo las capas superiores a la capa base de datos, solo para almacenar la informacion…SERIA INTERESANTE SI SE PRESENTA UN EJEMPLO USANDO AJAX CARGANDO GRIDS.

    Responder
    • Debes hacerte una clase que se encargue del tema de la autenticación y ahí hacer un método para el login.

      Para usar Ajax seria tan simple como crear una acción en cualquier controlador y que devuelva algo ya sea en JSON o HTML y luego hacer una llamada Ajax desde la vista y que se imprima lo que nos devuelve el controlador en alguna caja de la vista.

      Un saludo 😉

      Responder
  11. Buen dia Victor… Wow en serio que soy nuevo en este tema de PHP, me encanta la programación y es cierto lo que dices que muchas veces se hacen despropósitos con los códigos. He viendo queriendo aprender todo sobre la programación WEB y cada vez que leo me confundo un poco más. Tengo una serie de dudas y te pregunto MVC… ok así como lo muestras lo entiendo… las vistas se cargan en un controlador frontal y si queremos cualquier vista ya creada solo se carga el controlador y se lanza la acción. Pregunta: tengo los PHP orientados a objetos la BD alojada en el servidor pero tengo mis html creados y solo necesito hacer una inserción desde un boton para guardar la información del contáctenos datos básicos del usuario que visita mi página… si hago un simple AJAX… mi arquitectura deja de ser MVC?…

    Responder
    • No, la arquitectura MVC está en el leguanje del lado del servidor, es decir, en tu código PHP. Las peticiones AJAX que puedas incluir en tus javascripts forman parte de la vista, no hay ningún tipo de problema, la petición AJAX la harás a una url que cargará una acción del controlador, esa acción pedirá lo que le haga falta al modelo y luego devolverá una vista (un trozo de html, un JSON o lo que quieras).

      Espero haberte aclarado la duda 😉

      Responder
  12. Hola Víctor,
    Excelente documento para los que queremos empezar a usar MVC en php.

    A ver si me podéis echar un cable con una cosa que no entiendo… He cogido este documento como base y he añadido un botón de «editar» los usuarios mediante AJAX.

    Más o menos. todo bien salvo el tema de los includes… en la carpeta core he añadido un archivo ajaxUsuario.php que hace uso de la clase Usuario. Debido a las herencias, se acaban usando el archivo config/database.php y la clase Conectar. Y ahí está mi problema, al hacer el require de esos dos archivos falla pero solo cuando lo hace ajax, da error de que no encuentra el archivo. En cambio el controlador para el index sí lo coge correctamente… la solución rápida que he cogido ha sido meter el contenido de esos dos archivos en clsEntidadBase.php ya que solo se usan en la clase EntidadBase… pero no es lo correcto

    ¿Alguna luz en este punto? sé que me explico fatal, aquí he subido mis modificaciones: http://mvc.msolla.extrasoft.es/mvc.rar

    Gracias. Y gracias Víctor otra vez por tu excelente aporte.

    Responder
    • añado que ahora ha empezado a darme muchos errores de este tipo:
      [02-Dec-2015 13:18:46 Europe/Madrid] PHP Fatal error: Class ‘404Controller’ not found in /home/msolla/public_html/mvc/core/ControladorFrontal.func.php on line 13

      Responder
      • Ahí es porque no te está cargando el controlador, tienes que ponerle el prefijo «cls» que has usado en tus cambios al código para que lo pueda cargar a la hora de crear el objeto.

        Espero haberte ayudado 😉

        Responder
    • En el AJAX tu petición debe ser a una URL no a una ruta de un fichero(el htaccess está montado para cargar controladores y acciones no ficheros xD), y cuando digo URL me refiero a una acción de un controlador, no debes hacer los métodos que has hecho para que te devuelvan un JSON dentro del core, porque esos métodos tienen que ser ACCIONES de un CONTROLADOR. Y luego desde ajax llamas a la url del tipo «index.php?controller=Usuarios&action=detalle».

      Lo unico que ficheros que tienes que tocar para contruir tu aplicación son los modelos, las vistas y los controlados, el resto de ficheros son para añadir funcionalidades al core, que en tu caso no es necesario.

      Espero que puedas solucionarlo 😉

      Responder
      • Hola,
        te contesto solo aquí para no llenarte todo de comentarios: perfecto. Muchas gracias.

        solo me queda pegarme con el .htaccess para hacer que las url salgan siempre «bonitas», pero eso ya es otra guerra

        Responder
        • De nada para eso estamos 😉

          Tendrás que usar las rewrite rules de apache en el htaccess y usar expresiones regulares básicas, en internet tienes información de como hacerlo.

          Un saludo 😉

          Responder
  13. muy buen aporte pero tengo problemas para recuperar los datos de un solo registro para poder actualizarlos en la misma vista, si podrias ayudarme

    Responder
    • Si te he entendido bien, lo que tienes que hacer es crearte una nueva acción en un controlador y tener también un método en un modelo que haga una consulta de un solo registro en función del id del registro te haga la consulta y devuelves el resultset, luego desde la acción llamas a ese método y le pasas los datos a una nueva vista que hayas creado, y accedes por la url a esa acción de ese controlador.

      Espero haberte ayudado, si tienes más dudas puedes ver este vídeo donde hago este ejemplo paso a paso:
      https://www.youtube.com/watch?v=U5lP8aOjKfw

      Saludos 😉

      Responder
  14. Buenas, quería hacerte una pregunta pero no se si podrás ayudarme, la cosa es que soy un novato en esto de programar orientado a objetos pero con tu ejemplo me puesto a montar un chat, entonces tengo un problema ya que estoy utilizando AJAX y cuando un usuario introduce un mensaje primero guardo el mensaje utilizando la funcion save de la clase Mensaje pero luego creo un objeto MensajeModelo para volver a listar los últimos mensajes del chat y al crear el objeto MensajeModelo me peta, creo que es por que no se puede tener dos objetos con conexión a la base de datos a la vez, es eso cierto?

    No se si habras entendido algo de lo que he puesto ya que tengo la desgracia de explicarme como un libro en llamas.

    Responder
    • Debes tener solo una conexión a la base de datos y pasarsela a los diferentes objetos para que la utilicen.

      Saludos 😉

      Responder
      • pero tal como lo tienes tu montado la conexión se crea en el constructor de EntidadBase de tal forma que si necesitas crear dos objetos solo funcionara la conexión a la base de datos en el primero, para que pudiera funcionar en los dos habría que hacer aparte la conexión no?

        Responder
        • En principio debería funcionar como esta montado, siempre puedes hacer una conexión aparte y funcionará.

          Aún así no se que problema puedes estar teniendo ya que no veo el error ni el código.

          Espero que puedas arreglarlo 😉

          Responder
          • Problema al crear dos objetos en la clase usuarioController en el metodo index, si necesitas crear dos objetos solo funciona la conexión a la base de datos para el primero, alguien sabe como solucionar esto?

          • El problema es que se hacen dos instancias a la conexión a la base de datos, esto se soluciona creando un adaptador para la conexión a la base de datos, y luego pasandole ese adaptador a las clases que necesiten usar la base de datos, de esta forma solo instanciamos una vez el objeto de la BBDD y lo reutilizamos. Busca información acerca del patrón de diseño adapter en PHP.

            Saludos 😉

  15. Veo que aquí se debate el uso de MySQLi y PDO, y en este proyecto se usa MySQLi pero veo declaraciones de PDO en el método fluent, ¿estas se están utilizando para este proyecto o es algo que se usará para una futura característica en caso de expandir este proyecto y puedo eliminarlos sin problema?

    Responder
    • Usamos mysqli para las queries que hacemos desde php y fluent por su parte usa PDO, es algo que no influye. Fluent no se usa en este proyecto, es algo que se usaría si se quisiera para hacer consultas con este query builder, si tu no lo usas en tus modelos para hacer nada lo puedes eliminar si quieres.

      Saludos 😉

      Responder
  16. Hola Víctor…. quiero probar el código pero no encuentro el sql ayuda

    Responder
    • Aquí tienes el código de la base de datos:

      CREATE DATABASE pruebas;
      use pruebas;

      CREATE TABLE IF NOT EXISTS `usuarios` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
      `password` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
      `nombre` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
      `apellido` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
      PRIMARY KEY (`id`)
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

      Responder
  17. muchas gracias por tu labor de enseñar al resto de gente ávida de conocimientos,
    quizá si le es posible poner el ejemplo de como realizar una actualización, que es la parte que no me sale ya que el resto de la aplicación me funciono de maravillas.

    lamentablemente estoy aprendiendo y tu ejemplo me ha servido de mucho

    Gracias

    Responder
    • LUCHANDO Y APRENDIENDO SIMPLEMENTE FUE MAL ECHO EL UPDATE Y TODO OK
      GRACIAS POR TODO

      Responder
  18. Muy buen trabajo, sobre todo muy claro el funcionamiento del MVC. Felicidades !!

    Responder
  19. Te felicito gracias por compartir tus conocimientos y capacidades, soy alumno en tu curso de UDEMY

    Responder
  20. Excelente tutorial. Gracias por compartir.
    Una duda: como puedo mostrar dentro del formulario los datos ya existentes en la bbdd??
    por ejemplo: en un despegable el contenido de una tabla usando este framework.
    He intentado haciendo un foreach() del modelo Usuario->getUsuario()…

    Responder
  21. Este tutorial me agrada debido a la arquitectura de los módulos, pero mi duda es cómo puedo modificar este tutorial para incluir múltiples vistas ya que aquí solo se maneja una que es la de indexView.php y esta clase de aplicaciones suelen tener más de una vista.

    ¡Saludos!

    Responder
  22. Buen dia, alguien que sepa como mostrar el detalle de un listado de registros en la misma pagina con php mysql, agradeceria de su ayuda

    Responder
  23. Ayuda … en mi caso tengo sql y mi aplicacion se conectara a 3 bases de datos ayuda…

    Responder
    • Usa un Framework ya echo jaja, o en el fichero de conexión añadir más conexiones con mysqli y devuélvelas en el método de esta forma puedes tener varias conexiones abiertas disponible para ser utilizadas. Saludos 😉

      Responder
      • Estimado Victor, cuando dices archivo conexion es el archivo conectar.php? o el database.php.

        Y cuando dices retornarlas en el metodo me imagino que es el conectar(), o debo crear otro metodo como por ejemplo conectar_base1(), conectar_base2() y con getConetar() seria lo mismo .

        De antemano agradeceré tu ayuda.

        Responder
        • Es el conectar.php y si es el método conectar.

          Saludos 🙂

          Responder
  24. Hola Victor, estoy intentando poner en funcionamiento tu aplicación. Tengo un mismo error que otro usuario, y al leer tu respuesta hice la la base de datos pruebas y la tabla usuarios. Sin embargo, me sale estos mensajes.

    Notice: Undefined index: localhost in C:\xampp\htdocs\mvc-master\core\Conectar.php on line 9
    Notice: Undefined index: root in C:\xampp\htdocs\mvc-master\core\Conectar.php on line 10
    Notice: Undefined index: in C:\xampp\htdocs\mvc-master\core\Conectar.php on line 11
    Notice: Undefined index: pruebas in C:\xampp\htdocs\mvc-master\core\Conectar.php on line 12
    Fatal error: Call to a member function fetch_object() on a non-object in C:\xampp\htdocs\mvc-master\core\EntidadBase.php on line 26

    Disculpas si es un error tonto :/

    Responder
    • No tienes bien configurado el fichero de conexión a la base de datos database.php, pon bien las credenciales de tu MySQL en este archivo y luego incluyelo en Conectar.PHP

      Espero haberte ayudado. Saludos 🙂

      Responder
      • Hola Victor, gracias por contestar. Ya lo resolví volviendo a definir los ficheros y las clases con patrón Singleton. Muchas gracias por tu aporte y me ha servido un montón.
        Tengo unas críticas constructivas acerca de tus videos, en especial tu opinión acerca del desarrollo por estilo de procedimientos en PHP con los famosos spaghetti de códigos. Pero bueno, no puedo poner las manos en el fuegos por otros desarrolladores defendiéndolos – jeje.
        Me pareces un excelente profesional y docente.
        Veré cuando pueda contratarte algún curso.

        Saludos desde Argentina!

        Responder
  25. hola victor tengo el siguente problema ;

    public function index(){

    //Creamos el objeto usuario
    $usuario=new Usuario();
    $rol=new Rol(); /************** cuando creo el objeto Rol sale Error … ***************/

    //Conseguimos todos los usuarios
    $allusers=$usuario->getAll();
    $allRol=$Rol->ListarRol(‘5’); /***************** sale error ********************* ***/

    de que manera puedo consumir otras modelos ya creados , tengo modelo Empleado, Roles,Permisos.. en este caso solo quiero mostrar el rol del usuario … gracias ,

    Responder
    • He añadido una nueva sección al tutorial «Correcciones y mejoras (2016)» revisalá y prueba con eso.

      Saludos 🙂

      Responder
  26. Excelente Trabajo pero creo que ya es tiempo de actualizar en estos dias ya es obsoleto y un antipatron instanciar objetos dentro de un clase pues se crean dependencias muy fuertes entre clases seria bueno actualizar tu ejemplo con inyeccion de dependencias y algunos otros ptrones de diseño ..una ves mas excelente trabajo

    Responder
    • Gracias Enrique, claro, seria bueno actualizarlo para usar inyección de dependencias y mejores practicas, pero bueno para ser un ejemplo rápido yo creo que es suficiente de momento. Saludos 😀

      Responder
  27. Hola Víctor

    Me da el siguiente error al descargarme tu práctica y base de datos. Estoy dándole vueltas pero no doy con lo que es

    Warning: Missing argument 1 for Usuario::__construct(), called in /Applications/XAMPP/xamppfiles/htdocs/MVC/controller/UsuariosController.php on line 37 and defined in /Applications/XAMPP/xamppfiles/htdocs/MVC/model/Usuario.php on line 9

    Fatal error: Call to a member function query() on a non-object in /Applications/XAMPP/xamppfiles/htdocs/MVC/model/Usuario.php on line 61

    un saludo
    Carlos

    Responder
    • Seguramente no le estarás pasando la conexión de la base de datos al modelo. Saludos 😉

      Responder
    • Hola Carlos, Yo tambien tengo el mismo problema, lograste solucionarlo ? te agradeceria mucho el que me lo comentaras

      Responder
      • gracias Victor por el ejemplo, encontre el error, era en el constructor en la definición de los parametros 😀 , excelente ejemplo sirve de mucho en especial a quienes estamos iniciando en MVC con PHP 🙂

        Responder
        • Hola Clara, yo estoy con el mismo problema.. ¿cómo lo solucionaste?
          please

          Responder
          • en el controlador a la hora de instanciar el modelo debes incluir el adaptador
            ejemplo:

            $usuario=new Usuario($this->adapter);

          • Buenas.. Como solucionaron el error.?. Se los agradecería enormemente..

          • Al momento de instanciar en las funciones crear y borrar debe agregar el adaptador. funciona perfectamente.

            $usuario=new Usuario($this->adapter);

          • muchas gracias Juan Esteban, me salvaste la vida

    • Al momento de instanciar en las funciones crear y borrar debe agregar el adaptador. funciona perfectamente.

      Responder
  28. Tengo una pregunta la verdad soy nuevo en esto.
    Me gustaría saber en que parte van los archivos css, js ?

    Responder
  29. Victor enormes gracias por el artículo y el video. Mañana mismo me pongo las pilas. Un abrazo

    Responder
  30. Estimado estoy implementando paso a paso tu desarrollo MVC y he aprendido como hacerlo, lo que no me resulta es cargar los datos para actualizar, agradecería me ayudaras es un trabajo para la universidad.

    Gracias.

    Responder
  31. Hola que tal.. todo esta muy bien, a excepción de un pequeño error al llamar al action «hola», me lanza el siguiente error. Alguna idea de porque pasa eso?

    http://localhost:89/mvcaj/index.php?controller=usuarios&action=hola

    Fatal error: Call to a member function startFluent() on a non-object in C:\xampp\htdocs\mvcaj\core\ModeloBase.php on line 10

    Responder
    • Hola. por favor me puede decir si logro corregir el error, yo también tengo el mismo problema y no he encontrado la solución. Gracias.

      Responder
      • Debes comentar la linea #10 de la clase ModeloBase

        Responder
  32. Hola, muchas gracias por el tutorial. Hay algo que no me cierra y quería consultarte. ¿En qué momento se llenan los modelos con el resultado de getAll()? Porque en definitiva lo que está haciendo la arquitectura es devolviendo el array de la base de datos, pero en ningún momento se está llenando el modelo para consumirlo desde la vista con un método (por ejemplo) getDescripcion(). Espero haber sido claro en mi duda.
    Muchas gracias nuevamente.

    Responder
  33. buenas tarde Victor

    Que pena, apenas estoy empezando a conocer, que puede ser este error. gracias

    Warning: Missing argument 1 for Usuario::__construct(), called in C:\xampp\htdocs\mvc\controller\UsuariosController.php on line 37 and defined in C:\xampp\htdocs\mvc\model\Usuario.php on line 9

    Notice: Undefined variable: adapter in C:\xampp\htdocs\mvc\model\Usuario.php on line 11

    Fatal error: Call to a member function query() on null in C:\xampp\htdocs\mvc\model\Usuario.php on line 61

    Responder
    • Ya lo solucione, muchas gracias.

      Responder
    • busca el archivo «UsuariosController.php» dentro de la carpeta controller y lo reemplazar por este codigo

      conectar=new Conectar();
      $this->adapter=$this->conectar->conexion();
      }

      public function index(){

      //Creamos el objeto usuario
      $usuario=new Usuario($this->adapter);

      //Conseguimos todos los usuarios
      $allusers=$usuario->getAll();

      //Producto
      $producto=new Producto($this->adapter);
      $allproducts=$producto->getAll();

      //Cargamos la vista index y le pasamos valores
      $this->view(«index»,array(
      «allusers»=>$allusers,
      «allproducts» => $allproducts,
      «Hola» =>»Soy Víctor Robles»
      ));
      }

      public function crear(){
      if(isset($_POST[«nombre»])){

      //Creamos un usuario
      $usuario=new Usuario($this->adapter); // se debe agregar dentro del parentesis ($this->adapter)
      $usuario->setNombre($_POST[«nombre»]);
      $usuario->setApellido($_POST[«apellido»]);
      $usuario->setEmail($_POST[«email»]);
      $usuario->setPassword(sha1($_POST[«password»]));
      $save=$usuario->save();
      }
      $this->redirect(«Usuarios», «index»);
      }

      public function borrar(){
      if(isset($_GET[«id»])){
      $id=(int)$_GET[«id»];

      $usuario=new Usuario($this->adapter); // se debe agregar dentro del parentesis ($this->adapter)
      $usuario->deleteById($id);
      }
      $this->redirect();
      }

      public function hola(){
      $usuarios=new UsuariosModel($this->adapter);
      $usu=$usuarios->getUnUsuario();
      var_dump($usu);
      }

      }
      ?>

      Responder
  34. Buenas noches, que pena la pregunta, me puedes explicar brevemente cómo puedo configurar un menú de bootstrap con esta arquitectura? gracias.

    Responder
  35. Hola Victor, me podrias orientar como seria mi model/usuario si esta tiene un forenkey de model/personal que seria personal_id .

    Responder
  36. Hola, traté de seguir el tutorial y me carga los datos de mysql perfectamente pero cuando le doy crear usurio llama al controlador pero no guarda los datos a myslq ni me redirecciona a ningún lado, alguien sabe porqué?

    Responder
  37. Muchas gracias Victor por tú tiempo y el tener la deferencia de compartir tus enseñanzas con nosotros. Espero que toda la ayuda prestada por tú parte se te devuelva algún día.

    Responder
  38. Estoy empeñado en cambiar mi forma de programar que llevo hasta el momento en PHP siendo novato, creo que este tutorial tuyo sera de gran ayuda. Te agradezco.

    Responder
  39. buen dia, te agradezco por este buen aporte que haces a las personas como yo que empezamos con la programación, tengo una duda como implementarias el uso de session si le agregas un login, antemano gracias

    Responder
  40. Solo puedo decir que Gran Trabajo!!!!….

    Responder
  41. Felicitaciones Victor, gracias por compartir tus conocimientos. Coincido contigo en cuanto a que no hay una «forma correcta» de hacer las cosas. He aprendido que solo hay una verdad absoluta y menos en programacion. A mi me parece que tu codigo esta bastante bien.

    Responder
  42. Muy buen post, solo una cosa. En la corrección de 2016, si no hay usuarios en la tabla te muestra un error fatal.
    Lo he arreglado simplemente declarando $resulSet como array() antes del While (Línea 29 de EntidadBase.php)

    Responder

Poner un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *