Un práctico para hacer en clases, al final de la unidad. Junta todo lo visto:
variables, condicionales, ciclos, arreglos, include y formularios. Se construye por
pasos; cada uno indica qué parte de la unidad usa y termina con un commit.
El encargo
El Cine del Estrecho, un cine de Punta Arenas con dos salas, quiere un sitio con dos vistas: la cartelera de hoy y un formulario para comprar entradas. No tiene base de datos: las películas viven en un arreglo de PHP.
Archivos del proyecto
cine-del-estrecho/ |-- index.php <- cartelera: una tarjeta por película |-- comprar.php <- formulario de compra y resumen |-- datos.php <- los arreglos $peliculas y $precios `-- partes/ |-- cabecera.php <- <head>, Tailwind y navbar con la página activa `-- pie.php <- footer con el año actual
Cómo se navega
flowchart LR A["index.php<br/>cartelera"] -->|"navbar"| B["comprar.php<br/>formulario"] B -->|"POST con errores"| B B -->|"POST correcto"| C["comprar.php<br/>resumen de la compra"]
Crea un repositorio llamado cine-del-estrecho, clónalo y trabaja dentro de esa
carpeta. Para verlo en el navegador: php -S localhost:8000.
Los datos
Copia este archivo tal cual. Cada película es una función (una sala y una hora).
<?php
// datos.php
$peliculas = [
["titulo" => "El faro del fin del mundo", "genero" => "Drama", "duracion" => 128, "clasificacion" => "+14", "sala" => 1, "hora" => "18:30", "cupos" => 24],
["titulo" => "Pingüinos de Magdalena", "genero" => "Documental", "duracion" => 95, "clasificacion" => "TE", "sala" => 2, "hora" => "16:00", "cupos" => 40],
["titulo" => "Viento blanco", "genero" => "Suspenso", "duracion" => 110, "clasificacion" => "+18", "sala" => 1, "hora" => "22:00", "cupos" => 0],
["titulo" => "La última barcaza", "genero" => "Aventura", "duracion" => 120, "clasificacion" => "+7", "sala" => 2, "hora" => "19:15", "cupos" => 3],
["titulo" => "Noche en Porvenir", "genero" => "Comedia", "duracion" => 102, "clasificacion" => "+14", "sala" => 1, "hora" => "20:45", "cupos" => 18],
["titulo" => "Estrecho", "genero" => "Drama", "duracion" => 141, "clasificacion" => "+7", "sala" => 2, "hora" => "21:30", "cupos" => 0],
];
$precios = [
"general" => 5500,
"estudiante" => 3500,
"adulto-mayor" => 3000,
];duracionestá en minutos.clasificaciones"TE","+7","+14"o"+18".- Una película con
"cupos" => 0está agotada. $precioses un arreglo asociativo: la clave es el tipo de entrada y el valor, su precio.
Paso 1 · Partes compartidas
-
partes/cabecera.phpcon el<!DOCTYPE html>, el<head>(con la meta viewport y Tailwind por CDN) y una navbar con dos enlaces: Cartelera y Comprar - La navbar marca la vista actual con una clase distinta, según una variable
$paginadefinida en cada vista - El
<title>de cada vista sale de una variable$titulo -
partes/pie.phpcon un footer que muestra el año actual condate("Y")y la zona horariaAmerica/Punta_Arenas -
index.phpycomprar.phpcargandatos.phpconrequire_oncee incluyen la cabecera y el pie coninclude, usando__DIR__
Prueba de este paso
Las dos vistas tienen la misma navbar y el mismo pie, y en cada una aparece marcado el enlace correcto.
Commit: partes compartidas y navbar activa.
Paso 2 · La cartelera
Parte 2, Parte 3, Parte 5: tablas desde una lista de registros.
En index.php, una tarjeta por película, generada con foreach:
- Grilla responsiva con Tailwind: una columna en el teléfono, dos en la tablet y tres en el computador
- Cada tarjeta muestra el título, el género, la duración, la clasificación, la sala y la hora
- La duración en horas y minutos:
128se muestra como2 h 8 min, y120como2 h(sin0 min) - La clasificación es una insignia de color, con la clase elegida con
match: verde paraTE, celeste para+7, ámbar para+14y rojo para+18 - El estado de los cupos:
Agotadasi son 0,Últimos N cupossi quedan menos de 5, yN cuposen otro caso - Sobre la grilla, cuántas películas hay en cartelera, con
count() - Si
$peliculasestá vacío, en vez de la grilla apareceNo hay películas esta semana.
Pistas
- Horas y minutos salen de
intdiv($duracion, 60)y$duracion % 60(Operadores).- Calcula la duración y el color al comienzo de cada vuelta, en un bloque
<?php … ?>, y en el HTML solo imprímelos.- El
matchdevuelve las clases completas, como en Clases de Tailwind: siempre completas.
Lo que se ve en dos de las tarjetas:
| Título | Datos | Clasificación | Estado |
|---|---|---|---|
| El faro del fin del mundo | Drama · 2 h 8 min · Sala 1 · 18:30 | +14 (ámbar) | 24 cupos |
| La última barcaza | Aventura · 2 h · Sala 2 · 19:15 | +7 (celeste) | Últimos 3 cupos |
Commit: cartelera generada desde los datos.
Paso 3 · El formulario de compra
Parte 4: ciclos que generan HTML, Parte 5: arreglos y foreach.
En comprar.php, un formulario method="post" que se envía a la misma página, con
cada campo y su <label for>:
- Nombre y correo
- Película: un
<select>generado conforeachque muestra solo las películas con cupos, con el título y la hora. Elvaluede cada opción es la posición de la película en$peliculas - Cantidad: un
<select>del 1 al 10 generado confor - Tipo de entrada: un
<select>generado conforeach ($precios as $tipo => $precio), que muestra el tipo y su precio (por ejemplo,estudiante · $3.500) - Cada
<select>empieza con una opción vacía que pide elegir (Elige una película), salvo la cantidad
Pista: la posición como value
Con
foreach ($peliculas as $posicion => $pelicula),$posicionvale 0, 1, 2… Si la película de la posición 2 está agotada, simplemente no se imprime su<option>: elifva dentro delforeach.
Commit: formulario de compra.
Paso 4 · Validar en el servidor
Parte 6: Validar en el servidor.
Al enviar, PHP revisa cada campo y guarda los errores en $errores, con el name
del campo como clave:
- Nombre: obligatorio, de al menos 3 letras (
mb_strlen) - Correo: con formato válido (
FILTER_VALIDATE_EMAIL) - Película: tiene que existir en
$peliculasy tener cupos - Cantidad: un entero entre 1 y 10, que además no supere los cupos de esa película (
Solo quedan 3 cupos para esa función.) - Tipo: tiene que ser una de las claves de
$precios - Cada error aparece debajo de su campo, en texto
- Si hay errores, el formulario conserva lo escrito y lo elegido en cada
<select> - En la primera visita (sin enviar) no aparece ningún error ni ningún Warning
Pistas
- Todo llega como texto: la película 3 llega como
"3".isset($peliculas[$elegida])dice si existe esa posición, yisset($precios[$tipo])si existe ese tipo: no hace falta recorrer nada.- Para la cantidad, convierte con
(int)antes de comparar.- Revisa los cupos solo si la película es válida: si no existe, no se puede leer
$peliculas[$elegida]["cupos"].- Para conservar lo elegido, compara el
valuede cada opción con lo que llegó, ambos como texto:(string) $posicion === $elegida.
Commit: validacion de la compra en el servidor.
Paso 5 · El resumen de la compra
Si no hay errores, en vez del formulario se muestra el resumen:
- Un saludo con el nombre y el correo al que se envían las entradas
- La película, la sala y la hora
- La cantidad, el tipo y el precio unitario
- Si se compran 5 o más entradas, un 10% de descuento sobre el subtotal
- El total, con punto de miles
- El nombre y el correo se imprimen con
htmlspecialchars()
Por ejemplo, 5 entradas de estudiante para Pingüinos de Magdalena:
| Concepto | Monto |
|---|---|
| 5 entradas de estudiante × $3.500 | $17.500 |
| Descuento por 5 o más entradas | -$1.750 |
| Total | $15.750 |
Los cupos no bajan, y está bien
Sin base de datos, el sitio no puede recordar una compra: si recargas, los cupos vuelven a su valor original. Guardar las compras es lo que se agrega en la Unidad 3.
Commit: resumen de la compra con descuento.
Antes de terminar
Recorre el sitio completo y revisa cada caso:
- La cartelera muestra las 6 películas, con
2 hpara La última barcaza yAgotadaen Viento blanco y Estrecho - Con
$peliculas = [];aparece el mensaje de cartelera vacía (luego vuelve a dejar los datos) - Las películas agotadas no aparecen en el
<select>de la compra - El formulario enviado vacío muestra un error en cada campo y ningún Warning
- 5 entradas para La última barcaza dan el error de cupos y conservan lo elegido
- Con
F12, cambia elvaluede una película a2(Viento blanco) y el de un tipo avip: PHP rechaza los dos - Un nombre como
<b>Ana</b>se ve en el resumen tal cual, sin negrita - La compra del ejemplo da un total de $15.750
- El sitio se ve bien en el teléfono (
F12→ vista de dispositivo)
Cero mensajes de PHP
Ninguna vista puede mostrar un Warning, un Deprecated o un Fatal error, en ningún caso: tampoco con el formulario vacío ni con valores cambiados con
F12.
Si terminas antes
- Resumen de la cartelera: bajo el título, cuántas películas están agotadas y cuántos cupos quedan en total, calculados con un acumulador en un
foreach. - Tabla de precios: en
comprar.php, una tabla con el total de 1 a 10 entradas generales, con el descuento desde 5, como en los ejercicios de ciclos. - Botón por película: en cada tarjeta con cupos, un enlace Comprar a
comprar.php.