Modelo
Para contextualizar, el "modelo" es la "M" en MVC, o sea en Model-View-Controller, el patrón de diseño que implementa Yupp PHP Framework. Más concretamente, el "modelo" es la capa de datos e información de nuestra aplicación, lo que generalmente se denomina "modelo de datos" o "modelo de información.
En Yupp, el modelo de datos se define como clases y relaciones, y todas esas clases y relaciones son persistentes, es decir, instancias de este modelo se pueden almacenar en una base de datos de forma automática, Yupp PHP Framework se encarga de todo el trabajo de generar las tablas, guardar los datos y pedir los datos, lo que simplifica enormemente las tareas del programador.
Los objetos y relaciones antes mencionados se definen utilizando el paradigma de Programación Orientada a Objetos, como todo en Yupp Framework.
En Programación Orientada a Objetos, los objetos pueden estar relacionados de distintas formas, por ejemplo mediante referencias o mediante herencia. La relación de referencia es básicamente una asociación entre dos clases, y la relación de herencia es cuando una clase hereda de otra.
Definición de clases del modelo
La mejor forma de explicar como se define una clase del modelo es con un ejemplo:
class Usuario extends PersistentObject {
function __construct( $args = array() )
{
$this->withTable = "usuarios";
$this->addAttribute("name", Datatypes::TEXT);
$this->addAttribute("email", Datatypes::TEXT);
$this->addAttribute("age", Datatypes::INT_NUMBER);
parent::__construct( $args );
}
}
?>
Como primer regla, todas las clases del modelo heredan de PersistentObject, que es la clase que implementa toda la lógica necesaria para definición del modelo.
En este caso definimos una clase "Usuario" con tres campos: name (nombre del usuario), email (correo electrónico del usuario) y age (edad del usuario). Para definir estos campos se utiliza el método "addAttribute" de PersistentObject. El segundo argumento de dicho método es el tipo de atributo, este tipo servirá luego para generar el tipo de la columna donde se guarden los valores correspondientes a ese atributo en la tabla en la que se almacenan las instancias de esta clase.
El atributo "withTable" de PersistentObject se utiliza para definir el nombre de la tabla donde las instancias de la clase Usuario van a ser almacenadas. Yupp Framework no tiene restricciones sobre los nombres de las tablas, por ejemplo, no exige nombres definidos en plural, como en otros frameworks donde esta regla es necesaria.
Como se puede apreciar en la última línea del constructor de "Usuario", es necesario llamar al construcor de la clase padre, en este caso "PersistentObject", para que se haga el trabajo necesario de inicialización del objeto. Esto inyecta atributos, que serán de utilidad luego, y define sus valores.
Introducción a relaciones entre clases
Yupp soporta relaciones de distinta cardinalidad entre clases del modelo de datos. La cardinalidad indica cuantas instancias de la clase relacionada se pueden tener asociadas.
Las tres posibilidades de cardinalidad de las relaciones son: 1-1, 1-N y N-N. Y las relaciones pueden ser unidireccionales o bidireccionales, en la siguiente imagen se muestran todas las opciones que soporta Yupp PHP Framework para las reslaciones entre clases:
Nota: Yupp PHP Framework soporta salvar un modelo en cascada, esto quiere decir que si se salva una sola instancia de una clase, se salvan también las instancias de las clases asociadas. Para poder hacer esto, Yupp necesita saber cual es la clase "fuerte" en la relación. Yupp posee una forma de declarar explícitamente cual es la parte fuerte de la relación, esto es a través del atributo "belongsTo" en las clases del modelo, que indica que la clase pertenece a otra mediante una asociación, y esta última es la parte fuerte de esa asociación. No siempre es necesario declarar ese atributo, en los casos que no se declara Yupp Framework sigue ciertas reglas para determinar la parte fuerte de la relació, aquí se listan todas las reglas:
- Si la relacion es A (1) -> (1) B, entonces B belongsTo A.
- Si la relacion es A (1) <-> (1) B, se necesita belongsTo para saber cual es el lado fuerte.
- Si la relacion es A (1) -> (*) B, entonces B belongsTo A.
- Si la relacion es A (1) <-> (*), B entonces B belongsTo A.
- Si la relacion es A (*) -> (*) B, entonces B belongsTo A.
- Si la relacion es A (*) <-> (*) B, se necesita belongsTo para saber cual es el lado fuerte.
La característica importante del modelo es que es persistente, y a través del componente de ORM de Yupp (YORM), se pueden persistir y obtener datos de la base de datos sin necesidad de escribir SQL ni manejar la estructura de la base de datos (generar la estructura, obtener datos, hacer JOINs, etc).
Definiendo relaciones:
Un usuario tiene un login donde se almacena un nombre de usuario y una clave que el usuario utiliza para ingresar a nuestro sistema. Este ejemplo presenta una forma unidireccional de generar esta relación, desde el "Login" al "Usuario":
class Login extends PersistentObject {
function __construct( $args = array() )
{
$this->withTable = "login";
$this->addAttribute("username", Datatypes::TEXT);
$this->addAttribute("password", Datatypes::TEXT);
$this->addHasOne("user", Usuario);
parent::__construct( $args );
}
}
?>
Con el método addHasOne, se especifica una relación 1-1 unidireccional desde Login a Usuario, mediante un atributo usuario en la clase Login.
Un ejemplo de como se utiliza el modelo es el siguiente:
$usuario = new Usuario( array("name" =>"Pablo",
"email" =>"a@b.com",
"age" =>26) );
$usuario->save();
$login = new Login( array("username"=>"unusuario", "password"=>"abcd1234" );
$login->setUser( $usuario );
$login->save();
?>
Yupp también soporta relaciones múltiples, por ejemplo, si el usuario escribe entradas en un blog, el usuario tiene varias entradas:
class Entrada extends PersistentObject {
function __construct( $args = array() )
{
$this->withTable = "entradas";
$this->addAttribute("title", Datatypes::TEXT);
$this->addAttribute("text", Datatypes::TEXT);
parent::__construct( $args );
}
}
?>
Ahora agregamos la nueva relación 1-N de Usuario a Entrada:
class Usuario extends PersistentObject {
function __construct( $args = array() )
{
$this->withTable = "usuarios";
$this->addAttribute("name", Datatypes::TEXT);
$this->addAttribute("email", Datatypes::TEXT);
$this->addAttribute("age", Datatypes::INT_NUMBER);
$this->addHasMany("entradas", Entrada);
parent::__construct( $args );
}
}
?>
Como podemos ver, este es un caso de relaciones 1-N unidireccionales, por que se define la relación de Usuario a Entrada pero no de Entrada a Usuario. En la mayoría de los casos no será necesario definir relaciones bidireccionales, pero como vimos antes, Yupp Framework igualmente las soporta.
Un ejemplo de uso de la relación 1-N:
$usuario = Usuario::get( $id );
$entrada1 = new Entrada( array("title"=>"Un titulo",
"text" =>"Lorem ipsum dolor sit amet...") );
$entrada2 = new Entrada( array("title"=>"Otro titulo",
"text" =>"Lorem ipsum dolor sit amet...") );
$usuario->addToEntradas( $entrada1 );
$usuario->addToEntradas( $entrada2 );
$usuario->save();
?>
Más ejemplos de uso de clases del modelo:
- Crear un nuevo usuario y guardarlo en la base de datos:
<?php
$obj = new Usuario();
$obj->setProperties( array("name" => "Pablo",
"email" => "a@b.com",
"age" => 26) );
$obj->save();
?>
- Pedir todos los usuarios guardados en la base y mostrar sus nombres:
<?php
$list = Usuario::listAll();
foreach ( $list as $usuario )
{
echo $usuario->getNombre();
}
?>
- Eliminar un usuario:
<?php
$obj = Usuario::get( $id );
$obj->delete();
?>
