¿Qué es esta página?

Esta materia formaba parte de la antigua carrera de Analista de Sistemas de Computación. Se dictó por última vez durante el ciclo lectivo 2017.

Permanece online únicamente para que sirva como material de consulta para los estudiantes que adeuden el examen final.

Solución TP Funciones

Les dejo para descargar el TP de funciones resuelto (ambas comisiones).

TP Final

Les dejo la consigna del TP final.

Agenda

Viernes 3/11: Última clase para desarrollar el TP (ambas comisiones).

Domingo 5/11: Último día para enviar por mail la versión final del TP

Defensa del TP y recuperatorio del examen escrito de funciones:

  • - 2do 2da: Miércoles 8/11, 20hs
  • - 2do 3ra: Lunes 13/11, 20hs

Jueves 16/11, 21:30hs (ambas comisiones):

  • - Recuperatorio de la defensa del TP.
  • - Recuperatorio del examen escrito
  • - Recuperatorio de los contenidos del 1er cuatrimestre (solamente para quienes ya tengan el 2do cuatrimestre aprobado el día 8 ó 13 de noviembre).

Modificación y baja a MySQL con PDO

Comenzaremos realizando una consulta simple a la BD:

SELECT numero, nombre, equipo FROM ciclistas;

Una vez realizada la consulta, mostraremos los datos en una tabla. Le agregaremos a cada ciclista un enlace que permitirá modificar sus datos o eliminarlo. Para eso, un breve recordatorio de GET en HTML: el enlace

< a href="ejemplo.php?x=3">...</a>

"apunta" al archivo ejemplo.php, pero envía además por GET la variable $_GET['x'] con un valor de 3, en la parte resaltada en negrita.

Hagamos entonces el código que necesitamos:

<?php require('bd.php'); $pdo = conectar(); //La función conectar() está definida en bd.php, y conecta a la BD, //retornando un objeto de clase PDO con la conexión. $consulta = $pdo->prepare("SELECT numero, nombre, equipo FROM ciclistas;"); //Aqui no hay parámetros, puede ejecutarse esta consulta con // $pdo->query(), lo omitimos por brevedad. Leer más $consulta -> execute(); $resultado = $consulta->fetchAll(PDO::FETCH_ASSOC); //Mostramos los resultados en una tabla: echo '<table><tr>'; echo '<th>Nombre<th>Equipo</th>'; echo '<th>Baja<th>Modificación</th></tr>'; foreach ($resultado as $unCiclista) { echo '<tr>'; echo '<td>'.$unCiclista['nombre'].'</td>'; echo '<td>'.$unCiclista['equipo'].'</td>'; //Celda con el link para eliminar: echo '<td><a href="baja.php?id='.$unCiclista['numero'].'">Borrar</a></td>'; //Celda con el link para modificar: echo '<td><a href="modificar.php?id='.$unCiclista['numero'].'">Modificar</a></td>'; echo '</tr>'; } echo '</table>'; ?>

El código anterior nos da este resultado:
Captura de pantalla

Baja

Ahora tenemos que hacer el archivo baja.php, que recibirá por GET el número de ciclista que queremos eliminar.

<?php require('bd.php'); $pdo = conectar(); //Preparamos la eliminacion $eliminacion=$pdo->prepare("DELETE FROM ciclistas WHERE numero=:numeroRecibido"); //Vinculamos el parámetro :numeroRecibido con el id recibido por GET: $eliminacion->bindValue(':numeroRecibido',$_GET['id']); //Ejecutamos la eliminación, mostrando un mensaje de éxito o error según corresponda: if($eliminacion->execute()) { echo "Ciclista eliminado correctamente"; } else { echo "Error al eliminar el ciclista"; } ?>

Modificación

Este script también recibe por GET el numero del ciclista, pero debemos presentarle al usuario un formulario para que pueda cargar los nuevos datos del ciclista. (Archivo modificar.php)

<?php require('bd.php'); $pdo = conectar(); //Consultamos los datos actuales del ciclista: $consulta=$pdo->prepare("SELECT numero, nombre, edad, equipo FROM ciclistas WHERE numero=:numeroRecibido"); //Vinculamos el parámetro :numeroRecibido con el id recibido por GET: $consulta->bindValue(':numeroRecibido',$_GET['id']); $consulta->execute(); $datos = $consulta->fetchAll(PDO::FETCH_ASSOC); //Armamos un formulario para que el usuario ingrese los nuevos datos: echo '<form action="guardarModif.php" method="post">'; echo "<input name='numero' type='hidden' value='{$_GET['id']}'>"; echo "Nombre: <input name='nombre' value='{$datos[0]['nombre']}'><br>"; echo "Edad: <input name='edad' type='number' value='{$datos[0]['edad']}'><br>"; echo "Equipo: <input name='equipo' value='{$datos[0]['equipo']}'><br>"; echo '<input type="submit" value="Modificar datos">'; echo '</form>';

El código anterior presenta un formulario que tiene, por defecto, los valores actuales del ciclista, para que el usuario modifique los datos que desee:
Captura de pantalla

Nos queda, por último, hacer el código que cargue la modificacion a la BD (archivo guardarModif.php)

<?php require('bd.php'); $pdo = conectar(); //Preparamos la sentencia de modificacion: $modificacion=$pdo->prepare("UPDATE ciclistas SET nombre = :nombre, edad = :edad, equipo = :equipo WHERE numero=:numero"); //Vinculamos los parámetros con los datos recibidos por POST: $modificacion->bindValue(':nombre',$_POST['nombre']); $modificacion->bindValue(':edad',$_POST['edad']); $modificacion->bindValue(':equipo',$_POST['equipo']); $modificacion->bindValue(':numero',$_POST['numero']); //Ejecutamos la modificación, mostrando un mensaje de éxito o error según corresponda: if($modificacion->execute()) { echo "Datos modificados correctamente"; } else { echo "Error al modificar los datos del ciclista"; } ?>

