Tecnologías del lado del servidor

PHP - Bases de Datos

Programación en Ambiente Web (11086) - 2020

PHP - Caracteristicas

  • Lenguaje del lado del servidor

  • Objetivo: Desarrollo Web

  • HTML y PHP (Tags <?php y ?>)

  • Multiplataforma

  • Libre y Gratuito

  • Soportado por muchos Web Servers

  • Ultima versión: 7

PHP - Repaso

<?php

    echo "Hola Mundo\n";

Test de codigo

$ php7 holaMundo.php

holaMundo.php

PHP - Repaso I

$varInt = 12345;
$varString = "soy un string";
$varArray = [1, 2, 3, 4, 5];
$varArray2 = array(1, 2, 3, 4, 5);
$varAssocArray = [
    "primer_clave" => "primer valor",
    "segunda_clave" => 123,
    5 => "otro valor mas"
];
$varBoolean = false; # true

Variables y Tipos

PHP - Repaso II

echo $varInt;
print_r($varArray);
var_dump($varAssocArray);

Mostrar variables

PHP - Repaso III

if (<expresion>) {
    // Acciones si expresion es true
} else {
    // Acciones si expresion es false
}

Estructuras de Control - Condicional

PHP - Repaso IV

foreach ($coleccion as $clave => $item) {
    // Hacer algo por cada par $clave e $item
}

for ($i = $limiteInferior; $i <= $limiteSuperior; $i++) {
    // Ejecutar n veces; n = $limiteSuperior - $limiteInferior
}

while (expresion) {
    // mientras expresion sea true, hacer algo
}

Estructuras de Control -Ciclos

PHP - Repaso V

function unaFuncion($param, $paramDefault = null) {
    // Hacer algo con la funcion
    return $algunValor; // Return es optativo
}

Funciones

PHP - POO I

class UnaClase extends ClasePadre {
    
    private $variablePrivada;

    public function __construct() {
        // Hacer algo en el constructor
    }

    private function metodoPrivado($param) {
        $this->variablePrivada = $param;
        // Hacer algo mas
    }

    public function unMetodoPublico($valor) {
        $this->variablePrivada = $valor;
    }

    public function otroMetodoPublico() {
        return $this->variablePrivada;
    }

Definición de Clases

class UnaClase extends ClasePadre {
    
    // ... sigue

    public static function metodoEstatico() {
        return self::getNumero();
    }

    private static function getNumero() {
        return 333;
    }
}

PHP - POO II

// Invoca __construct() de forma implicita
$objeto = new UnaClase();

$objeto->unMetodoPublico(12345);

echo $objeto->otroMetodoPublico();

echo UnaClase::metodoEstatico();

Uso de Objetos

Bases de Datos

MySQL - Instalacion

sudo apt-get install mysql-server mysql-client

MySQL -Interfaces - CLI

$ mysql -u paw -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.7.21-0ubuntu0.16.04.1 (Ubuntu)

Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

MySQL -Interfaces

MySQL -Interfaces

Instalar

PHP y MySQL

PDO y Drivers

sudo apt-get install php5-mysql
  • Capa uniforme de acceso a datos
  • Requiere driver para dialogar con un SGDB
  • Ofrece abstracción y uniformidad
  • PHP Data Objects
CREATE SCHEMA movies;

USE movies;

CREATE TABLE peliculas (
    id INT NOT NULL AUTO_INCREMENT,
    nombre VARCHAR(300) NOT NULL,
    anio_estreno INT NOT NULL,
    reparto VARCHAR(5000) NULL,
    cartel VARCHAR(300) NULL,
    PRIMARY KEY (id)
);
<?php

$params = [
    'host' => 'localhost',
    'user' => 'paw',
    'pwd' => 'paw',
    'db' => 'movies'
];

try {
    // Definimos el Data Source Name
    $dsn = sprintf("mysql:host=%s;dbname=%s", $params['host'], $params['db']);

    // Se crea el objeto conexion
    $pdo = new PDO($dsn, $params['user'], $params['pwd']);

} catch (PDOException $e) {
    echo $e->getMessage();
} catch (Throwable $e) {
    echo $e->getMessage();
}

// sigue ...

testPDO.php

// ... sigue

$sentencia = $pdo->query("SELECT * FROM peliculas");

// PDO::FETCH_NUM o PDO::FETCH_ASSOC
while ($row = $sentencia->fetch(PDO::FETCH_ASSOC)) {
    print_r($row);
}

testPDO.php

Proyecto de Prueba

Objetivos

