MVC com PHP e procedimentos armazenados en MySQL

MVC com PHP e procedimentos armazenados en MySQL

Ao trabalhar em uma aplicação que combina bancos de dados MySQL e PHP, é normal pensar que tudo deve ser feito a partir do código. No entanto, não custa nada separar a lógica de programação da lógica de dados.

É por isso que os Procedimentos Armazenados (Stored Procedures) poderiam ser utilizados para evitar a sobrecarga do código e deixar toda a lógica dos dados para o SGBD (Sistema Gerenciador de Banco de Dados), sempre cuidando para segmentar corretamente o código. Para fazer isso, você pode usar o padrão MVC (Model - View - Controller) em PHP.

Procedimentos Armazenados


“A stored procedure is a prepared SQL code that you can save, so the code can be reused over and over again” (Fonte: www.w3schools.com/sql/sql_stored_procedures.asp).

Como lá disse, um procedimento armazenado permite que o código SQL (consultas) seja salvo no SGBD, de forma que possa ser reutilizado com mais eficiência.

MVC


MVC (Model - View - Controller) é um padrão de design de software comumente usado para implementar interfaces de usuário, dados e lógica de controle. Ele enfatiza uma separação entre a lógica de negócios e sua visualização (Fonte: www.developer.mozilla.org/es/docs/Glossary/MVC).

Tecnologia que usaremos

  • Visual Studio Code.
  • MySQL (versión 8.0 de preferencia).
  • MySQL Workbench.
  • WampServer.
  • Bootstrap.


O funcionamento básico da aplicação em relação ao código é realizado sob a estrutura do padrão MVC com a linguagem PHP, que possui comunicação com cada uma de suas camadas.

A camada de modelo (M) terá comunicação bidirecional com os procedimentos armazenados e o banco de dados MySQL, da mesma forma que o roteador (roteador) servirá de ponte entre o controller e a view.

Vejamos a figura abaixo:

MVC (em espanhol)

Para iniciar o pequeno projeto, é necessário ter criado um ambiente de servidor local com a ferramenta WampServer devidamente instalada e configurada. Se for necessária ajuda adicional para configurá-lo, assista ao vídeo a seguir.

Após configurar o servidor local, é necessário baixar e instalar a ferramenta MySQL Workbench para criar o espaço de banco de dados a ser utilizado no MySQL Server.

Dados para configurar a conexão do servidor local no MySQL Workbench:

  • Connection Name: (de preferência).
  • Hostname: 127.0.0.1.
  • Port: 3306.
  • Username: root.
  • Password: ****** (vazio por padrão).


Criação do banco de dados e tabelas


Em seguida, deve-se acessar a conexão configurada acima e criar o banco de dados e as tabelas necessárias, utilizando scripts com a ferramenta MySQL Workbench.

Para criar o banco de dados devemos executar o seguinte script:

CREATE SCHEMA notas DEFAULT CHARACTER SET utf8;

USE notas;

DROP TABLE if exists notas;

CREATE TABLE notas(
idnota INT not null AUTO_INCREMENT,
autor varchar(20) not null,
titulo varchar(30) not null,
descripcion varchar(150) not null,
fecha_hora TIMESTAMP null DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY(idnota),
UNIQUE INDEX index_titulo_unico (titulo ASC),
INDEX index_autor (autor ASC)
)
engine = InnoDB
default character set=utf8;


Execute o seguinte script para criar os procedimentos armazenados:

USE notas;
drop procedure if exists SP_Nueva_Nota;
drop procedure if exists SP_Obtener_Notas;
drop procedure if exists SP_Obtener_Nota_Por_Id;
drop procedure if exists SP_Modificar_Nota;
drop procedure if exists SP_Eliminar_Nota_Por_Id;

#definimos delimitador
delimiter //

#store procedure para crear nueva nota
create procedure SP_Nueva_Nota(
in prm_autor varchar(20),
    in prm_titulo varchar(30),
    in prm_descripcion varchar(150)
)
begin
insert into notas(autor,titulo,descripcion)
    values(prm_autor,prm_titulo,prm_descripcion);
end//

#store procedure para obtener todas las notas
create procedure SP_Obtener_Notas()
begin
select * from notas;
end//

