K. Carpeta « php »

Versión para imprimir.

A. php / amigo-agrega.php

1
<?php
2
3
require_once __DIR__ . "/lib/manejaErrores.php";
4
require_once __DIR__ . "/lib/recibeEntero.php";
5
require_once __DIR__ . "/lib/recibeTextoObligatorio.php";
6
require_once __DIR__ . "/lib/recibeEnteroOpcional.php";
7
require_once __DIR__ . "/lib/devuelveCreated.php";
8
require_once __DIR__ . "/Bd.php";
9
require_once __DIR__ . "/pasatiempoOptions.php";
10
11
$nombre = recibeTextoObligatorio("nombre");
12
$pasId = recibeEnteroOpcional("pasId");
13
14
$pdo = Bd::pdo();
15
$stmt = $pdo->prepare(
16
 "INSERT INTO AMIGO (
17
   AMI_NOMBRE, PAS_ID
18
  ) values (
19
   :PAS_NOMBRE, :PAS_ID
20
  )"
21
);
22
$stmt->execute([
23
 ":PAS_NOMBRE" => $nombre,
24
 ":PAS_ID" => $pasId
25
]);
26
$id = $pdo->lastInsertId();
27
28
$encodeId = urlencode($id);
29
devuelveCreated(
30
 "/php/amigo-vista-modifica.php?id=$encodeId",
31
 [
32
  "id" => ["value" => $id],
33
  "nombre" => ["value" => $nombre],
34
  "pasId" => [
35
   "innerHTML" => pasatiempoOptons(),
36
   "value" => $pasId === null ? "" : $pasId
37
  ]
38
 ]
39
);
40

B. php / amigo-elimina.php

1
<?php
2
3
require_once __DIR__ . "/lib/manejaErrores.php";
4
require_once __DIR__ . "/lib/recibeEnteroObligatorio.php";
5
require_once __DIR__ . "/lib/devuelveNoContent.php";
6
require_once __DIR__ . "/Bd.php";
7
8
$id = recibeEnteroObligatorio("id");
9
10
$bd = Bd::pdo();
11
$stmt = $bd->prepare("DELETE FROM AMIGO WHERE AMI_ID = :AMI_ID");
12
$stmt->execute([":AMI_ID" => $id]);
13
14
devuelveNoContent();
15

C. php / amigo-modifica.php

1
<?php
2
3
require_once __DIR__ . "/lib/manejaErrores.php";
4
require_once __DIR__ . "/lib/recibeEnteroObligatorio.php";
5
require_once __DIR__ . "/lib/recibeTextoObligatorio.php";
6
require_once __DIR__ . "/lib/recibeEnteroOpcional.php";
7
require_once __DIR__ . "/lib/devuelveJson.php";
8
require_once __DIR__ . "/Bd.php";
9
require_once __DIR__ . "/pasatiempoOptions.php";
10
11
$id = recibeEnteroObligatorio("id");
12
$nombre = recibeTextoObligatorio("nombre");
13
$pasId = recibeEnteroOpcional("pasId");
14
15
$bd = Bd::pdo();
16
$stmt = $bd->prepare(
17
 "UPDATE AMIGO
18
   SET
19
    AMI_NOMBRE = :AMI_NOMBRE,
20
    PAS_ID = :PAS_ID
21
   WHERE
22
    AMI_ID = :AMI_ID"
23
);
24
$stmt->execute([
25
 ":AMI_NOMBRE" => $nombre,
26
 ":PAS_ID" => $pasId,
27
 ":AMI_ID" => $id,
28
]);
29
30
devuelveJson([
31
 "id" => ["value" => $id],
32
 "nombre" => ["value" => $nombre],
33
 "pasId" => [
34
  "innerHTML" => pasatiempoOptons(),
35
  "value" => $pasId === null ? "" : $pasId
36
 ]
37
]);
38

D. php / amigo-vista-agrega.php