Ejercicio propuesto

Hacer un listado de modificación y baja de los equipos. El enlace para la baja debe aparecer solamente en aquellos equipos que no tienen inscripto ningún ciclista.

Consultas a MySQL con PDO

Vamos a suponer que recibimos por GET una edad, y queremos ver una lista con el nombre y el equipo de los ciclistas menores que la edad recibida.

Por ejemplo, si recibiéramos 30, querríamos ejecutar la consulta: SELECT nombre, equipo FROM ciclistas WHERE edad < 30;

Entonces, procederíamos del siguiente modo:

<?php require('bd.php'); $pdo = conectar(); //La función conectar() está definida en bd.php, y conecta a la BD, //retornando un objeto de clase PDO con la conexión. //Preparamos la consulta: $consulta = $pdo->prepare("SELECT nombre, equipo FROM ciclistas WHERE edad < :edadTope;"); //Vinculamos el valor del parámetro :edadTope a la variable $edadTope. (Explicado al final) $consulta->bindParam(':edadTope',$edadTope,PDO::PARAM_INT); $edadTope = $_GET['formularioEdad']; //Ejecutamos la consulta $consulta->execute(); //Guardamos lo que "trajo" la consulta en el array $resultado $resultado = $consulta->fetchAll(PDO::FETCH_ASSOC); //$resultado es un array bidimensional, como se ve más abajo: echo '<pre>'; print_r($resultado); echo '</pre>';

El resultado obtenido es el siguiente:

Array( [0] => Array( [nombre] => Alex Zulle [equipo] => ONCE ) [1] => Array( [nombre] => Mario Cipollini [equipo] => Mercatone Uno ) .... etc, etc, ... )

Es decir: Retorna un array bidimensional (una matriz), en donde las filas están numeradas de 0 en adelante (indexado), y las columnas tienen por nombre el campo de la tabla (lo que aparece después de la palabra SELECT en la consulta). Podríamos representarlo así:

  [nombre] [equipo]
[0] Alex Zulle ONCE
[1] Mario Cipollini Mercatone Uno
[...] ... ...

Si quisiéramos que las columnas de la matriz no tengan nombre sino un número (de 0 en adelante), deberíamos cambiar la línea que está en rojo, del siguiente modo:

$resultado = $consulta->fetchAll(PDO::FETCH_NUM);

De este modo, $resultado contiene una matriz con el resultado de la consulta realizada. Podemos "procesar" ese resultado desde php como queramos, por ejemplo, mostrándolo en una tabla:

//El final del programa anterior, en lugar del print_r, tendría: //Mostramos el resultado en una tabla: echo '<table><tr><th>Nombre</th><th>Equipo</th></tr>'; foreach ($resultado as $unCiclista) { echo '<tr>'; echo '<td>' . $unCiclista['nombre'] . '</td>'; echo '<td>' . $unCiclista['equipo'] . '</td>'; echo '</tr>'; } echo '</table>';

bindParam y bindValue

En el código anterior, puede haber llamado la atención lo siguiente:

//Preparamos la consulta: $consulta = $pdo->prepare("SELECT nombre, equipo FROM ciclistas WHERE edad < :edadTope;"); //Vinculamos el valor del parámetro :edadTope a la variable $edadTope. $consulta->bindParam(':edadTope',$edadTope,PDO::PARAM_INT); $edadTope = $_GET['formularioEdad'];

Si nos fijamos, en la línea resaltada en negrita, se asocia el parámetro :edadTope con la variable $edadTope, aún cuando esta variable todavía no existe. En el artículo anterior, habíamos usado bindValue. ¿Cuál es la diferencia?

  • Con bindValue, asociamos el parámetro a un valor. Si utilizamos una variable, estamos dándole al parámetro el valor que tenga la variable en ese momento.
  • Con bindParam, asociamos el parámetro a una variable. No estamos dándole al parámetro el valor actual de la variable, sino el valor que tendrá la variable al momento de ejecutar la consulta, con $consulta->execute()

Cuando usamos bindParam, además del parámetro y la variable, debemos indicar el tipo de dato. Por ejemplo:

  • Si es un número: $consulta->bindParam(':edadTope',$edadTope,PDO::PARAM_INT);
  • Si es una cadena: $consulta->bindParam(':nombre',$nombre,PDO::PARAM_STR);

Conexión y alta a MySQL con PDO

Para que nuestros scripts PHP puedan interactuar con MySQL, utilizaremos la clase PDO, cuyo uso intentaremos explicar a continuación.

Siguiendo con nuestro ejemplo, daremos de alta un nuevo equipo en la tabla "equipos" de la BD de ciclismo. Dicha tabla tiene solamente dos campos:

  • - nombre_equipo (nombre del equipo, cadena, clave principal)
  • - director (nombre del director del equipo, cadena)

En primer lugar, creamos un formulario HTML sin mayores complicaciones:

<!DOCTYPE html> <html lang="es"> <head> <meta charset="utf-8" /> <title>Alta de Equipo</title> </head> <body> <h1>Alta de equipos</h1> <form action="insertequipo.php" method="post"> Equipo: <input name="nombre_equipo"><br> Director: <input name="director"><br> <input type="submit" value="Agregar equipo"> </body></html>