#store procedure para obtener nota por id
create procedure SP_Obtener_Nota_Por_Id(in prm_idnota int)
begin
select * from notas
    where idnota=prm_idnota;
end//

#store procedure para procedimiento para modificar notas
create procedure SP_Modificar_Nota(
in prm_idnota int,
in prm_autor varchar(20),
    in prm_titulo varchar(30),
    in prm_descripcion varchar(150)
)
begin
update notas
    set autor=prm_autor, titulo=prm_titulo, descripcion=prm_descripcion
    where idnota=prm_idnota;
end//

#store procedure para eliminar nota por id
create procedure SP_Eliminar_Nota_Por_Id(in prm_idnota int)
begin
delete from notas
    where idnota=prm_idnota;
end//


Criação da estrutura de pastas


Na pasta que foi criada para o projeto no servidor local, use o Visual Studio Code para criar a seguinte estrutura de pastas:

Criar arquivos de configuração path e banco de dados


Código de Config.php:

<?php

    //definimos constantes de conexión
    define('LOCAL_PATH','http://localhost:{puerto}/{nombre_carpeta_app}/');
    define('DB_HOST','127.0.0.1');
    define('DB_PORT','3306');
    define('DB_DATABASE','notas');
    define('DB_USER','root');
    define('DB_PASS','');

    define('CHARSET','utf8');

?>

Nota: Substituir {porto} e {nome_pastaa_app}.

Código de Database.php:

<?php

    require_once __DIR__ .'/../model/Config.php';

    class Database{

        private function conectar(){

            $basedatos = new PDO('mysql:host='.DB_HOST.':'.DB_PORT.';'.'dbname='.DB_DATABASE.';charset='.CHARSET.'',DB_USER,DB_PASS);
            return $basedatos;

        }

        //Función para ejecutar procedimiento almacenado general
        public function EjecutarSPConParams($consulta, $parametros){
         
            try{
                $conexion = $this->conectar();
                $conexion->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                $sentencia = $conexion->prepare($consulta);
                $sentencia->execute($parametros);

                $respuesta = $sentencia->fetchAll(PDO::FETCH_ASSOC);
                $sentencia->closeCursor();

                //limpiamos
                $conexion=null;
                $sentencia=null;

                return $respuesta;

            }catch(PDOException $exception) {
                return $exception;
            }
        }

        //Función para ejecutar procedimiento sin parámetros
        public function EjecutarSPSinParams($consulta){
         
            try{
                $conexion = $this->conectar();
                $conexion->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                $sentencia = $conexion->prepare($consulta);
                $sentencia->execute();

                $respuesta = $sentencia->fetchAll(PDO::FETCH_ASSOC);
                $sentencia->closeCursor();

                //limpiamos
                $conexion=null;
                $sentencia=null;

                return $respuesta;

            }catch(PDOException $exception) {
                return $exception;
            }
        }

    }

?>

Criar modelo, controlador e roteador


É assim que o modelo NotaModel.php é criado:

<?php

    require_once 'Database.php';

    class NotaModel{

        private $conexion;

        public function __construct(){
            $this->conexion = new Database();
        }

        public function ObtenerNotasModel(){
            $consulta = "CALL SP_Obtener_Notas();";
            $respuesta = $this->conexion->EjecutarSPSinParams($consulta);
            return $respuesta;
        }

        public function ObtenerNotaPorIdModel($idnota){
            $consulta = "CALL SP_Obtener_Nota_Por_Id(:prm_idnota);";
            $parametros = array(
                "prm_idnota"=>$idnota
            );

            $respuesta = $this->conexion->EjecutarSPConParams($consulta, $parametros);
            return $respuesta;

        }

        public function NuevaNotaModel($datos){
            $consulta = "CALL SP_Nueva_Nota(:prm_autor, :prm_titulo, :prm_descripcion);";
            $parametros = array(
                "prm_autor"=>$datos['autor'],
                "prm_titulo"=>$datos['titulo'],
                "prm_descripcion"=>$datos['descripcion']
            );

            $respuesta = $this->conexion->EjecutarSPConParams($consulta, $parametros);
            return $respuesta;

        }

        public function ModificarNotaModel($datos){
            $consulta = "CALL SP_Modificar_Nota(:prm_idnota, :prm_autor, :prm_titulo, :prm_descripcion);";
            $parametros = array(
                "prm_idnota"=>$datos['idnota'],
                "prm_autor"=>$datos['autor'],
                "prm_titulo"=>$datos['titulo'],
                "prm_descripcion"=>$datos['descripcion']
            );

            $respuesta = $this->conexion->EjecutarSPConParams($consulta, $parametros);
            return $respuesta;

        }

        public function EliminarNotaModel($idnota){
            $consulta = "CALL SP_Eliminar_Nota_Por_Id(:prm_idnota);";
            $parametros = array(
                "prm_idnota"=>$idnota
            );

            $respuesta = $this->conexion->EjecutarSPConParams($consulta, $parametros);
            return $respuesta;

        }

    }