1
<?php
2
3
require_once __DIR__ . "/lib/manejaErrores.php";
4
require_once __DIR__ . "/lib/devuelveJson.php";
5
require_once __DIR__ . "/pasatiempoOptions.php";
6
7
devuelveJson(["pasId" => ["innerHTML" => pasatiempoOptons()]]);
8

E. php / amigo-vista-index.php

1
<?php
2
3
require_once __DIR__ . "/lib/manejaErrores.php";
4
require_once __DIR__ . "/lib/devuelveJson.php";
5
require_once __DIR__ . "/Bd.php";
6
7
$bd = Bd::pdo();
8
$stmt = $bd->query(
9
 "SELECT
10
    A.AMI_ID,
11
    A.AMI_NOMBRE,
12
    P.PAS_NOMBRE
13
   FROM AMIGO A
14
    LEFT JOIN PASATIEMPO P
15
    ON A.PAS_ID = P.PAS_ID
16
   ORDER BY
17
    A.AMI_NOMBRE"
18
);
19
$lista = $stmt->fetchAll(PDO::FETCH_ASSOC);
20
21
$render = "";
22
foreach ($lista as $modelo) {
23
 $encodeAmiId = urlencode($modelo["AMI_ID"]);
24
 $amiId = htmlentities($encodeAmiId);
25
 $amiNombre = htmlentities($modelo["AMI_NOMBRE"]);
26
 $pasNombre = $modelo["PAS_NOMBRE"] === null
27
  ? "<em>-- Sin pasatiempo --</em>"
28
  : htmlentities($modelo["PAS_NOMBRE"]);
29
 $render .=
30
  "<dt><a href='modifica.html?id=$amiId'>$amiNombre</a></dt>
31
    <dd><a href='modifica.html?id=$amiId'>$pasNombre</a></dd>";
32
}
33
34
devuelveJson(["lista" => ["innerHTML" => $render]]);
35

F. php / amigo-vista-modifica.php

1
<?php
2
3
require_once __DIR__ . "/lib/manejaErrores.php";
4
require_once __DIR__ . "/lib/recibeEnteroObligatorio.php";
5
require_once __DIR__ . "/lib/validaEntidadObligatoria.php";
6
require_once __DIR__ . "/lib/devuelveJson.php";
7
require_once __DIR__ . "/Bd.php";
8
require_once __DIR__ . "/pasatiempoOptions.php";
9
10
$amiId = recibeEnteroObligatorio("id");
11
12
$bd = Bd::pdo();
13
$stmt = $bd->prepare("SELECT * FROM AMIGO WHERE AMI_ID = :AMI_ID");
14
$stmt->execute([":AMI_ID" => $amiId]);
15
$modelo = $stmt->fetch(PDO::FETCH_ASSOC);
16
17
$modelo = validaEntidadObligatoria("Amigo",  $modelo);
18
19
devuelveJson([
20
 "id" => ["value" => $amiId],
21
 "nombre" => ["value" => $modelo["AMI_NOMBRE"]],
22
 "pasId" => [
23
  "innerHTML" => pasatiempoOptons(),
24
  "value" => $modelo["PAS_ID"] === null ? "" : $modelo["PAS_ID"]
25
 ]
26
]);
27

G. php / Bd.php

