1 | <?php |
2 | |
3 | require_once __DIR__ . "/../lib/php/recuperaJson.php"; |
4 | require_once __DIR__ . "/../lib/php/devuelveJson.php"; |
5 | require_once __DIR__ . "/../lib/php/ProblemDetails.php"; |
6 | require_once __DIR__ . "/../lib/php/devuelveProblemDetails.php"; |
7 | require_once __DIR__ . "/../lib/php/devuelveErrorInterno.php"; |
8 | require_once __DIR__ . "/modelo/Pasatiempo.php"; |
9 | require_once __DIR__ . "/modelo/recuperaPasatiempo.php"; |
10 | require_once __DIR__ . "/bd/pasatiempoAgrega.php"; |
11 | require_once __DIR__ . "/bd/pasatiempoBusca.php"; |
12 | require_once __DIR__ . "/bd/pasatiempoConsultaNoEliminados.php"; |
13 | require_once __DIR__ . "/bd/pasatiempoModifica.php"; |
14 | |
15 | try { |
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 |
1 | <?php |
2 | |
3 | require_once __DIR__ . "/../modelo/Pasatiempo.php"; |
4 | |
5 | class 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 |
1 | <?php |
2 | |
3 | require_once __DIR__ . "/Bd.php"; |
4 | require_once __DIR__ . "/../modelo/Pasatiempo.php"; |
5 | |
6 | function 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 |
1 | <?php |
2 | |
3 | require_once __DIR__ . "/../../lib/php/fetch.php"; |
4 | require_once __DIR__ . "/Bd.php"; |
5 | require_once __DIR__ . "/../modelo/Pasatiempo.php"; |
6 | |
7 | function 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 |
1 | <?php |
2 | |
3 | require_once __DIR__ . "/../../lib/php/fetchAll.php"; |
4 | require_once __DIR__ . "/Bd.php"; |
5 | require_once __DIR__ . "/../modelo/Pasatiempo.php"; |
6 | |
7 | /** @return Pasatiempo[] */ |
8 | function 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 |
1 | <?php |
2 | |
3 | require_once __DIR__ . "/Bd.php"; |
4 | require_once __DIR__ . "/../modelo/Pasatiempo.php"; |
5 | |
6 | function 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 |
1 | <?php |
2 | |
3 | class 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 |
1 | <?php |
2 | |
3 | require_once __DIR__ . "/../../lib/php/BAD_REQUEST.php"; |
4 | require_once __DIR__ . "/../../lib/php/validaJson.php"; |
5 | require_once __DIR__ . "/../../lib/php/ProblemDetails.php"; |
6 | require_once __DIR__ . "/Pasatiempo.php"; |
7 | |
8 | function 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 |