?>


Criação do controlador NotaController.php:

<?php

    require_once __DIR__ . '/../model/NotaModel.php';

    class NotaController{

        private $notaModel;
        private $respuesta;

        public function __construct(){
            $this->notaModel = new NotaModel();
        }

        public function ObtenerNotasController()
        {
            try {
                $resultados = $this->notaModel->ObtenerNotasModel();
                $this->respuesta = array(
                    "state" => true,
                    "notas" => $resultados
                );
            } catch (PDOException $pdoEx) {
                $this->respuesta = array(
                    "state" => false,
                    "mensaje" => $pdoEx->getMessage()
                );
            }

            return $this->respuesta;
        }

        public function NuevaNotaController($datos)
        {
            try {
                $resultados = $this->notaModel->NuevaNotaModel($datos);
                $this->respuesta = array(
                    "state" => true,
                    "resultado" => $resultados
                );
            } catch (PDOException $pdoEx) {
                $this->respuesta = array(
                    "state" => false,
                    "mensaje" => $pdoEx->getMessage()
                );
            }

            return $this->respuesta;
        }


    }

?>

É assim que o arquivo do roteador NotesRouter.php é criado:

<?php

    require_once __DIR__ . '/../controller/NotaController.php';

    $accion = $_POST['accion'];

    switch ($accion) {
        case 'nuevo':
            echo NuevaNota();       
            break;
        default:
            break;
    }


    function NuevaNota()
    {
        $notaController = new NotaController();

        if(isset($_POST['autor'])
            && isset($_POST['titulo'])
            && isset($_POST['descripcion'])){

            //creamos arreglo de datos
            $datos = array(
                "autor" => $_POST['autor'],
                "titulo" => $_POST['titulo'],
                "descripcion" => $_POST['descripcion']
            );

            $respuesta = $notaController->NuevaNotaController($datos);
         
            if($respuesta['state']==true){
                header("Location: ../index.php");
            }         

        }
    }

?>


Criação de visualizações


É assim que o NotaView.php é criado:

<?php
    require_once 'controller/NotaController.php';
    $notaController = new NotaController();

    $notas = $notaController->ObtenerNotasController();
?>

<div class="row">
    <div class="col-md-3 p-3 m-1 bg-info">
        <h4>Edición de Notas</h4>
        <form method="POST" action="router/NotasRouter.php">
            <div class="mb-3">
                <input type="text" name="accion" value="nuevo" hidden>
                <label for="in-autor" class="form-label">Autor</label>
                <input type="text" class="form-control" id="in-autor" name="autor" placeholder="Nombre de autor..." required>
            </div>
            <div class="mb-3">
                <label for="in-titulo" class="form-label">Título</label>
                <input type="text" class="form-control" id="in-titulo" name="titulo" placeholder="Título de nota..." required>
            </div>
            <div class="mb-3">
                <label for="tarea-descripcion">Descripción</label>
                <textarea class="form-control" placeholder="Descripción de nota..." id="tarea-descripcion" name="descripcion"></textarea>
            </div>
            <button type="submit" class="btn btn-primary">Guardar</button>
        </form>
    </div>

    <div class="col-md-8 p-3 m-1">
        <h4>Notas Guardadas</h4>
        <table class="table table-hover">
            <thead>
                <tr>
                    <th scope="col">ID</th>
                    <th scope="col">AUTOR</th>
                    <th scope="col">TITULO</th>
                    <th scope="col">DESCRIPCION</th>
                    <th scope="col">FECHA</th>
                </tr>
            </thead>
            <tbody>
                <?php if($notas['state']==1){
                    foreach ($notas['notas'] as $nota) {?>
                        <tr>
                            <th scope="row"><?php echo $nota['idnota'] ?></th>
                            <td><?php echo $nota['autor'] ?></td>
                            <td><?php echo $nota['titulo'] ?></td>
                            <td><?php echo $nota['descripcion'] ?></td>
                            <td><?php echo $nota['fecha_hora'] ?></td>
                        </tr>
                <?php } } ?>
            </tbody>
        </table>
    </div>