1
<?php
2
3
class Bd
4
{
5
 private static ?PDO $pdo = null;
6
7
 static function pdo(): PDO
8
 {
9
  if (self::$pdo === null) {
10
11
   self::$pdo = new PDO(
12
    // cadena de conexión
13
    "sqlite:" . __DIR__ . "/srvauno.db",
14
    // usuario
15
    null,
16
    // contraseña
17
    null,
18
    // Opciones: pdos no persistentes y lanza excepciones.
19
    [PDO::ATTR_PERSISTENT => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
20
   );
21
22
   self::$pdo->exec(
23
    'CREATE TABLE IF NOT EXISTS PASATIEMPO (
24
      PAS_ID INTEGER,
25
      PAS_NOMBRE TEXT NOT NULL,
26
      CONSTRAINT PAS_PK
27
       PRIMARY KEY(PAS_ID),
28
      CONSTRAINT PAS_NOM_UNQ
29
       UNIQUE(PAS_NOMBRE),
30
      CONSTRAINT PAS_NOM_NV
31
       CHECK(LENGTH(PAS_NOMBRE) > 0)
32
     )'
33
   );
34
   self::$pdo->exec(
35
    'CREATE TABLE IF NOT EXISTS AMIGO (
36
      AMI_ID INTEGER,
37
      AMI_NOMBRE TEXT NOT NULL,
38
      PAS_ID INTEGER,
39
      CONSTRAINT AMI_PK
40
       PRIMARY KEY(AMI_ID),
41
      CONSTRAINT AMI_NOM_UNQ
42
       UNIQUE(AMI_NOMBRE),
43
      CONSTRAINT AMI_NOM_NV
44
       CHECK(LENGTH(AMI_NOMBRE) > 0),
45
      CONSTRAINT AMI_PAS_FK
46
       FOREIGN KEY (PAS_ID) REFERENCES PASATIEMPO(PAS_ID)
47
     )'
48
   );
49
50
   $cantidadDePasatiempos =
51
    self::$pdo->query("SELECT COUNT(PAS_ID) FROM PASATIEMPO")->fetchColumn(0);
52
53
   if ($cantidadDePasatiempos === 0) {
54
    self::$pdo->exec(
55
     "INSERT INTO PASATIEMPO (PAS_NOMBRE) VALUES ('Futbol'), ('Videojuegos')"
56
    );
57
   }
58
  }
59
60
  return self::$pdo;
61
 }
62
}
63

H. php / pasatiempoOptions.php

1
<?php
2
3
require_once __DIR__ . "/Bd.php";
4
5
function pasatiempoOptons()
6
{
7
 $bd = Bd::pdo();
8
 $stmt = $bd->query("SELECT * FROM PASATIEMPO ORDER BY PAS_NOMBRE");
9
 $lista = $stmt->fetchAll(PDO::FETCH_ASSOC);
10
11
 $render = "<option value=''>-- Sin pasatiempo --</option>";
12
 foreach ($lista as $modelo) {
13
  $id = htmlentities($modelo["PAS_ID"]);
14
  $nombre = htmlentities($modelo["PAS_NOMBRE"]);
15
  $render .= "<option value='$id'>{$nombre}</option>";
16
 }
17
18
 return $render;
19
}
20

I. Carpeta « php / lib »

Versión para imprimir.

1. php / lib / BAD_REQUEST.php

1
<?php
2
3
const BAD_REQUEST = 400;
4

2. php / lib / devuelveCreated.php

1
<?php
2
3
require_once __DIR__ . "/devuelveResultadoNoJson.php";
4
5
function devuelveCreated($urlDelNuevo, $resultado)
6
{
7
 $json = json_encode($resultado);
8
 if ($json === false) {
9
  devuelveResultadoNoJson();
10
 } else {
11
  http_response_code(201);
12
  header("Location: $urlDelNuevo");
13
  header("Content-Type: application/json; charset=utf-8");
14
  echo $json;
15
 }
16
}
17

3. php / lib / devuelveJson.php

1
<?php
2
3
require_once __DIR__ . "/devuelveResultadoNoJson.php";
4
5
function devuelveJson($resultado)
6
{
7
 $json = json_encode($resultado);
8
 if ($json === false) {
9
  devuelveResultadoNoJson();
10
 } else {
11
  header("Content-Type: application/json; charset=utf-8");
12
  echo $json;
13
 }
14
 exit();
15
}
16

4. php / lib / devuelveNoContent.php

1
<?php
2
3
function devuelveNoContent()
4
{
5
 http_response_code(204);
6
}
7

5. php / lib / devuelveResultadoNoJson.php

