Hoy veremos un sistema muy básico para gestionar estudiantes, materias y notas/calificaciones usando el lenguaje de programación PHP con la base de datos MySQL.
Más allá de un sistema es como un ejercicio. Se trata de:
Es como la versión básica de un control escolar.
He usado MySQL como motor de base de datos, a través de la extensión MySQLi de PHP. El archivo de conexión se ve así:
<?php
$host = "localhost";
$usuario = "root";
$contrasenia = "";
$base_de_datos = "escuela";
$mysqli = new mysqli($host, $usuario, $contrasenia, $base_de_datos);
if ($mysqli->connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
Recuerda cambiar tus credenciales según sea tu caso. Y no olvides crear la base de datos. Las tablas quedan como a continuación:
CREATE TABLE estudiantes(
id bigint unsigned not null primary key auto_increment,
nombre varchar(255) not null,
grupo varchar(255) not null
);
CREATE TABLE materias(
id bigint unsigned not null primary key auto_increment,
nombre varchar(255) not null
);
CREATE TABLE notas_estudiantes_materias(
id bigint unsigned not null primary key auto_increment,
id_estudiante bigint unsigned not null,
id_materia bigint unsigned not null,
puntaje decimal(9,2) not null,
foreign key (id_estudiante) references estudiantes(id) on delete cascade on update cascade,
foreign key (id_materia) references materias(id) on delete cascade on update cascade
);
El esquema indica que hay 3 tablas. La primera guarda información de los estudiantes, como lo son el id, nombre y grupo. La segunda tabla es la tabla de materias y se encarga de guardar el nombre de las mismas.
Finalmente la última tabla se encarga de unir las 2 primeras tablas, es decir, la de estudiantes y materias, además de agregar un puntaje que indica la calificación o nota del alumno. La misma también se encarga de relacionar las dos tablas con foreign key.
He diseñado este sistema con Bootstrap para evitar darle estilos manuales y ahorrar tiempo. También he separado toda la plantilla en un encabezado, pie y contenido. El contenido depende de cada página.
Por lo tanto el encabezado queda así:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="bootstrap.min.css">
<style>
body {
padding-top: 70px;
}
</style>
<title>Control de notas</title>
</head>
<body>
<nav class="navbar navbar-expand-md navbar-dark bg-success fixed-top">
<a class="navbar-brand" href="https://parzibyte.me/blog">Control de notas</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="mostrar_estudiantes.php">Estudiantes</a>
</li>
<li class="nav-item">
<a class="nav-link" href="mostrar_materias.php">Materias</a>
</li>
</ul>
</div>
<div class="collapse navbar-collapse" id="menu">
<ul class="navbar-nav ml-auto">
<li class="nav-item active">
<a class="nav-link" href="//parzibyte.me/blog">Soporte y ayuda <i class="fa fa-hands-helping"></i></a>
</li>
</ul>
</div>
</nav>
<main class="container-fluid">
Y el pie queda así:
<footer class="px-2 py-2 fixed-bottom bg-dark">
<span class="text-muted">Simple proyecto de sistema escolar creado por
<a class="text-white" href="//parzibyte.me/blog">Parzibyte</a>
|
<a target="_blank" class="text-white" href="https://github.com/parzibyte/escuela_php">
Ver código fuente
</a>
</span>
</footer>
</main>
</body>
</html>
Cuando una página quiere usar esto, simplemente usa include
de PHP.
Tenemos un “controlador” que se encarga de la gestión de los estudiantes. No es como tal un controlador ni un modelo, es una clase que encapsula todos los métodos; pero usamos Programación orientada a objetos con PHP:
<?php
class Estudiante
{
private $nombre, $grupo, $id;
public function __construct($nombre, $grupo, $id = null)
{
$this->nombre = $nombre;
$this->grupo = $grupo;
if ($id) {
$this->id = $id;
}
}
public function guardar()
{
global $mysqli;
$sentencia = $mysqli->prepare("INSERT INTO estudiantes
(nombre, grupo)
VALUES
(?, ?)");
$sentencia->bind_param("ss", $this->nombre, $this->grupo);
$sentencia->execute();
}
public static function obtener()
{
global $mysqli;
$resultado = $mysqli->query("SELECT id, nombre, grupo FROM estudiantes");
return $resultado->fetch_all(MYSQLI_ASSOC);
}
public static function obtenerUno($id)
{
global $mysqli;
$sentencia = $mysqli->prepare("SELECT id, nombre, grupo FROM estudiantes WHERE id = ?");
$sentencia->bind_param("i", $id);
$sentencia->execute();
$resultado = $sentencia->get_result();
return $resultado->fetch_object();
}
public function actualizar()
{
global $mysqli;
$sentencia = $mysqli->prepare("update estudiantes set nombre = ?, grupo = ? where id = ?");
$sentencia->bind_param("ssi", $this->nombre, $this->grupo, $this->id);
$sentencia->execute();
}
public static function eliminar($id)
{
global $mysqli;
$sentencia = $mysqli->prepare("DELETE FROM estudiantes WHERE id = ?");
$sentencia->bind_param("i", $id);
$sentencia->execute();
}
}
Tenemos varias funciones. Por ejemplo, obtenerUno
regresa un estudiante de la base de datos a partir de su id (método estático). El método eliminar
se encarga de borrar el estudiante a partir del id; el de actualizar
se encarga de hacer la operación update y el de guardar se encarga de insertarlo en la base de datos.
También existe el método obtener que devuelve todos los estudiantes como un arreglo usando fetch_all
.
Todos los métodos que mencioné anteriormente ya están en el post de CRUD con MySQLi y PHP.
Cada que se requiere hacer una operación, se crea una instancia de la clase y se invoca al método; o si el método es estático simplemente se invoca sin instanciar.
Para crearlo se reciben los datos a través de un formulario, se crea una instancia y se guarda en la base de datos de MySQL:
<?php
include_once "conexion.php";
include_once "Estudiante.php";
$estudiante = new Estudiante($_POST["nombre"], $_POST["grupo"]);
$estudiante->guardar();
header("Location: mostrar_estudiantes.php");
Como ves, es muy simple gestionar estudiantes en este pequeño sistema escolar con PHP pues creamos un objeto, invocamos a guardar y luego hacemos una redirección.
Para mostrarlos vemos el método usado para listarlos. Obtenemos todos los estudiantes y luego creamos una tabla HTML que muestra 3 botones: editar, eliminar y administrar las notas.
<?php
include_once "conexion.php";
include_once "encabezado.php";
include_once "Estudiante.php";
$estudiantes = Estudiante::obtener();
?>
<div class="row">
<div class="col-12">
<h1>Listado de estudiantes</h1>
<a href="formulario_registro_estudiante.php" class="btn btn-info my-2">Nuevo</a>
</div>
<div class="col-12 table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th>Nombre</th>
<th>Grupo</th>
<th>Notas</th>
<th>Editar</th>
<th>Eliminar</th>
</tr>
</thead>
<tbody>
<?php foreach ($estudiantes as $estudiante) { ?>
<tr>
<td><?php echo $estudiante["nombre"] ?></td>
<td><?php echo $estudiante["grupo"] ?></td>
<td>
<a href="notas_estudiante.php?id=<?php echo $estudiante["id"] ?>" class="btn btn-info">
Notas
</a>
</td>
<td>
<a href="editar_estudiante.php?id=<?php echo $estudiante["id"] ?>" class="btn btn-warning">
Editar
</a>
</td>
<td>
<a href="eliminar_estudiante.php?id=<?php echo $estudiante["id"] ?>" class="btn btn-danger">
Eliminar
</a>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
<?php
include_once "pie.php";
En este caso invocamos al método obtener
, pero directamente desde la clase, pues es un método estático. Lo que importa es que en la tabla tenemos los 3 botones que en realidad son enlaces, y cada uno lleva respectivamente a notas_estudiante.php
, editar_estudiante.php
y eliminar_estudiante.php
pasándole el id a través de la URL.
Para editar mostramos el formulario y lo rellenamos con los datos existentes:
<?php
include_once "conexion.php";
include_once "Estudiante.php";
include_once "encabezado.php";
$estudiante = Estudiante::obtenerUno($_GET["id"]);
?>
<div class="row">
<div class="col-12">
<h1>Editar estudiante</h1>
<form action="actualizar_estudiante.php" method="POST">
<input type="hidden" name="id" value="<?php echo $_GET["id"] ?>">
<div class="form-group">
<label for="nombre">Nombre</label>
<input value="<?php echo $estudiante->nombre ?>" name="nombre" required type="text" id="nombre" class="form-control" placeholder="Nombre">
</div>
<div class="form-group">
<label for="grupo">Grupo</label>
<input value="<?php echo $estudiante->grupo ?>" name="grupo" required type="text" id="grupo" class="form-control" placeholder="Grupo">
</div>
<div class="form-group">
<button class="btn btn-success" type="submit">Guardar</button>
</div>
</form>
</div>
</div>
<?php include_once "pie.php" ?>
En este caso estamos invocando al método obtenerUno
y rellenamos los valores usando el atributo value
de los input de HTML. El action
del formulario es actualizar_estudiante.php
que es donde este sistema escolar actualiza los datos del alumno:
<?php
include_once "conexion.php";
include_once "Estudiante.php";
$estudiante = new Estudiante($_POST["nombre"], $_POST["grupo"], $_POST["id"]);
$estudiante->actualizar();
header("Location: mostrar_estudiantes.php");
Ahora se está invocando al método actualizar
y después de eso se redirecciona al listado.
Para eliminar simplemente se recupera el id de la URL y se borra el registro de la base de datos:
<?php
include_once "conexion.php";
include_once "Estudiante.php";
Estudiante::eliminar($_GET["id"]);
header("Location: mostrar_estudiantes.php");
La administración de materias en este sistema es muy similar a la de estudiantes, así que para no hacer más largo el post, dejaré únicamente la clase que se encarga de gestionarlas. Obviamente puedes explorar todo el código que te dejaré al final del post.
<?php
class Materia
{
private $nombre, $id;
public function __construct($nombre, $id = null)
{
$this->nombre = $nombre;
if ($id) {
$this->id = $id;
}
}
public function guardar()
{
global $mysqli;
$sentencia = $mysqli->prepare("INSERT INTO materias
(nombre)
VALUES
(?)");
$sentencia->bind_param("s", $this->nombre);
$sentencia->execute();
}
public static function obtener()
{
global $mysqli;
$resultado = $mysqli->query("SELECT id, nombre FROM materias");
return $resultado->fetch_all(MYSQLI_ASSOC);
}
public static function obtenerUna($id)
{
global $mysqli;
$sentencia = $mysqli->prepare("SELECT id, nombre FROM materias WHERE id = ?");
$sentencia->bind_param("i", $id);
$sentencia->execute();
$resultado = $sentencia->get_result();
return $resultado->fetch_object();
}
public function actualizar()
{
global $mysqli;
$sentencia = $mysqli->prepare("update materias set nombre = ? where id = ?");
$sentencia->bind_param("si", $this->nombre, $this->id);
$sentencia->execute();
}
public static function eliminar($id)
{
global $mysqli;
$sentencia = $mysqli->prepare("DELETE FROM materias WHERE id = ?");
$sentencia->bind_param("i", $id);
$sentencia->execute();
}
}
Ahora pasemos a algo importante, las notas o calificaciones.
Las notas o calificaciones en este caso no llevan un período relacionado, solo materias. Así que por cada materia que exista en la base de datos hay que mostrar un campo que solicite la calificación, creando un formulario dinámico.
El promedio se calcula llevando una sumatoria de los puntajes y dividiendo entre la longitud del arreglo.
Al final de la tabla se muestra el promedio de todas las calificaciones calculado de manera automática. Las calificaciones se pueden editar en cualquier momento y son por cada estudiante.
<?php
class Nota
{
private $puntaje, $idEstudiante, $idMateria;
public function __construct($puntaje, $idEstudiante, $idMateria)
{
$this->puntaje = $puntaje;
$this->idEstudiante = $idEstudiante;
$this->idMateria = $idMateria;
}
public function guardar()
{
global $mysqli;
// La eliminamos en caso de que exista
$this->eliminar();
// Y siempre la insertamos. No importa si es la primera vez o es una actualización
$sentencia = $mysqli->prepare("INSERT INTO notas_estudiantes_materias
(id_estudiante, id_materia, puntaje)
VALUES
(?, ?, ?)");
$sentencia->bind_param("ssd", $this->idEstudiante, $this->idMateria, $this->puntaje);
$sentencia->execute();
}
public static function obtenerDeEstudiante($idEstudiante)
{
global $mysqli;
$sentencia = $mysqli->prepare("SELECT id, id_estudiante, id_materia, puntaje FROM notas_estudiantes_materias WHERE id_estudiante = ?");
$sentencia->bind_param("i", $idEstudiante);
$sentencia->execute();
$resultado = $sentencia->get_result();
return $resultado->fetch_all(MYSQLI_ASSOC);
}
public static function combinar($materias, $notas)
{
for ($x = 0; $x < count($materias); $x++) {
$materias[$x]["puntaje"] = self::obtenerCalificacion($notas, $materias[$x]["id"]);
}
return $materias;
}
private static function obtenerCalificacion($notas, $idMateria)
{
foreach ($notas as $nota) {
if (intval($nota["id_materia"]) === intval($idMateria)) {
return $nota["puntaje"];
}
}
return 0;
}
public function eliminar()
{
global $mysqli;
$sentencia = $mysqli->prepare("DELETE FROM notas_estudiantes_materias WHERE id_estudiante = ? AND id_materia = ?");
$sentencia->bind_param("ii", $this->idEstudiante, $this->idMateria);
$sentencia->execute();
}
}
En este caso observamos el método guardar
, que primero elimina todas las notas anteriores pertenecientes a ese alumno. Esto se hace así porque:
Se hace de este modo porque así no se tiene que detectar si se debe realizar un insert o un update; siempre se hace un insert y se elimina lo anterior.
Para mostrar las notas en la tabla, con los datos rellenos, se usa el siguiente código. En el mismo observamos que primero obtenemos los datos del estudiante, y luego todas las materias.
Luego obtenemos las notas y finalmente en la línea 10 obtenemos las materias con calificación.
El método combinar es importante y se encarga de combinar las materias con notas. En este caso si ya existe una nota para esa materia, regresa esa nota. Caso contrario, regresa 0. Así que la primera vez que se visite la página, todos los campos estarán en 0, y si luego se guarda algún dato, estará así para la siguiente ocasión.
<?php
include_once "conexion.php";
include_once "encabezado.php";
include_once "Estudiante.php";
include_once "Materia.php";
include_once "Nota.php";
$estudiante = Estudiante::obtenerUno($_GET["id"]);
$materias = Materia::obtener();
$notas = Nota::obtenerDeEstudiante($estudiante->id);
$materiasConCalificacion = Nota::combinar($materias, $notas);
?>
<div class="row">
<div class="col-12">
<h1>Notas de <?php echo $estudiante->nombre ?></h1>
</div>
<div class="col-12 table-responsive">
<table class="table">
<thead>
<tr>
<th>Materia</th>
<th>Puntaje</th>
</tr>
</thead>
<tbody>
<?php
$sumatoria = 0;
foreach ($materiasConCalificacion as $materia) {
$sumatoria += $materia["puntaje"];
?>
<tr>
<td>
<?php echo $materia["nombre"] ?>
</td>
<td>
<form action="modificar_nota.php" method="POST" class="form-inline">
<input type="hidden" value="<?php echo $estudiante->id ?>" name="id_estudiante">
<input type="hidden" value="<?php echo $materia["id"] ?>" name="id_materia">
<input value="<?php echo $materia["puntaje"] ?>" required min="0" name="puntaje" placeholder="Escriba la calificación" type="number" class="form-control">
<button class="btn btn-success mx-2">Guardar</button>
</form>
</td>
</tr>
<?php } ?>
</tbody>
<tfoot>
<tr>
<td>Promedio</td>
<td>
<strong>
<?php
$promedio = $sumatoria / count($materias);
echo $promedio;
?>
</strong>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
<?php
include_once "pie.php";
También es importante notar el método que modifica la calificación o nota, llamado modificar_nota.php
al que le estamos enviando el id de estudiante, id de materia y el nuevo puntaje.
El código del mismo se ve así:
<?php
include_once "conexion.php";
include_once "Nota.php";
$nota = new Nota($_POST["puntaje"], $_POST["id_estudiante"], $_POST["id_materia"]);
$nota->guardar();
header("Location: notas_estudiante.php?id=" . $_POST["id_estudiante"]);
Estamos usando de nuevo a la clase Nota, que no es más que la encarga de gestionar todo lo relacionado a las calificaciones en este sistema de estudiantes, materias y notas con PHP y MySQL.
Así es como termina la presentación de este pequeño sistema de escuela en PHP. El código fuente lo encuentras en mi GitHub.
Te invito a ver otros proyectos que he creado, a leer más sobre PHP y sobre MySQL.
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Ayer estaba editando unos archivos que son servidos con el servidor Apache y al visitarlos…
Esta web usa cookies.
Ver comentarios
buenas, podria tomar tu trabajo prestado para el colegio, lo que te queria pedir es que no logro entender el codigo de boostrap
Buenas. Claro, úselo sin problema. Si necesita alguna guía o ayuda puede contactarme en https://parzibyte.me/#contacto
Me gustaria pedirte este trabajo prestado para la universidad, pero el detalle es que a pesar de estar el codigo fuente, aun no se como ejecutarlo. seria de mucha ayuda tu orientacion.
Hola. Claro, con gusto lo atiendo en https://parzibyte.me/#contacto
Saludos!
Hola amigo
Buen día
Una consulta, si quiero levantar este proyecto tuyo a heroku que debería de tener en cuenta para que funcione?
Hola. Si tiene alguna consulta puede hacérmela llegar en https://parzibyte.me/#contacto