  • Estructura de un proyecto simple en PHP
  • Discutir nociones avanzadas de organización de proyectos
  • Separación básica de conceptos (pseudo MVC)
  • migrations y manejo de base de datos
  • Estructuras de datos, uso de objetos y vistas

Estructura del proyecto

movies-list/
├── config.php
├── config.php.sample
├── index.php
├── README.md
├── sql/
│   └── 0.5.0/
│       └── 001_create_table_peliculas.sql
├── src/
│   ├── classes/
│   │   └── Pelicula.php
│   ├── core/
│   │   ├── Config.php
│   │   └── PdoFactory.php
│   ├── get.movies.php
│   ├── save.movies.php
│   └── views/
│       ├── index.view.php
│       └── new.movie.view.php
├── .gitignore
└── VERSION
<?php
require __DIR__ . '/src/get.movies.php';
require __DIR__ . '/src/views/index.view.php';

index.php

<?php
require __DIR__ . '/classes/Pelicula.php';

$peliculaModel = new Pelicula();
$peliculas = $peliculaModel->selectAll();

src/get.movies.php

<?php
require __DIR__ . '/../core/PdoFactory.php';

/**
* Representa una pelicula
*/
class Pelicula
{

    private $nombre;
    private $anio_estreno;
    private $reparto;
    private $cartel;
    private $campos = ['nombre', 'anio_estreno', 'reparto', 'cartel'];

    public function selectAll()
    {
        $pdo = PdoFactory::build();
        $query = $pdo->prepare("SELECT * FROM peliculas");
        $query->execute();
        return $query->fetchAll();
    }

    public function setDatos($datos)
    {
        //$nombre, $anio_estreno, $reparto = null, $cartel = null
        if (is_null($datos['nombre']) || is_null($datos['anio_estreno'])) {
            throw new Exception("Nombre y Fecha de Estreno son obligatorios", 1);
        }

        foreach ($this->campos as $campo) {
            $this->$campo = $datos[$campo];
        }
    }

    public function insert()
    {
        $campos = $this->getCampos();
        $pdoString = $this->getValoresParametrizadosPDO();
        $pdo = PdoFactory::build();
        $query = $pdo->prepare("INSERT INTO peliculas ($campos) VALUES ($pdoString)");
        $query->execute($this->getValues());
    }

    public function getCampos() {
        return join(',', $this->campos);
    }

    /**
     * Retorna un string '?, ?, ?...' listo para ser usado en una query PDO
     *
     * @return [type] [description]
     */
    public function getValoresParametrizadosPDO()
    {
        return implode(',', array_fill(0, count($this->campos), '?')); // Retorna el values parametrizado para PDO
    }

    public function getValues()
    {
        $data = [];
        foreach ($this->campos as $campo) {
            $data[] = $this->$campo;
        }
        return $data;
    }
}

src/classes/Pelicula.php

<?php

require __DIR__ . '/Config.php';

class PdoFactory
{
    public static function build()
    {
        try {
            $dbConfig = self::getConfig();
            return new PDO(self::getDsn(), $dbConfig->username, $dbConfig->password);
        } catch (PDOException $e) {
            echo "Error PDOException: " . $e->getMessage();
            die();
        }
    }

    private static function getDsn()
    {
        $dbConfig = self::getConfig();
        return "mysql:host=" . $dbConfig->host . ";dbname=" . $dbConfig->databasename;
    }

    private static function getConfig()
    {
        return (new Config())->db;
    }
}

src/core/PdoFactory.php

<?php

class Config
{

    private $configFile = __DIR__ . "/../../config.php";
    public $db;

