Rediseñar la clase Model para reemplazar a ModelClass. La nueva versión que se colocaría en la carpeta Template, de acuerdo a la nueva estructura de carpetas del Core. La versión original permanecería en Core/Model/Base para mantener compatibilidad durante unos meses.
<?php
namespace FacturaScripts\Plugins\MyNewPlugin\Model;
use FacturaScripts\Core\Template\Model;
use FacturaScripts\Core\Template\ModelTrait;
class Project extends Model
{
use ModelTrait;
const ID_COLUMN = 'id';
const TABLE_NAME = 'proyectos';
}
// cargamos el proyecto 23
$project = new Project();
if ($project->load(23) ) {
// lo ha cargado de la base de datos
// imprimimos su clave primaria
echo $project->id(); // antes era con primaryColumnValue()
}
Para dar compatibilidad hacia atrás se implementarían los alias a las funciones correspondientes en el ModelClass anterior, es decir, añadirle una función loadFromCode() que internamente llame a load() y una primaryColumnValue() que llame a id().
El principal cambio es que esta versión ya no asume el límite a 50.
Project::all(); // devuelve todos los proyectos
Project::all([], [], 0, 50); // devuelve 50 proyectos
Project::all([], ['name' => 'ASC'], 0, 50); // devuelve los primeros 50 proyectos ordenados por name
// con Where
$where = [
Where::eq('nick', 'pepe'),
Where::andGt('total', 1000)
];
Project::all($where, ['name' => 'ASC']); // devuelve todos los proyectos con nick = 'pepe' y total > 1000 y ordenados por name
$project = new Project();
$project->load(1); // carga el proyecto 1, carga y devuelve true si lo encuentra. clear() y devuelve false en caso contrario
$project->loadOrFail(1); // carga el proyecto 1, carga los datos si lo encuentra. Lanza una excepción en caso contrario
$project->loadWhere($where);
$project->loadWhereOrNew($where); // carga el primer proyecto que cumpla where. clear() y asigna esos valores en caso contrario
// la función anterior sirve para reemplazar a estructuras de este estilo
$where = [
Where::eq('nick', 'jose'),
Where::eq('codalmacen', 'ALG')
];
if(false === $project->loadWhere($where)) {
// no existe, lo creamos
$project->nick = 'jose';
$project->codalmacen = 'ALG'
}
Project::find(1); // devuelve el proyecto con id = 1, o null si no lo encuentra
Project::findOrFail(1); // devuelve el proyecto con id = 1, o lanza excepción si no lo encuentra
$where = [
Where::eq('nick', 'jose'),
];
Project::findWhere($where); // devuelve el primer proyecto con nick = jose o null en caso contrario
Project::findWhereOrNew($where); // devuelve el primer proyecto nick = jose o uno nuevo con nick = jose ya asignado
Si en los métodos load() y find() comprobamos primero la Caché, podemos ahorrar mucho tiempo en consultas a la base de datos. Y nos podemos ahorrar las clases DataSrc.
La función newCode() debe resolver los problemas de concurrencia que tienen los modelos antiguos, donde 2 procesos en paralelo pueden obtener el mismo código a crear, generando un error en el segundo proceso. Una solución es usan Cache::increment('Model.XXX.newCode.YYY)
En ocasiones queremos eliminar muchos registros de una sola vez:
// eliminamos todos los proyectos con total = 0
Project::deleteAll([Where::eq('total', 0]);
Internamente el método puede construir y ejecutar el SQL para eliminar todos esos registros de una sola vez. Pero a la vez, como es un método y podemos añadirle extensiones, podemos ejecutar código antes para hacer ciertas cosas o impedir que suceda si se dan ciertas condiciones.
Cuando solamente queremos modificar los valores de alguna de las columnas, sin actualizar el resto, podemos usar el método update():
$producto123 = Producto::findWhereEq('referencia', '123');
if ($producto123) {
$producto123->update('coste', 7); // update productos set coste = '7' where idproducto = 'XXX';
}
$project = new Project();
$project->load(123);
$project->relatedOne('User'); // devuelve el usuario con nick = nick de la tabla, null si no lo encuentra
$project->relatedOne('User', 'author'); // devuelve el usuario con nick = author de la tabla, null si no lo encuentra
$project->relatedAll('Task', 'idproject'); // devuelve todas las tareas con idproject = id() del modelo
$project->relatedAll('Task', 'idproject', [], 0, 50); // devuelve las primeras 50 tareas con idproject = id() del modelo
$project->relatedAll('Task', 'idproject', ['creationdate' => 'ASC']); // devuelve todas las tareas, ordenadas por creationdate, con idproject = id() del modelo
En el método test(), que se llama al hacer save(), debe comprobar que todas las columnas tengan valores válidos, es decir, si el campo total está definido como float en el xml de la tabla, que no permita guardar una cadena de texto.
Podemos añadir al xml de la tabla validaciones adicionales antes de guardar los datos, por ejemplo, para los campos que son email, podemos añadir una regla de validación de email, de forma que sin hacer nada más, el modelo compruebe que si hay un email debe ser correcto:
<column>
<name>name</name>
<type>character varying(100)</type>
<validation>valid_email,min_length[5]</validation>
</column>
¿Por qué poner la regla en el xml? De esta forma podemos añadir validaciones o cambiarlas mediante extensiones. Podríamos añadir mediante una extensión un campo email a un modelo y definir ahí la validación de email, con solamente poner el xml en la carpeta Extension/Table del plugin.
Algunas validaciones interesantes:
En lugar de tener una clase ModelOnChange y solamente aquellos modelos que hereden de esta clase tendrán posibilidad de reaccionar a cambios de valores en sus columnas, todos los modelos deberían soportar estas características. Para ello hay que implementar en la clase Model las funciones:
Esta tarea fue aprobada el 06-11-2023 y está pendiente para comenzar el desarrollo el 29-01-2025.