Los datos introducidos en este formulario, son procesados por el siguiente archivo, su funcionamiento se explica en el mismo código:

<?php function conectar() { //Esta función conviene que esté en un archivo aparte, para reutilizarla en todos los archivos. //Datos del servidor $servidor = 'localhost'; $nombreBD = 'ciclismo'; $usuario = 'miUsuario'; $clave = 'miClave'; try { //Instanciamos un nuevo objeto de la clase PDO, y lo guardamos en $pdo. $pdo = new PDO("mysql:host=$servidor;dbname=$nombreBD;charset=utf8", $usuario, $clave); //Retornamos el objeto $pdo. return $pdo; } catch (PDOException $e) { //Si falló la conexión del try, lanza una excepción que capturamos aquí. echo "¡Error!" . $e->getMessage() . "<br>"; } } $equipo = $_POST['nombre_equipo']; $director = $_POST['director']; //Nos conectamos a la BD $pdo = conectar(); //Preparamos la sentencia INSERT, con dos parámetros, a los que llamamos :e y :d $insercion = $pdo -> prepare("INSERT INTO equipos (nombre_equipo, director) VALUES (:e, :d);"); //El símbolo -> significa que la clase PDO (de la que $pdo es una //instancia), tiene un método llamado prepare, que invocamos en este momento. //Le asignamos valor a los parámetros :e y :d $insercion -> bindValue(':e',$equipo); $insercion -> bindValue(':d',$director); //Ejecutamos la sentencia preparada antes, y así insertamos el nuevo equipo: if ($insercion -> execute()) { //Si la inserción fue exitosa: echo "El equipo fue agregado"; } else { echo "Error al agregar el equipo"; }

Ejercicio: Realizar el alta de ciclistas y de etapas.

Resultados TP funciones

Resultados de 2do 2da:
Aprobados: Franco A. - María Laura D. - Uriel P. - Nicolás P. - Sofía R. - Micaela M. - Leandro R. - Nicolás Br. - Leonardo G. - Fernando S.
No aprobados: Luis L. - Soledad E. - Matias O.T. - Fernando J. - Laura C. - Ignacio E. - Facundo I. - Paula J. - Natalia P.

Resultados de 2do 3ra:
Aprobados: Hans S. - Rubén S. - Martín A. - Nicolás D. - Álvaro M. - Nicolás Bl. - Matías A. - Marcelo P. - Lisandro L. - Cristian G. - Kevin F.
No aprobados:Elián V. - Nicolás Bon. - Leonel Q. - Maximiliano B. - Lucas A. - Maximiliano M. - Mauricio A.

Los que no figuran en estas listas, estuvieron ausentes.

Consultas a varias tablas

Para ejemplificar las consultas a más de una tabla, utilizaremos las siguientes tablas.

Tabla ciclista2        Tabla etapa2
numero nombre edad        numero_etapa kms ganador
1Fulano25        150 2
2Mengano27       2 203
3Magoya28       360 2
4Cadorna21      445 NULL

Noten que hay dos ciclistas (el 1 y el 4) que no ganaron ninguna etapa. Además, la última etapa aun no se corrió, por lo que no tiene ganador.

Supongamos que queremos ver el número de todas las etapas junto con el nombre de su ganador. Para eso realizaremos la siguiente consulta:

SELECT numero_etapa, nombre FROM etapa2 JOIN ciclista2 ON etapa2.ganador=ciclista2.numero;

La segunda línea significa que a la tabla etapa2, se le unen los datos de la tabla ciclista2, a través de los campos ganador y numero.

La consulta mostrará:

numero_etapanombre
1Mengano
2Magoya
3Mengano

Nótese que no aparecen las etapas que no tienen ganador, ni tampoco los ciclistas que no han ganado ninguna etapa.

Si queremos que aparezcan las etapas que no tienen ganador, debemos hacer un LEFT JOIN, que hace que aparezcan todas las filas de la tabla que está en el FROM, con el dato correspondiente a la otra tabla. Donde no haya tal correspondencia, aparecerá NULL:

SELECT numero_etapa, nombre FROM etapa2 LEFT JOIN ciclista2 ON etapa2.ganador=ciclista2.numero;
numero_etapanombre
1Mengano
2Magoya
3Mengano
4NULL

Si queremos que aparezcan todos los ciclistas, incluso los que no han ganado ninguna etapa, debemos hacer RIGHT JOIN, que hace que aparezcan todas las filas de la tabla que está en el JOIN, con el dato correspondiente a la otra tabla. Donde no haya tal correspondencia, aparecerá NULL:

SELECT numero_etapa, nombre FROM etapa2 RIGHT JOIN ciclista2 ON etapa2.ganador=ciclista2.numero;
numetro_etapanombre
1Mengano
2Magoya
3Mengano
NULLFulano
NULLCadorna

Si queremos unir más de dos tablas, basta con repetir la sentencia JOIN, por ejemplo: SELECT numero_etapa, nombre, equipo.nombre_equipo, director FROM etapa2 JOIN ciclista2 ON etapa2.ganador=ciclista2.numero JOIN equipo ON ciclista2.equipo=equipo.nombre_equipo;

numero_etapanombrenombre_equipodirector
1MenganoEquipoAPedro
2MagoyaEquipoBMarta
3MenganoEquipoAPedro

Ejercicios propuestos

Con las tablas de ciclismo importadas la clase anterior, realizar los siguientes ejercicios:

  1. 21) Listado de ciclistas, incluyendo nombre, edad, equipo y director de equipo.
  2. 22) Repetir la consulta anterior, pero ordenarlos alfabéticamente por equipo, y dentro de cada equipo, ordenarlos por edad de mayor a menor
  3. 23) Listado de todas las etapas corridas, con número de etapa, salida, llegada, nombre del ganador, y edad del ganador, ordenado por este último dato de mayor a menor.
  4. 24) Rehacer la consulta anterior, pero agregando ahora las etapas que no se corrieron
  5. 25) ¿Qué edad tiene el ganador más joven?
  6. 26) ¿Cómo se llama y a qué equipo pertenece el ganador de la etapa 11?
  7. 27) Listar todos los datos de las etapas, incluyendo nombre del ganador, nombre de su equipo y de su director.