1
<?php
2
3
require_once __DIR__ . "/INTERNAL_SERVER_ERROR.php";
4
5
function devuelveResultadoNoJson()
6
{
7
 http_response_code(INTERNAL_SERVER_ERROR);
8
 header("Content-Type: application/problem+json; charset=utf-8");
9
10
 echo '{' .
11
  "status: " . INTERNAL_SERVER_ERROR .
12
  '"title": "El resultado no puede representarse como JSON."' .
13
  '"type": "/errors/resultadonojson.html"' .
14
  '}';
15
}
16

6. php / lib / INTERNAL_SERVER_ERROR.php

1
<?php
2
3
const INTERNAL_SERVER_ERROR = 500;

7. php / lib / manejaErrores.php

1
<?php
2
3
require_once __DIR__ . "/INTERNAL_SERVER_ERROR.php";
4
require_once __DIR__ . "/ProblemDetailsException.php";
5
6
// Hace que se lance una excepción automáticamente cuando se genere un error.
7
set_error_handler(function ($severity, $message, $file, $line) {
8
 throw new ErrorException($message, 0, $severity, $file, $line);
9
});
10
11
// Código cuando una excepción no es atrapada.
12
set_exception_handler(function (Throwable $excepcion) {
13
 if ($excepcion instanceof ProblemDetailsException) {
14
  devuelveProblemDetails($excepcion->problemDetails);
15
 } else {
16
  devuelveProblemDetails([
17
   "status" => INTERNAL_SERVER_ERROR,
18
   "title" => "Error interno del servidor",
19
   "detail" => $excepcion->getMessage(),
20
   "type" => "/errors/errorinterno.html",
21
  ]);
22
 }
23
 exit();
24
});
25
26
function devuelveProblemDetails(array $array)
27
{
28
 $json = json_encode($array);
29
 if ($json === false) {
30
  devuelveResultadoNoJson();
31
 } else {
32
  http_response_code(isset($array["status"]) ? $array["status"] : 500);
33
  header("Content-Type: application/problem+json; charset=utf-8");
34
  echo $json;
35
 }
36
}
37

8. php / lib / NOT_FOUND.php

1
<?php
2
3
const NOT_FOUND = 404;
4

9. php / lib / ProblemDetailsException.php

1
<?php
2
3
require_once __DIR__ . "/INTERNAL_SERVER_ERROR.php";
4
5
/**
6
 * Detalle de los errores devueltos por un servicio.
7
 */
8
class ProblemDetailsException extends Exception
9
{
10
11
 public array $problemDetails;
12
13
 public function __construct(
14
  array $problemDetails,
15
 ) {
16
  
17
  parent::__construct(
18
   isset($problemDetails["detail"])
19
    ? $problemDetails["detail"]
20
    : (isset($problemDetails["title"])
21
     ? $problemDetails["title"]
22
     : "Error"),
23
   $problemDetails["status"]
24
    ? $problemDetails["status"]
25
    : INTERNAL_SERVER_ERROR
26
  );
27
28
  $this->problemDetails = $problemDetails;
29
 }
30
}
31

10. php / lib / recibeEntero.php

1
<?php
2
3
require_once __DIR__ . "/recibeTexto.php";
4
5
/**
6
 * Devuelve el valor entero de un parámetro recibido en el
7
 * servidor por medio de GET, POST o cookie.
8
 * 
9
 * Si el parámetro no se recibe, devuelve false
10
 *
11
 * Si se recibe una cadena vacía, se devuelve null.
12
 * 
13
 * Si parámetro no se puede convertir a entero, se genera
14
 * un error.
15
 */
16
function recibeEntero(string $parametro): false|null|int
17
{
18
 $valor = recibeTexto($parametro);
19
 if ($valor === false) {
20
  return false;
21
 } else {
22
  $valor = trim($valor);
23
  if ($valor === "") {
24
   return null;
25
  } else {
26
   return (int) $valor;
27
  }
28
 }
29
}
30