    public function __construct()
    {
        $this->db = require $this->configFile;
    }
}

src/core/Config.php

<?php

$db = new stdClass();

$db->username = 'paw';
$db->password = 'paw';
$db->host = 'localhost';
$db->databasename = 'movies';

return $db;

config.php y config.php.sample

<!DOCTYPE html>
<html lang="es">
<head>
  <title>Peliculas</title>
</head>
<body>
  <nav>
    <ul>
      <li><a href="src/views/new.movie.view.php">Agregar Pelicula</a></li>
    </ul>
  </nav>
  <main>
    <section id="listado_peliculas">
      <h1>Lista de Peliculas</h1>
      <?php if ($peliculas) : ?>
        <ul>
            <?php foreach ($peliculas as $pelicula) : ?>
            <li><?= $pelicula['nombre'] ?> (<?= $pelicula['anio_estreno'] ?>)</li>
            <?php endforeach; ?>
        </ul>
        <?php else : ?>
        <p>No hay peliculas cargadas</p>
        <?php endif; ?>
    </section>
  </main>
</body>
</html>

src/views/index.view.php

<!DOCTYPE html>
<html lang="es">
<head>
  <title>Nueva Pelicula</title>
</head>
<body>
  <head>
    <h1>Agregar Pelicula</h1>
  </head>
  <main>
    <form action="/src/save.movies.php" method="post" enctype="multipart/form-data">
      <label for="nombre">Nombre</label>
      <input type="text" name="nombre" placeholder="Nombre de la pelicula" required="required">
      <label for="anio_estreno">Año de Estreno</label>
      <input type="year" name="anio_estreno" placeholder="Año de Estreno" required="required">
      <label for="reparto">Reparto</label>
      <textarea name="reparto" placeholder="Reparto"></textarea>
      <label for="cartel">Cartelera</label>
      <input type="file" name="cartel" placeholder="Cartel">
      <input type="submit" name="guardar" value="Guardar">
      <input type="reset" value="Restablecer">
    </form>
  </main>
</body>
</html>

src/views/new.movie.view.php

<?php
require __DIR__ . '/classes/Pelicula.php';

if (empty($_POST)) {
    throw new Exception("Operacion no permitida", 1);
}

$datosPersona = [
    "nombre" => $_POST['nombre'],
    "anio_estreno" => $_POST['anio_estreno'],
    "reparto" => isset($_POST['reparto']) && ! empty($_POST['reparto']) ? $_POST['reparto'] : null,
    "cartel" => isset($_FILES['cartel']) ? $_FILES['cartel']['tmp_name'] : null
];

$pelicula = new Pelicula();
$pelicula->setDatos($datosPersona);
$pelicula->insert();

header('Location: /index.php');

src/save.movies.php

# Sistema de carga de peliculas

Permite administrar un catalogo de peliculas

## Instalacion

Copie el archivo de configuracion de ejemplo y ponga los datos de su base de datos:

```
cp config.php.sample config.php
vim config.php
```

A continuacion, cree la base de datos con los scripts que se encuentran en el directorio `sql/<ultima_version>/nueva`.

Para esto, ejecute los scripts en orden numerico

## Actualizacion

Ejecute los scripts que se encuentran en el directorio `sql/<ultima_version>/migration`.

Para esto, ejecute los scripts en orden numerico. Estos scripts suponen que usted actualiza desde la version estrictamente
anterior. Si no es asi, actualice de forma incremental hasta la ultima.

## Creacion de version

Cuando se genera una nueva version en la carpeta `sql/<nueva_version>/*.sql`, numerar los scripts
a partir del codigo `001_`, dejando libre el codigo `000_` para una creacion nueva de la base
de una version anterior.

README.md

CREATE TABLE peliculas (
  id INT NOT NULL AUTO_INCREMENT,
  nombre VARCHAR(300) NOT NULL,
  anio_estreno INT NOT NULL,
  reparto VARCHAR(5000) NULL,
  cartel VARCHAR(300) NULL,
  PRIMARY KEY (id)
);

sql/0.5.0/001_create_table_peliculas.sql

0.5.0

VERSION

.gitignore

config.php
$ cd ~/workspace/movies-list/
$ php -S localhost:8888
PHP 7.2.3-1+ubuntu16.04.1+deb.sury.org+1 Development Server started at Wed Apr  4 17:17:08 2018
Listening on http://localhost:8888
Document root is /home/tomas/Dropbox/UNLu/PAW/2018/backend-2/movies-review
Press Ctrl-C to quit.
[Wed Apr  4 17:35:19 2018] 127.0.0.1:44712 [200]: /
[Wed Apr  4 17:45:48 2018] 127.0.0.1:44794 [200]: /src/views/new.movie.view.php
[Wed Apr  4 17:46:09 2018] 127.0.0.1:44796 [302]: /src/save.movies.php
[Wed Apr  4 17:46:09 2018] 127.0.0.1:44798 [200]: /index.php

Ejecutar Proyecto

Problemas con el proyecto

  • No hay un único punto de acceso
    • Expone la estructura del proyecto

    • require de forma "dispersa" en todos los archivos
    • No se puede implementar un ruteador (No es obligatorio en MVC pero altamente recomendable)
    • No se puede o es muy difícil implementar un autoload
  • Clase Películas tiene mezclada lógica de query builder con conocimiento del modelo
    • Se puede refactorizar en una clase "Modelo" genérica y que Película extienda de dicha clase
  • Se pueden incorporar pequeños elementos de auditoria de base
    • created_at
    • updated_at
    • Quien realiza un cambio o insert

Problemas con el proyecto

  • Estructura de archivos del proyecto
    • No tiene parte "no publica"
    • config.php no debería estar en la raíz del proyecto
    • En general, lo publico suele ir dentro de un subdirectorio (llamado por convención `www/` o `public/`) y la raiz del proyecto no es publica
    • Falta un directorio `resources/` o `static/` donde van imágenes, css, javascript, fuentes, etc...
  • Falta un objeto que maneje los datos de los Request (ver el manejo que se hace de los mismos en `save.movies.php`.

Consigna

Extender el proyecto movies-list para permitir editar y eliminar las películas cargadas.

Prestar atención a las decisiones a adoptar en relación a: URL a ser construidas; Validación de datos; Manejo de errores; reutilización de vistas; etc...

Backend Programming 2

By Tomas Delvechio

Backend Programming 2

PHP - OOP and Databases

  • 1,006