Descargar solución propuesta

Ejercicio de consultas SQL

Importar este archivo en la BD. (Las nuevas tablas se sumarán a las que ya había).

El modelo de datos generados es el siguiente:

Modelo de datos

Realizar los siguientes ejercicios. Les recomiendo ir guardando en un archivo de texto la sentencia SQL utilizada en cada caso.

  1. 1)Agregar varios ciclistas a la lista.
  2. 2)Eliminar un ciclista de los agregados en el paso anterior.
  3. 3)El ciclista Tony Rominger solicita que se cambie en la BD su apodo (Tony) por su nombre de pila (Antonio).
  4. 4)Listar todos los datos de todos los ciclistas.
  5. 5)Listar el número y nombre de todos los cilcistas, por orden alfabético del nombre.
  6. 6)Consultar el nombre del ciclista que lleva el número 19.
  7. 7)Listar nombre y número de todos los ciclistas de más de 30 años.
  8. 8)Listar todos los datos de todos los ciclistas del equipo "Mercatone Uno"
  9. 9)Listar número y nombre de todos los ciclistas cuyo nombre de pila sea Angel.
  10. 10)Listar todos los datos de todos los equipos.
  11. 11)Listar todos los datos de los ciclistas del equipo Banesto, menores de 30 años.
  12. 12)Listar el nombre del director del equipo Banesto.
  13. 13)Listar todos los datos de todas las etapas.
  14. 14)Corregir el error en la llegada de la etapa 11.
  15. 15)¿Cuántas etapas son de más de 40 km?
  16. 16)¿Cuál es la etapa más larga?
  17. 17)¿Cuál es la más breve?
  18. 18)Mostrar el total de kilómetros recorridos por los ciclistas.
  19. 19)Mostrar el promedio de kilómetros de las etapas
  20. 20)Mostrar el nombre de cada equipo con la cantidad de ciclistas que lo componen y su promedio de edad. Ordenarlo por este último dato, de mayor a menor.

Descargar solucion

Sentencias SQL básicas

En la siguiente presentación se explican las principales sentencias SQL:

  • * INSERT, para agregar nuevos registros
  • * UPDATE, para modificar registros
  • * DELETE, para eliminar registros
  • * SELECT, para consultas

Aviso: Las tablas usadas como ejemplo en las diapositivas no están en 3FN.

Descargar la presentación en formato pdf

Tipos de datos en Mysql

MySQL soporta numerosos tipos de datos. Veremos solamente los de uso más frecuente. El conjunto completo de tipos de datos puede consultarse en la documentación oficial.

Números enteros:

Para números enteros, se soportan los siguientes tipos:

  • TINYINT [-128;127] con signo; [0;255] sin signo. (1 Byte)
  • SMALLINT[-32768;32767] con signo; [0;65535] sin signo. (2 Bytes)
  • MEDIUMINT [-8.388.608;8.388.607] con signo; [0;16.777.215] sin signo. (3 Bytes)
  • INT [-2.147.483.648;2.147.483.647] con signo; [0;4.294.967.295] sin signo. (4 Bytes)
  • BIGINT [-9.223.372.036.854.775.808 ;9.223.372.036.854.775.807] con signo; [0;18.446.744.073.709.551.615] sin signo. (8 Bytes)

Números decimales:

  • FLOAT: Precisión simple, 4 Bytes.
  • DOUBLE: Precisión doble, 8 Bytes.
  • DECIMAL: Optimizado para guardar una cantidad exacta de dígitos después del punto decimal. Útil para representar dinero, por ejemplo.

Fecha y hora:

  • DATE: Una fecha, en formato AAAA-MM-DD Ej: 2017-08-30 (30 de agosoto de 2017).
  • TIME: Una hora, en formato HHH:MM:SS Ej: 14:35:15 (14hs, 35 min. y 15 seg.).
  • DATETIME: Una combinación de las 2 anteriores. Ej: 2017-08-30 14:35:15
  • TIMESTAMP: Similar al timestamp de Unix, que vimos en php. La diferencia con DATETIME es que al ser agregado un nuevo registro, o modificado uno existente, este campo se actualiza con el timestamp actual.
  • YEAR: Un año, por ejemplo, 1983 o 2047.

Cadenas:

  • CHAR(X) Una cadena de exactamente "X" caracteres. Si la cadena tiene menos de X caracteres, se agregan espacios en blanco; si tiene más, se trunca.
  • VARCHAR(X) Una cadena de hasta "X" caracteres. Si la cadena tiene más de X caracteres, se trunca.
  • TEXT: Texto largo. Para diferentes longitudes de texto existen los tipos: TINYTEXT (hasta 28 caracteres), TEXT (hasta 216), MEDIUMTEXT (hasta 224), y LONGTEXT(hasta 232).

