M. Carpeta « srv »

Versión para imprimir.

A. srv / sincroniza.php

1<?php
2
3require_once __DIR__ . "/../lib/php/recuperaJson.php";
4require_once __DIR__ . "/../lib/php/devuelveJson.php";
5require_once __DIR__ . "/../lib/php/ProblemDetails.php";
6require_once __DIR__ . "/../lib/php/devuelveProblemDetails.php";
7require_once __DIR__ . "/../lib/php/devuelveErrorInterno.php";
8require_once __DIR__ . "/modelo/Pasatiempo.php";
9require_once __DIR__ . "/modelo/recuperaPasatiempo.php";
10require_once __DIR__ . "/bd/pasatiempoAgrega.php";
11require_once __DIR__ . "/bd/pasatiempoBusca.php";
12require_once __DIR__ . "/bd/pasatiempoConsultaNoEliminados.php";
13require_once __DIR__ . "/bd/pasatiempoModifica.php";
14
15try {
16
17 $lista = recuperaJson();
18
19 if (!is_array($lista)) {
20 $lista = [];
21 }
22
23 foreach ($lista as $objeto) {
24
25 $modeloEnElCliente = recuperaPasatiempo($objeto);
26 $modeloEnElServidor = pasatiempoBusca($modeloEnElCliente->uuid);
27
28 if ($modeloEnElServidor === false) {
29
30 /* CONFLICTO. El objeto no ha estado en el servidor.
31 * AGREGARLO solamente si no está eliminado. */
32 if (!$modeloEnElCliente->eliminado) {
33 pasatiempoAgrega($modeloEnElCliente);
34 }
35 } elseif (
36 !$modeloEnElServidor->eliminado && $modeloEnElCliente->eliminado
37 ) {
38
39 /* CONFLICTO. El registro está en el servidor, donde no se ha
40 * eliminado, pero ha sido eliminado en el cliente.
41 * Gana el cliente, porque optamos por no revivir lo que se ha
42 * borrado. */
43 pasatiempoModifica($modeloEnElCliente);
44 } else if (
45 !$modeloEnElCliente->eliminado && !$modeloEnElServidor->eliminado
46 ) {
47
48 /* CONFLICTO. El registro está tanto en el servidor como en el
49 * cliente. Los datos pueden ser diferentes.
50 * PREVALECE LA FECHA MÁS GRANDE. Cuando gana el servidor no se
51 * hace nada.*/
52 if (
53 $modeloEnElCliente->modificacion > $modeloEnElServidor->modificacion
54 ) {
55 // La versión del cliente es más nueva y prevalece.
56 pasatiempoModifica($modeloEnElCliente);
57 }
58 }
59 }
60
61 $lista = pasatiempoConsultaNoEliminados();
62
63 devuelveJson($lista);
64} catch (ProblemDetails $details) {
65
66 devuelveProblemDetails($details);
67} catch (Throwable $error) {
68
69 devuelveErrorInterno($error);
70}
71

B. Carpeta « srv / bd »

Versión para imprimir.

1. srv / bd / Bd.php

1<?php
2
3require_once __DIR__ . "/../modelo/Pasatiempo.php";
4
5class Bd
6{
7
8 private static ?PDO $conexion = null;
9
10 static function getConexion(): PDO
11 {
12 if (self::$conexion === null) {
13 self::$conexion = new PDO(
14 // cadena de conexión
15 "sqlite:sincronizacion.db",
16 // usuario
17 null,
18 // contraseña
19 null,
20 // Opciones: conexiones persistentes y lanza excepciones.
21 [PDO::ATTR_PERSISTENT => true, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
22 );
23
24 self::$conexion->exec(
25 'CREATE TABLE IF NOT EXISTS PASATIEMPO (
26 PAS_UUID TEXT NOT NULL,
27 PAS_NOMBRE TEXT NOT NULL,
28 PAS_MODIFICACION INTEGER NOT NULL,
29 PAS_ELIMINADO INTEGER NOT NULL,
30 CONSTRAINT PAS_PK
31 PRIMARY KEY(PAS_UUID),
32 CONSTRAINT PAS_UUID_NV
33 CHECK(LENGTH(PAS_UUID) > 0),
34 CONSTRAINT PAS_NOM_NV
35 CHECK(LENGTH(PAS_NOMBRE) > 0)
36 )'
37 );
38 }
39
40 return self::$conexion;
41 }
42}
43

2. srv / bd / pasatiempoAgrega.php

1<?php
2
3require_once __DIR__ . "/Bd.php";
4require_once __DIR__ . "/../modelo/Pasatiempo.php";
5
6function pasatiempoAgrega(Pasatiempo $modelo)
7{
8
9 $modelo->valida();
10
11 $conexion = Bd::getConexion();
12
13 $conexion->prepare(
14 "INSERT INTO PASATIEMPO
15 (PAS_UUID, PAS_NOMBRE, PAS_MODIFICACION, PAS_ELIMINADO)
16 VALUES
17 (:uuid, :nombre, :modificacion, :eliminado)"
18 )
19 ->execute([
20 ":uuid" => $modelo->uuid,
21 ":nombre" => $modelo->nombre,
22 ":modificacion" => $modelo->modificacion,
23 ":eliminado" => $modelo->eliminado ? 1 : 0
24 ]);
25}
26