</div>

Criar index.php e arquivo .htaccess


É assim que o index.php é criado:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Notas</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css">
<body>
    <div class="container-fluid">
        <div class="row">
            <nav class="navbar bg-dark p-2" data-bs-theme="dark">
                <h1>Notas (MVC-PHP-MYSQL)</h1>
            </nav>
        </div>

        <?php include 'view/NotaView.php' ?>

    </div>

    <!-- Bootstrap Popper-->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js" integrity="sha384-oBqDVmMz9ATKxIep9tiCxS/Z9fNfEXiDAYTujMAeBAsjFuCZSmKbSSUnQlmh/jp3" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.min.js" integrity="sha384-mQ93GR66B00ZXjt0YO5KlohRA5SY2XofN4zfuZxLkoj1gXtW8ANNCe9d5Y3eG5eD" crossorigin="anonymous"></script>
</body>
</html>


Criar .thaccess


Para dar alguma segurança ao aplicativo, você pode usar o arquivo .htaccess e escrever as seguintes linhas de código:

#página de inicio
DirectoryIndex index.php

#acceso restrictivo a directorio principal
Options All -Indexes

Para testar o aplicativo, acesse-o pelo navegador com a URL configurada anteriormente no arquivo Config.php.


Conclusão


Talvez uma das razões mais significativas para o uso do MVC seja a fácil organização do código (adaptável a diferentes frameworks atuais), sua escalabilidade potencial e a facilidade de trabalhar em equipe com outros desenvolvedores.

Em relação aos procedimentos armazenados no MySQL, o desempenho é melhorado com respostas rápidas e eficientes na aplicação, flexível na medida em que os dados podem ser acessados ​​por diferentes aplicações e linguagens, não deixando de lado a modularização do código SQL.

Espero que este material tenha sido útil. Até logo.

Referências

Documentation. (n.d.). PHP. Retrieved January 27, 2023, from https://www.php.net/docs.php

Get started with Bootstrap · Bootstrap v5.3. (n.d.). Bootstrap. Retrieved January 27, 2023, from https://getbootstrap.com/docs/5.3/

Los procedimientos almacenados en MySQL y sus ventajas. (2017, November 23). VIU. Retrieved January 27, 2023, from https://www.universidadviu.com/es/actualidad/nuestros-expertos/los-procedimientos-almacenados-en-mysql-y-sus-ventajas

MVC - Glosario de MDN Web Docs: Definiciones de términos relacionados con la Web | MDN. (2022, December 5). MDN Web Docs. Retrieved January 27, 2023, from http://www.developer.mozilla.org/es/docs/Glossary/MVC

MySQL :: MySQL 8.0 Reference Manual. (n.d.). MySQL :: MySQL 8.0 Reference Manual. Retrieved January 27, 2023, from https://dev.mysql.com/doc/refman/8.0/en/

Rodríguez, F. (2022, July 13). Qué es la arquitectura MVC. KeepCoding. Retrieved January 27, 2023, from https://keepcoding.io/blog/que-es-la-arquitectura-mvc/

SQL Stored Procedures for SQL Server. (n.d.). W3Schools. Retrieved January 27, 2023, from http://www.w3schools.com/sql/sql_stored_procedures.asp

Stored Procedure Advantages | SQL Server Stored Procedure Basics. (2002, February 8). InformIT. Retrieved January 27, 2023, from https://www.informit.com/articles/article.aspx?p=25288&seqNum=3

💡
As opiniões e comentários expressos neste artigo são de propriedade exclusiva de seu autor e não representam necessariamente o ponto de vista da Revelo.

A Revelo Content Network acolhe todas as raças, etnias, nacionalidades, credos, gêneros, orientações, pontos de vista e ideologias, desde que promovam diversidade, equidade, inclusão e crescimento na carreira dos profissionais de tecnologia.