Algunas propiedades:

  • PRIMARY KEY: Define que un campo es la clave principal de una tabla.
  • INDEX: Define que un campo es algún tipo de clave (no primaria), es decir, un índice.
  • UNSIGNED: Indica que un valor numérico no tendrá signo.
  • AUTO_INCREMENT: Indica que un valor entero será autoincrementado (1 para el primer registro, 2 para el segundo, etc.)
  • DEFAULT X: Indica que, si no se establece un valor para el campo, se asignará el valor X.
  • NULL (o NOT NULL): Indica que se permite (o no) que el valor de un campo no esté definido.

Ejercicio: Normalización de BD

Generar una BD en 3ª Forma Normal para el siguiente caso

Una biblioteca guarda, para cada uno de sus préstamos, los siguientes datos:

Numero, nombre, apellido, dirección y teléfono del socio - ISBN del libro - Título del libro - Código del autor del libro - Nombre y apellido del autor del libro - Fecha de nacimiento y de muerte del autor del libro - Tipo de autor(*) - Número de páginas del libro - Cantidad de días por los que se presta el libro - Fecha en la que se presta el libro - Fecha esperada de devolución - Fecha real de devolución

Condiciones:

  • * Cada socio tiene un número único que lo identifica.
  • * Se registra un solo domicilio y un solo teléfono por cada socio.
  • * Cada libro tiene un ISBN único que lo identifica. Suponer que la biblioteca solo tiene un ejemplar de cada libro.
  • * Cada autor se identifica con un número único.
  • * Un libro tiene siempre al menos un autor. Puede tener más de uno. No hay libros de autores desconocidos.
  • * Un socio puede sacar muchos libros, pero solo de a uno por vez (no se le presta un nuevo libro hasta que no haya devuelto el anterior).
  • * Para cada libro, se decide por cuántos días se prestará. Esta cantidad de días no depende del socio, sino del libro.
  • * Un mismo lector puede llevarse varias veces el mismo libro.

Si surgen dudas respecto a otras condiciones, consultar con el docente y registrar por escrito.

(*) Un autor puede ser: autor principal, coautor, prologuista, traductor o compilador. Un mismo autor puede ser, por ejemplo, prologuista de un libro y compilador de otro.

Agregado: Les dejo la solución que se trabajó en clase, en la siguiente imagen.

Modelo de datos de la biblioteca

Rubén Sato, estudiante de 2do3ra, me envió su solución del ejercicio, y me autorizó a compartirla. Descargar

Pueden descargar el archivo para importar la BD en MySQL: Sin relaciones - Agregar relaciones - Completo

Bases de datos

Resumen de teoría

Normalización de una base de datos (ejercicio resuelto)

Práctica: funciones

Ejercicio 11

Programar el contenido del archivo "funciones.php", necesario para que el siguiente código funcione como se espera.

<?php require('funciones.php'); ?> <!DOCTYPE html> <html lang="es-ar"> <head> <meta charset="utf-8"> <title>Funciones</title> </head> <body> <?php $n=$_GET['n']; //La funcion esMultiploTres retorna la cadena 'sí' o la cadena 'no', según corresponda. echo "El número $n " . esMultiploTres($n) . " es múltiplo de tres"; echo "
El múltiplo de tres anterior es: " . anterior_o_siguiente($n, 'ant'); echo "
El múltiplo de tres siguiente es: " . anterior_o_siguiente($n, 'sig'); ?> </body> </html>
Ver solución propuesta

Ejercicio 12

Hacer un programa en PHP que:

  • * reciba una cadena por POST.
  • * indique cuántas vocales, consonantes, espacios y símbolos tiene la cadena.
  • * indique cuántos caracteres tiene en total la cadena.

Se debe utilizar al menos una de las funciones del archivo cuenta.php, cuyo código se muestra al continuación.