3. srv / bd / pasatiempoBusca.php

1<?php
2
3require_once __DIR__ . "/../../lib/php/fetch.php";
4require_once __DIR__ . "/Bd.php";
5require_once __DIR__ . "/../modelo/Pasatiempo.php";
6
7function pasatiempoBusca(string $uuid): false|Pasatiempo
8{
9
10 $conexion = Bd::getConexion();
11
12 return fetch(
13 $conexion->prepare(
14 "SELECT
15 PAS_UUID AS uuid,
16 PAS_NOMBRE AS nombre,
17 PAS_MODIFICACION AS modificacion,
18 PAS_ELIMINADO AS eliminado
19 FROM PASATIEMPO
20 WHERE PAS_UUID = :uuid"
21 ),
22 [":uuid" => $uuid],
23 PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE,
24 Pasatiempo::class
25 );
26}
27

4. srv / bd / pasatiempoConsultaNoEliminados.php

1<?php
2
3require_once __DIR__ . "/../../lib/php/fetchAll.php";
4require_once __DIR__ . "/Bd.php";
5require_once __DIR__ . "/../modelo/Pasatiempo.php";
6
7/** @return Pasatiempo[] */
8function pasatiempoConsultaNoEliminados()
9{
10 $conexion = Bd::getConexion();
11
12 return fetchAll(
13 $conexion->query(
14 "SELECT
15 PAS_UUID AS uuid,
16 PAS_NOMBRE AS nombre,
17 PAS_MODIFICACION AS modificacion,
18 PAS_ELIMINADO AS eliminado
19 FROM PASATIEMPO
20 WHERE PAS_ELIMINADO = 0
21 ORDER BY PAS_NOMBRE"
22 ),
23 [],
24 PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE,
25 Pasatiempo::class
26 );
27}
28

5. srv / bd / pasatiempoModifica.php

1<?php
2
3require_once __DIR__ . "/Bd.php";
4require_once __DIR__ . "/../modelo/Pasatiempo.php";
5
6function pasatiempoModifica(Pasatiempo $modelo)
7{
8
9 $modelo->valida();
10
11 $conexion = Bd::getConexion();
12
13 $conexion->prepare(
14 "UPDATE PASATIEMPO
15 SET
16 PAS_NOMBRE = :nombre,
17 PAS_MODIFICACION = :modificacion,
18 PAS_ELIMINADO = :eliminado
19 WHERE PAS_UUID = :uuid"
20 )
21 ->execute([
22 ":uuid" => $modelo->uuid,
23 ":nombre" => $modelo->nombre,
24 ":modificacion" => $modelo->modificacion,
25 ":eliminado" => $modelo->eliminado ? 1 : 0
26 ]);
27}
28

C. Carpeta « srv / modelo »

Versión para imprimir.

1. srv / modelo / Pasatiempo.php

1<?php
2
3class Pasatiempo
4{
5 public string $uuid;
6 public string $nombre;
7 public int $modificacion;
8 public bool $eliminado;
9
10 public function __construct(
11 string $nombre = "",
12 string $uuid = "",
13 int $modificacion = 0,
14 bool $eliminado = true
15 ) {
16 $this->nombre = $nombre;
17 $this->uuid = $uuid;
18 $this->modificacion = $modificacion;
19 $this->eliminado = $eliminado;
20 }
21
22 public function valida()
23 {
24 if ($this->uuid === "")
25 throw new Exception("Falta el uuid.");
26 if ($this->nombre === "")
27 throw new Exception("Falta el nombre.");
28 }
29}
30

2. srv / modelo / recuperaPasatiempo.php

1<?php
2
3require_once __DIR__ . "/../../lib/php/BAD_REQUEST.php";
4require_once __DIR__ . "/../../lib/php/validaJson.php";
5require_once __DIR__ . "/../../lib/php/ProblemDetails.php";
6require_once __DIR__ . "/Pasatiempo.php";
7
8function recuperaPasatiempo($objeto)
9{
10
11 $objeto = validaJson($objeto);
12
13 if (!isset($objeto->nombre) || !is_string($objeto->nombre))
14 throw new ProblemDetails(
15 status: BAD_REQUEST,
16 title: "El nombre debe ser texto.",
17 type: "/error/nombreincorrecto.html",
18 );
19
20 if (!isset($objeto->uuid) || !is_string($objeto->uuid))
21 throw new ProblemDetails(
22 status: BAD_REQUEST,
23 title: "El uuid debe ser texto.",
24 type: "/error/uuidincorrecto.html",
25 );
26
27 if (!isset($objeto->eliminado) || !is_bool($objeto->eliminado))
28 throw new ProblemDetails(
29 status: BAD_REQUEST,
30 title: "El campo eliminado debe ser booleano.",
31 type: "/error/eliminadoincorrecto.html",
32 );
33
34 if (!isset($objeto->modificacion) || !is_int($objeto->modificacion))
35 throw new ProblemDetails(
36 status: BAD_REQUEST,
37 title: "La modificacion debe ser número.",
38 type: "/error/modificacionincorrecta.html",
39 );
40
41 return new Pasatiempo(
42 uuid: $objeto->uuid,
43 nombre: $objeto->nombre,
44 modificacion: $objeto->modificacion,
45 eliminado: $objeto->eliminado
46 );
47}
48