1 | <?php |
2 | |
3 | require_once __DIR__ . "/../lib/php/ejecutaServicio.php"; |
4 | require_once __DIR__ . "/../lib/php/recuperaJson.php"; |
5 | require_once __DIR__ . "/../lib/php/devuelveJson.php"; |
6 | require_once __DIR__ . "/../lib/php/ProblemDetails.php"; |
7 | require_once __DIR__ . "/../lib/php/devuelveProblemDetails.php"; |
8 | require_once __DIR__ . "/../lib/php/devuelveErrorInterno.php"; |
9 | require_once __DIR__ . "/modelo/TABLA_PASATIEMPO.php"; |
10 | require_once __DIR__ . "/modelo/validaPasatiempo.php"; |
11 | require_once __DIR__ . "/bd/pasatiempoAgrega.php"; |
12 | require_once __DIR__ . "/bd/pasatiempoBusca.php"; |
13 | require_once __DIR__ . "/bd/pasatiempoConsultaNoEliminados.php"; |
14 | require_once __DIR__ . "/bd/pasatiempoModifica.php"; |
15 | |
16 | ejecutaServicio(function () { |
17 | |
18 | $lista = recuperaJson(); |
19 | |
20 | if (!is_array($lista)) { |
21 | $lista = []; |
22 | } |
23 | |
24 | foreach ($lista as $modelo) { |
25 | $modeloEnElCliente = validaPasatiempo($modelo); |
26 | $modeloEnElServidor = pasatiempoBusca($modeloEnElCliente[PAS_ID]); |
27 | |
28 | if ($modeloEnElServidor === false) { |
29 | |
30 | /* CONFLICTO: El modelo no ha estado en el servidor. |
31 | * AGREGARLO solamente si no está eliminado. */ |
32 | if ($modeloEnElCliente[PAS_ELIMINADO] === 0) { |
33 | pasatiempoAgrega($modeloEnElCliente); |
34 | } |
35 | } elseif ( |
36 | $modeloEnElServidor[PAS_ELIMINADO] === 0 |
37 | && $modeloEnElCliente[PAS_ELIMINADO] === 1 |
38 | ) { |
39 | |
40 | /* CONFLICTO: El registro está en el servidor, donde no se ha eliminado, pero |
41 | * ha sido eliminado en el cliente. |
42 | * Gana el cliente, porque optamos por no revivir lo eliminado. */ |
43 | pasatiempoModifica($modeloEnElCliente); |
44 | } else if ( |
45 | $modeloEnElCliente[PAS_ELIMINADO] === 0 |
46 | && $modeloEnElServidor[PAS_ELIMINADO] === 0 |
47 | ) { |
48 | |
49 | /* CONFLICTO: Registros en el servidor y en el cliente. Pueden ser |
50 | * diferentes. |
51 | * GANA FECHA MÁS GRANDE. Cuando gana el servidor, no se hace nada. */ |
52 | if ( |
53 | $modeloEnElCliente[PAS_MODIFICACION] > |
54 | $modeloEnElServidor[PAS_MODIFICACION] |
55 | ) { |
56 | // La versión del cliente es más nueva y prevalece. |
57 | pasatiempoModifica($modeloEnElCliente); |
58 | } |
59 | } |
60 | } |
61 | |
62 | $lista = pasatiempoConsultaNoEliminados(); |
63 | |
64 | devuelveJson($lista); |
65 | }); |
66 |
1 | <?php |
2 | |
3 | class Bd |
4 | { |
5 | |
6 | private static ?PDO $pdo = null; |
7 | |
8 | static function pdo(): PDO |
9 | { |
10 | if (self::$pdo === null) { |
11 | self::$pdo = new PDO( |
12 | // cadena de conexión |
13 | "sqlite:sincronizacion.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 TEXT NOT NULL, |
25 | PAS_NOMBRE TEXT NOT NULL, |
26 | PAS_MODIFICACION INTEGER NOT NULL, |
27 | PAS_ELIMINADO INTEGER NOT NULL, |
28 | CONSTRAINT PAS_PK |
29 | PRIMARY KEY(PAS_ID), |
30 | CONSTRAINT PAS_ID_NV |
31 | CHECK(LENGTH(PAS_ID) > 0), |
32 | CONSTRAINT PAS_NOM_NV |
33 | CHECK(LENGTH(PAS_NOMBRE) > 0) |
34 | )' |
35 | ); |
36 | } |
37 | |
38 | return self::$pdo; |
39 | } |
40 | } |
41 |
1 | <?php |
2 | |
3 | require_once __DIR__ . "/../../lib/php/validaNombre.php"; |
4 | require_once __DIR__ . "/../../lib/php/insert.php"; |
5 | require_once __DIR__ . "/Bd.php"; |
6 | require_once __DIR__ . "/../modelo/TABLA_PASATIEMPO.php"; |
7 | require_once __DIR__ . "/../modelo/validaId.php"; |
8 | |
9 | /** |
10 | * @param array{ |
11 | * PAS_ID: string, |
12 | * PAS_NOMBRE: string, |
13 | * PAS_MODIFICACION: int, |
14 | * PAS_ELIMINADO: int |
15 | * } $modelo |
16 | */ |
17 | function pasatiempoAgrega(array $modelo) |
18 | { |
19 | validaId($modelo[PAS_ID]); |
20 | validaNombre($modelo[PAS_NOMBRE]); |
21 | insert(pdo: Bd::pdo(), into: PASATIEMPO, values: $modelo); |
22 | } |
23 |
1 | <?php |
2 | |
3 | require_once __DIR__ . "/../../lib/php/selectFirst.php"; |
4 | require_once __DIR__ . "/Bd.php"; |
5 | require_once __DIR__ . "/../modelo/TABLA_PASATIEMPO.php"; |
6 | |
7 | /** |
8 | * @return false | array{ |
9 | * PAS_ID: string, |
10 | * PAS_NOMBRE: string, |
11 | * PAS_MODIFICACION: int, |
12 | * PAS_ELIMINADO: int |
13 | * } |
14 | */ |
15 | function pasatiempoBusca(string $id): false|array |
16 | { |
17 | return selectFirst( |
18 | pdo: Bd::pdo(), |
19 | from: PASATIEMPO, |
20 | where: [PAS_ID => $id] |
21 | ); |
22 | } |
23 |
1 | <?php |
2 | |
3 | require_once __DIR__ . "/../../lib/php/select.php"; |
4 | require_once __DIR__ . "/Bd.php"; |
5 | require_once __DIR__ . "/../modelo/TABLA_PASATIEMPO.php"; |
6 | |
7 | /** |
8 | * @return array{ |
9 | * PAS_ID: string, |
10 | * PAS_NOMBRE: string, |
11 | * PAS_MODIFICACION: int, |
12 | * PAS_ELIMINADO: int |
13 | * }[] |
14 | */ |
15 | function pasatiempoConsultaNoEliminados() |
16 | { |
17 | return select( |
18 | pdo: Bd::pdo(), |
19 | from: PASATIEMPO, |
20 | where: [PAS_ELIMINADO => 0], |
21 | orderBy: PAS_NOMBRE |
22 | ); |
23 | } |
24 |
1 | <?php |
2 | |
3 | require_once __DIR__ . "/../../lib/php/validaNombre.php"; |
4 | require_once __DIR__ . "/../../lib/php/update.php"; |
5 | require_once __DIR__ . "/Bd.php"; |
6 | require_once __DIR__ . "/../modelo/TABLA_PASATIEMPO.php"; |
7 | require_once __DIR__ . "/../modelo/validaId.php"; |
8 | |
9 | /** |
10 | * @param array{ |
11 | * PAS_ID: string, |
12 | * PAS_NOMBRE: string, |
13 | * PAS_MODIFICACION: int, |
14 | * PAS_ELIMINADO: int |
15 | * } $modelo |
16 | */ |
17 | function pasatiempoModifica(array $modelo) |
18 | { |
19 | validaId($modelo[PAS_ID]); |
20 | validaNombre($modelo[PAS_NOMBRE]); |
21 | update( |
22 | pdo: Bd::pdo(), |
23 | table: PASATIEMPO, |
24 | set: $modelo, |
25 | where: [PAS_ID => $modelo[PAS_ID]] |
26 | ); |
27 | } |
28 |
1 | <?php |
2 | |
3 | const PASATIEMPO = "PASATIEMPO"; |
4 | const PAS_ID = "PAS_ID"; |
5 | const PAS_NOMBRE = "PAS_NOMBRE"; |
6 | const PAS_MODIFICACION = "PAS_MODIFICACION"; |
7 | const PAS_ELIMINADO = "PAS_ELIMINADO"; |
8 |
1 | <?php |
2 | |
3 | function validaId(string $id) |
4 | { |
5 | if ($id === "") |
6 | throw new ProblemDetails( |
7 | status: BAD_REQUEST, |
8 | title: "Falta el id.", |
9 | type: "/error/faltaid.html", |
10 | ); |
11 | } |
12 |
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__ . "/TABLA_PASATIEMPO.php"; |
7 | |
8 | function validaPasatiempo($objeto) |
9 | { |
10 | |
11 | $objeto = validaJson($objeto); |
12 | |
13 | if (!isset($objeto->PAS_ID) || !is_string($objeto->PAS_ID)) |
14 | throw new ProblemDetails( |
15 | status: BAD_REQUEST, |
16 | title: "El id debe ser texto.", |
17 | type: "/error/idincorrecto.html", |
18 | ); |
19 | |
20 | if (!isset($objeto->PAS_NOMBRE) || !is_string($objeto->PAS_NOMBRE)) |
21 | throw new ProblemDetails( |
22 | status: BAD_REQUEST, |
23 | title: "El nombre debe ser texto.", |
24 | type: "/error/nombreincorrecto.html", |
25 | ); |
26 | |
27 | if (!isset($objeto->PAS_MODIFICACION) || !is_int($objeto->PAS_MODIFICACION)) |
28 | throw new ProblemDetails( |
29 | status: BAD_REQUEST, |
30 | title: "La modificacion debe ser número.", |
31 | type: "/error/modificacionincorrecta.html", |
32 | ); |
33 | |
34 | if (!isset($objeto->PAS_ELIMINADO) || !is_int($objeto->PAS_ELIMINADO)) |
35 | throw new ProblemDetails( |
36 | status: BAD_REQUEST, |
37 | title: "El campo eliminado debe ser entero.", |
38 | type: "/error/eliminadoincorrecto.html", |
39 | ); |
40 | |
41 | return [ |
42 | PAS_ID => $objeto->PAS_ID, |
43 | PAS_NOMBRE => $objeto->PAS_NOMBRE, |
44 | PAS_MODIFICACION => $objeto->PAS_MODIFICACION, |
45 | PAS_ELIMINADO => $objeto->PAS_ELIMINADO |
46 | ]; |
47 | } |
48 |