<?php function cuenta($cadena, $tipo_buscado="V") { $contador=0; //Recorremos la cadena letra por letra: for($i=0;$i<strlen($cadena);$i++) { if(queEs($cadena[$i])===$tipo_buscado) { $contador++; } } return $contador; } function queEs($letra) { //Cadena con todas las vocales: $vocales = "aeiouáéíóúüAEIOUÁÉÍÓÚÜ"; //Cadena con todas las consonantes: $consonantes = "qwrtypsdfghjklñzxcvbnmQWRTYPSDFGHJKLÑZXCVBNM"; //¿Está $letra en la cadena $vocales? if(strpos($vocales,$letra) !== false) { return "V"; //Ya sabemos que la letra es vocal } //¿Está $letra en la cadena $consonantes? if(strpos($consonantes,$letra) !== false) { return "C"; //Ya sabemos que la letra es consonante } // ¿Es $letra el espacio en blanco? if($letra === ' ') { return "E"; } return "S"; //Ya sabemos que la letra es un símbolo } ?> Ver solución propuesta

Funciones en otro archivo

Para definir funciones en otro archivo, debemos agregar la línea:

include('ruta/al/archivo/funciones.php');

A partir de allí, las funciones definidas en "funciones.php" estarán disponibles en estre archivo.

require funciona de manera similar a include. La diferencia es que, si por algún motivo no se puede acceder al archivo, con include el script intenta continuar. Con require, el script finaliza inmediatamente.

require_once es idéntico a require. La diferencia es que, si el archivo ya ha sido requerido antes, no vuelve a hacerlo. Lo mismo sucede con include_once e include.

Ejercicio 10

Escribir una función que determine si un año es bisiesto. Deberá ingresar un año desde HTML y luego llamar a un script PHP.

Un año es bisiesto si es múltiplo de 4, pero los años múltiplos de 100 no son bisiestos, salvo cuando también son múltiplos de 400 (Ej: 2000 es bisiesto pero 1900 no). Deberá imprimir por pantalla: El año XXXX es bisiesto (o no, según corresponda).

En caso que no sea bisiesto, deberá imprimir cuál es el bisiesto siguiente. Ejemplo:

El año 2015 no es bisiesto. El próximo año bisiesto más cercano es el 2016.

Nota: Deberán crearse 2 funciones distintas, una para saber si el año es bisiesto y otra para calcular el año bisiesto más cercano. Si la función que informa si un año es bisiesto es invocada sin parámetros, se deberá asignar por defecto 2017.

Debe utilizarse el siguiente programa principal:

<?php require('bisiestos.php'); $anio=$_GET['anio']; if($anio==""||$anio==NULL) { //Si el año no está definido, invoca sin parámetros para que tome el año actual. $esBisiesto=bisiesto(); } else { $esBisiesto=bisiesto($anio); } if($esBisiesto) { echo "El año es bisiesto"; } else { echo "El año no es bisiesto, el próximo bisiesto es " . prox($anio); } ?>

Programar otro archivo con la definición de las funciones. En un comentario, indicar cómo se llama el archivo.

Ver solución propuesta

Valores por defecto

Es posible definir funciones que puedan ser invocadas omitiendo alguno de sus parámetros. En estos casos, se debe especificar un valor por defecto para cada uno de los parámetros omitidos. Esto se realiza del siguiente modo:

<?php function ejemplo($a, $b=3, $c="normal") { //El valor por defecto de $b es 3, y de $c, "normal" // ... aquí iría el contenido de la función... } //Invocación 1: $a vale 7; $b, 4 y $c, "especial" ejemplo(7, 4, "especial"); //Invocación 2: $a vale 7; $b, 4 y $c, "normal" (valor por defecto) ejemplo(7,4); //Invocación 3: $a vale 7; $b, 3 y $c, "normal" (valores por defecto) ejemplo(7); ?>

IMPORTANTE: No es posible "pasar" solamente $a y $c. Si se invoca con dos parámetros, se supone que el omitido es el último.

Por lo tanto, todos los parámetros que no tienen valores por defecto deben estar antes de los que sí los tienen.

Ejercicio 9

Escribir un formulario HTML que pida un importe y una cotización.

Luego deberá llamar a un programa PHP que contendrá una función llamada PesosADolares.

Esta función deberá recibir como parámetros los 2 datos ingresados en el formulario HTML. En el supuesto caso que la cotización ingresada sea 0 (porque el usuario desconoce la cotización del día), tendrá que suponer que se ingresó 15 (15$ = 1u$s). Deberá imprimir por pantalla:

La cantidad de $X equivale a u$sY

Luego deberá llamar a otra función CantidadDeBilletes que deberá imprimir la cantidad de billetes de 100 u$s, de 50 u$s, de 20 u$s, de 10 u$s y por último de 1 u$s, para igualar a la cantidad de dólares que le deberá entregar al usuario. No tener en cuenta los decimales.

Ejemplo: (Importe: 2175; Cotización: Desconocida)

La cantidad de 2175 $ equivalen a 145 u$s
1 billetes de 100 u$s
0 billetes de 50 u$s
2 billetes de 20 u$s
0 billetes de 10 u$s
5 billetes de 1 u$s

Ver solución propuesta <?php $pesos=$_GET['pesos']; if(isset($_GET['cotiz'])&&$_GET['cotiz']!=NULL) { //Si la cotización está definida, la envío a la función $dolares=PesosADolares($pesos, $_GET['cotiz']); } else { //Si no, invoco a la función solo con el 1er parámetro, para que tome el 2º por defecto $dolares=PesosADolares($pesos); } echo "<p>La cantidad de \$$pesos equivale a U\$S$dolares</p>" ; CantidadDeBilletes($dolares); function PesosADolares($pesos, $cotiz=15) { return $pesos/$cotiz; } function CantidadDeBilletes($total) { //Recordatorio: La función floor() "redondea para abajo" (trunca) $resto=auxiliarBilletes(floor($total),100); //La función auxiliarBilletes muestra el resultado de la división entera y retorna el resto. $resto=auxiliarBilletes($resto,50); $resto=auxiliarBilletes($resto,20); $resto=auxiliarBilletes($resto,10); echo "$resto billetes de u\$S1"; } function auxiliarBilletes($monto, $billete) { //Muestra el resultado de la división entera. echo "<p>" . floor($monto/$billete) . " billetes de u\$S $billete</p>"; //Retorna el resto a la función CantidadDeBilletes, desde donde fue invocada esta función. return $monto%$billete; } ?>

Variables globales y "estáticas"

Veremos dos casos particulares, que no deberían ser utilizados salvo que hubiera una buena razón para ello.

Variables globales

Se trata de una herramienta muy particular, que no es conveniente utilizar más que como excepción.

La declaración de una variable como global, le permite a una función acceder a una variable que está definida en el programa principal.

Lo veremos con nuestro ejemplo de las primeras clases:

Un comercio realiza descuentos con la siguiente regla:

  • Si la compra no alcanza los $100, no se realiza ningún descuento.
  • Si la compra está entre $100 y $499,99, se descuenta un 10%.
  • Si la compra supera los $500, se descuenta un 15%.

Dado el monto bruto de una compra, indicar cuánto debe pagar el cliente. Hacer la función "descuento", utilizando el siguiente código.

<?php $x=$_GET["precio"]; $precio_final=$x-descuento(); /*La función es invocada sin parámetros. Obtendrá el precio del producto ($x) de forma global.*/ echo "Debe pagar \$$precio_final."; //Definición de la función descuento: function descuento() { //La función no tiene argumentos, el precio de lista se obtiene en forma global: global $x; echo "<p>Precio de lista: $x</p>"; if($x>500) return $x*15/100; if($x<100) return 0; //(No hay descuento) //Si llegó hasta acá, es porque las dos condiciones anteriores dieron falso: return $x*10/100; } ?>

Variables "estáticas"

Las variables estáticas NO son globales, es decir, su ámbito está limitado a la función. Sin embargo, el valor de dichas variables se conserva entre distintas invocaciones a la función. Se comprende mejor con el siguiente ejemplo:

<?php function contador() { //Solamente la primera vez que se invoque a la función, se asigna 0 a $cont static $cont=0; $cont++; echo "<p>Invocaciones a la función: $cont</p>"; //Cuando la función vuelva a ser invocada, $cont conservará su valor actual. } //Ahora, invoco a la función 4 veces: contador(); contador(); contador(); contador(); ?>

El código anterior mostrará:

Invocaciones a la función: 1

Invocaciones a la función: 2

Invocaciones a la función: 3

Invocaciones a la función: 4


Repito: Las variables estáticas no son globales. En el ejemplo anterior, a pesar de que conserva su valor entre una invocación y otra, la variable $cont no está definida en el programa principal.

Más información en la documentación oficial.

Funciones que retornan valores

En la clase anterior, habíamos trabajado con funciones en las que se ejecutaban una serie de sentencias para luego volver al programa principal. Ninguna de esas funciones retornaba un valor al programa principal.

En la mayoría de los casos, desearemos que, terminada la ejecución de la función, ésta retorne algún dato al ámbito desde donde fue invocada.

Para retornar un valor, se utiliza la sentencia return. Es importante destacar que, en el momento en que aparece una sentencia return, la función retorna al ámbito desde donde fue invocada. Si hubieran más sentencias después del return, estas no se ejecutarían nunca. Lo veremos con un ejemplo.

Vuelvo a copiar el enunciado, que es el mismo que antes:

Un comercio realiza descuentos con la siguiente regla:

  • Si la compra no alcanza los $100, no se realiza ningún descuento.
  • Si la compra está entre $100 y $499,99, se descuenta un 10%.
  • Si la compra supera los $500, se descuenta un 15%.

Dado el monto bruto de una compra, indicar cuánto debe pagar el cliente. Hacer la función "descuento", utilizando el siguiente código.

<?php $x=$_GET["monto"]; $precio_final=$x-descuento($x); /*La función descuento es invocada en una resta. Por lo tanto, sabemos que dicha función deberá retornar un número.*/ echo "Debe pagar \$$precio_final."; //Solución: //Definición de la función descuento: function descuento($precio) { if($precio>500) { return $precio*15/100; } if($precio<100) { return 0;} //(No hay descuento) //Si llegó hasta acá, es porque las dos condiciones anteriores dieron falso: return $precio*10/100; } ?>

Ejercicios propuestos

Ejercicio 3

El siguiente programa recibe la longitud de los catetos de un triángulo rectángulo. Completar el programa con la definición de la función.

<?php $cat1=$_GET["cateto1"]; $cat2=$_GET["cateto2"]; echo "La longitud de la hipotenusa es igual a " . hipotenusa($cat1,$cat2); ?> <?php //Solución: function hipotenusa($a,$b) { return sqrt($a*$a+$b*$b); }

Ejercicio 4

Realizar una página Web que le permita al usuario ingresar una cadena. Dicha cadena es procesada por el siguiente programa php, al que le falta la definición de la función "esvocal". Se solicita programar dicha función.

<?php $cadena=$_GET["palabra"]; $cont=0; for($i=0;$i<strlen($cadena);$i++) { if(esvocal($cadena[$i])) {$cont++;} } echo "La cadena tiene $cont vocales.<br>"; //Incluir aquí la definición de la función //Solución: function esvocal($letra) { //Suponemos que son todas minúsculas sin tilde: if($letra=='a'||$letra=='e'||$letra=='i'||$letra=='o'||$letra=='u') { return true; } else {return false;} } //Otra solución, con todas las vocales: function esvocal($letra) { //Hacemos una cadena que tiene todas las vocales: $vocales = 'aeiouáéíóúüAEIOUÁÉÍÓÚÜ'; if(strpos($vocales,$letra) !== false) { //Ver al final *** return true; } else { return false; } //*** La función strpos retorna "false" //si el caracter $letra no está en la cadena $vocales. } ?>

Ejercicio 5

Completar el siguiente programa con la definición de la función

<?php $num=$_GET["num"]; echo "Los divisores de $num son: "; foreach(divisores($num) as $divisor) echo "$divisor "; //Incluir aquí la definición de la función //Solución: /*La función divisores es invocada en un foreach, por lo que resulta evidente que debe retornar un array */ function divisores($x) { //Recorre todos los valores entre 1 y el parámetro $x for($i=1;$i<=$x;$i++) { //Si $i es divisor de $x, lo agrega al array if($x%$i==0) { $lista_de_divisores[]=$i; } } //Retornamos el array completo: return $lista_de_divisores; } ?>

Ejercicio 6

Hacer una página Web que le permita a una persona ingresar su nombre. Luego, llamar a la función saludo(), para que le diga "Buenos días" (5:00 a 12:59hs), "Buenas tardes" (13:00 a 19:59hs), o "Buenas noches" (20:00 a 4:59hs). Completar el siguiente código.

Recordatorio: La función date("H") devuelve la hora actual (un número de 0 a 23).

<?php $nombre=$_GET['nombre']; echo "¡" . saludo() . " , $nombre!"; //Completar aquí con la definición de la función //Solución: function saludo() { $hora=date("H"); if($hora>=5 && $hora<13) return "Buenos días"; if($hora>=13 && $hora<20) return "Buenas tardes"; //Si llegó hasta acá, es porque las dos condiciones anteriores dieron falso: return "Buenas noches"; } ?>

Funciones en PHP

En el paradigma procedural, una función es una especie de subprograma, una serie de instrucciones que realizan una acción determinada en el momento en que se las invoca.

Por ejemplo, podemos pensar una función que simplemente escriba un saludo:

<!DOCTYPE html> <html lang="es-ar"> <head><title>Funciones</title></head> <body> <?php //Definición de la función saludar function saludar() { $saludo="Buenas noches a todos"; // Guarda la cadena en la variable $saludo echo "<h1>¡$saludo!</h1>"; } //Fin de la definición de la función saludar() /*El código anterior no hace nada, hasta tanto la función no sea invocada*/ //Invocación a la función saludar() saludar(); ?> </body> </html>

Como se ha visto, la función saludar() no hace nada, hasta tanto no sea invocada. Cuando esto sucede, imprime el saludo.

¿Cuando conviene usar funciones?

Quizá con el ejemplo anterior no haya quedado claro para qué conviene usar funciones. Hay tres casos en los que es evidente su utilidad:

  • Cuando un mismo conjunto de tareas va a ser ejecutado varias veces: Es evidente que resulta mucho más práctico escribir la función una sola vez, y luego invocarla cuando sea necesario.
  • Por razones de claridad en el código: Si bien lo más importante es que el programa funcione correctamente, son muy pocos los programadores que trabajan solos. Por lo tanto, es necesario escribir el código de tal manera que sea comprendido por el equipo de desarrollo. (Incluso ese "otro" que debe entender fácilmente el código, podemos ser nosotros mismos dentro de un tiempo). La utilización de funciones aporta a la claridad, puesto que divide un problema complejo en varios subproblemas de menor complejidad.
  • Modularidad: Relacionado con lo anterior, siempre es más sencillo el mantenimiento (corrección de errores, futuras adaptaciones, etc) si el programa está dividido en "módulos" que realizan un conjunto acotado y relacionado de tareas.

Ejercicio resuelto

Un comercio realiza descuentos con la siguiente regla:

  • Si la compra no alcanza los $100, no se realiza ningún descuento.
  • Si la compra está entre $100 y $499,99, se descuenta un 10%.
  • Si la compra supera los $500, se descuenta un 15%.

Dado el monto bruto de una compra, indicar cuánto debe pagar el cliente.

Se solicita utilizar esta función:

function descuento($monto,$porcent){ // Definicion de la funcion $con_desc=$monto-$monto*$porcent/100; // Calcula el precio con descuento echo "<p>Precio de lista: \$$monto</p>"; //Imprime echo "<p>Descuento: $porcent%</p>"; echo "<p>Precio con descuento: \$$con_desc</p>"; }

Solución

<?php //Copio y pego la definición de la función, dada en el enunciado: function descuento($monto,$porcent){ $con_desc=$monto-$monto*$porcent/100; // Calcula el precio con descuento echo "<p>Precio de lista: \$$monto</p>"; //Imprime echo "<p>Descuento: $porcent%</p>"; echo "<p>Precio con descuento: \$$con_desc</p>"; } //AQUÍ COMIENZA LA SOLUCIÓN: $sin_descuento = $_GET['sin_descuento']; if($sin_descuento>=500) { //Invoco a la función para que descuente el 15%: descuento($sin_descuento,15); } else if ($sin_descuento<100) { //Invoco a la función para que no descuente nada (0%): descuento($sin_descuento,0); } else { //Invoco a la función para que descuente el 10%: descuento($sin_descuento,10); } ?>

Ver en funcionamiento

Ejercicio propuesto

Hacer una página Web que le permita al usuario ingresar un numero.

Hacer también un programa en php, que:

  • Invoque a una función para que imprima todos los números pares (múltiplos de 2) menores que el número ingresado.
  • Invoque a la misma función, para que esta vez imprima los múltiplos de 3 menores que el número ingresado.
  • Invoque una vez más a la misma función, esta vez para que imprima todos los múltiplos de 4 menores que el número ingresado.

Solución propuesta:

<?php //Definición de la función function multiplos($tope,$multiplo) { //El "for" comienza en $multiplo, y avanza de $multiplo //en $multiplo mientras no supere a $tope for($i=$multiplo;$i<$tope;$i+=$multiplo) { echo "$i "; } echo '<br>'; } //Programa principal $tope=$_GET["numero"]; //Para imprimir los múltiplos de 2: multiplos($tope,2); //Para imprimir los múltiplos de 3: multiplos($tope,3); //Para imprimir los múltiplos de 4: multiplos($tope,4); ?>

Otro ejercicio propuesto:

Escribir una función que reciba la medida de los dos catetos de un triángulo rectángulo, y que muestre la longitud de la hipotenusa. Probar la función, invocándola varias veces con distintos valores.

Recordatorio 1: Teorema de pitágoras: (cateto1)2 + (cateto2)2 = hipotenusa2

Recordatorio 2: La función de php para calcular la raíz cuadrada de $x es sqrt($x);

Solución propuesta:

<?php //Definición de la función function hipotenusa($c1, $c2) { $sumaDeCuadrados = $c1*$c1 + $c2*$c2; $hipot = sqrt($sumaDeCuadrados); echo "<p>La hipotenusa es $hipot</p>"; } //Invoco varias veces a la función, para probar: hipotenusa(3,4); //Debería dar 5 hipotenusa(15,20); ?>

Ver contenidos del 1er cuatrimestre