11. php / lib / recibeEnteroObligatorio.php

1
<?php
2
3
require_once __DIR__ . "/BAD_REQUEST.php";
4
require_once __DIR__ . "/recibeEntero.php";
5
require_once __DIR__ . "/ProblemDetailsException.php";
6
7
function recibeEnteroObligatorio(string $parametro)
8
{
9
 $entero = recibeEntero($parametro);
10
11
 if ($entero === false)
12
  throw new ProblemDetailsException([
13
   "status" => BAD_REQUEST,
14
   "title" => "Falta el valor $parametro.",
15
   "type" => "/errors/faltavalor.html",
16
   "detail" => "La solicitud no tiene el valor de $parametro."
17
  ]);
18
19
 if ($entero === null)
20
  throw new ProblemDetailsException([
21
   "status" => BAD_REQUEST,
22
   "title" => "Campo $parametro en blanco.",
23
   "type" => "/errors/campoenteroenblanco.html",
24
   "detail" => "Pon un número entero en el campo $parametro."
25
  ]);
26
27
 return $entero;
28
}
29

12. php / lib / recibeEnteroOpcional.php

1
<?php
2
3
require_once __DIR__ . "/recibeEntero.php";
4
5
function recibeEnteroOpcional(string $parametro)
6
{
7
 $enteroOpcional = recibeEntero($parametro);
8
 return $enteroOpcional === false ? null : $enteroOpcional;
9
}
10

13. php / lib / recibeTexto.php

1
<?php
2
3
/**
4
 * Devuelve el texto de un parámetro enviado al
5
 * servidor por medio de GET, POST o cookie.
6
 * 
7
 * Si el parámetro no se recibe, devuelve false.
8
 */
9
function recibeTexto(string $parametro): false|string
10
{
11
 /* Si el parámetro está asignado en $_REQUEST,
12
  * devuelve su valor; de lo contrario, devuelve false.
13
  */
14
 $valor = isset($_REQUEST[$parametro])
15
  ? $_REQUEST[$parametro]
16
  : false;
17
 return $valor;
18
}
19

14. php / lib / recibeTextoObligatorio.php

1
<?php
2
3
require_once __DIR__ . "/BAD_REQUEST.php";
4
require_once __DIR__ . "/recibeTexto.php";
5
require_once __DIR__ . "/ProblemDetailsException.php";
6
7
function recibeTextoObligatorio(string $parametro)
8
{
9
 $texto = recibeTexto($parametro);
10
11
 if ($texto === false)
12
  throw new ProblemDetailsException([
13
   "status" => BAD_REQUEST,
14
   "title" => "Falta el valor $parametro.",
15
   "type" => "/errors/faltavalor.html",
16
   "detail" => "La solicitud no tiene el valor de $parametro."
17
  ]);
18
19
 $trimTexto = trim($texto);
20
21
 if ($trimTexto === "")
22
  throw new ProblemDetailsException([
23
   "status" => BAD_REQUEST,
24
   "title" => "Campo $parametro en blanco.",
25
   "type" => "/errors/campoenblanco.html",
26
   "detail" => "Pon texto en el campo $parametro."
27
  ]);
28
29
 return $trimTexto;
30
}
31

15. php / lib / validaEntidadObligatoria.php

1
<?php
2
3
require_once __DIR__ . "/NOT_FOUND.php";
4
require_once __DIR__ . "/ProblemDetailsException.php";
5
6
function validaEntidadObligatoria(string $nombre,  $entidad)
7
{
8
9
 if ($entidad === false)
10
  throw new ProblemDetailsException([
11
  "status" => NOT_FOUND,
12
  "title" => "Registro de $nombre no encontrado.",
13
  "type" => "/errors/entidadnoencontrada.html",
14
  "detail" => "No se encontró ningún registro de $nombre con el id solicitado.",
15
  ]);
16
17
 return $entidad;
18
}
19