Rediseñar el ListController. La nueva versión 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/Lib/ExtendedController para mantener compatibilidad durante unos meses.
<?php
namespace FacturaScripts\Plugins\MyNewPlugin\Controller;
class ListProject extends \FacturaScripts\Core\Template\ListController
{
const MENU = 'sales';
const TITLE = 'projects';
const ICON = 'fas fa-folders';
protected function init()
{
$this->addTabProjects();
}
protected function addTabProject(string $tabName = 'ListProject')
{
// llamamos a addListTab() en lugar de addView() para que sea igual que en los EditController
// también nos dejamos la puerta a añadir más tipos de pestañas
$this->addListTab($tabName, 'Project')
->addOrderBy(['name'], 'name')
->addSearchFields(['name']);
// tanto la función addListTab() como las funciones AddOrderBy() y addSearchField()
// deben devolver siempre $this->tabs[$tabName]
// así podemos encadenarlas
}
}
El uso de contantes permite al IDE autocompletar, mientras que con la función que devuelve un array te tienes que saber los nombres de los campos. Para mayor compatibilidad, la clase Controller tendrá una función getPageData() que devolverá un array usando los valores de estas constantes.
Esta sería la primera función de nuestro código que ejecute el ListController. Aquí podemos añadir las pestañas y botones y variables que necesitemos, pero también comprobar permisos. Por ejemplo, podríamos impedir la ejecución para un usuario no administrador:
protected function init()
{
if($this->user->admin === false) {
throw new KernelException('AccessDenied', 'access-denied');
}
$this->addTabProjects();
}
El flujo de ejecución del controlador sería:
ListView, EditView, EditListView, etc son realmente pestañas, por lo que es mejor llamarlos tabs. Además deberíamos implementar las funciones tab() y deleteTab(), para mayor comodidad:
// añadimos una pestaña de listado
$this->addListTab($tabName, 'Project')
->addOrderBy(['name'], 'name')
->addSearchFields(['name']);
// añadimos otra opción de ordenación a una pestaña existente
$this->tab($tabName)->addOrderBy(['creationdate'], 'date');
// desactivamos la pestaña
$this->tab($tabName)->settings('active', false);
// eliminamos la pestaña
$this->deleteTab($tabName);
Necesitamos poder añadir o mover pestañas a cierta posición, por ejemplo en segunda posición o en última posición. Para ello podemos añádir la función setPosition().
// añadimos una pestaña al final del todo
$this->addListTab($tabName, 'Project')
->setPosition(99)
->addOrderBy(['name'], 'name')
->addSearchFields(['name']);
Aunque se puede asignar el título de la pestaña al hacer el addListTab(), si lo ofrecemos también en una función independiente, podemos granar expresividad.
// añadimos una pestaña al final del todo
$this->addListTab($tabName, 'Project')
->setTitle('projects')
->addOrderBy(['name'], 'name')
->addSearchFields(['name']);
Aunque se puede asignar el icono de la pestaña al hacer el addListTab(), si lo ofrecemos también en una función independiente, podemos granar expresividad.
// añadimos una pestaña al final del todo
$this->addListTab($tabName, 'Project')
->setIcon('fas fa-folders')
->addOrderBy(['name'], 'name')
->addSearchFields(['name']);
Pintamos de ese color las filas del listado cuyo campo $field tenga el valor $value, o lo que es lo mismo, añadimos el option color al row status.
// añadimos una pestaña al final del todo
$this->addListTab($tabName, 'Project')
->addOrderBy(['name'], 'name')
->addSearchFields(['name'])
->addColor('name', 'test', 'danger', 'test');
Aunque ya tengamos las funciones para añadir filtros concretos, como selectores, filtros de fechas, etc. Si ofrecemos la función para añadir cualquier filtro, facilitamos la inclusión de nuevos filtros mediante plugins, haciendo que sean como otro cualquiera.
$this->addListTab($tabName, 'Project')
->addOrderBy(['name'], 'name')
->addSearchFields(['name'])
->addFilter(new FilterColor('colorvalue', 'color'));
Debemos poder añadir botones a la pestaña, como ya tenemos ahora:
// añadimos el botón a la vez que la pestaña
$this->addListTab($tabName, 'Project')
->addOrderBy(['name'], 'name')
->addSearchFields(['name'])
->addButton([
'action' => 'test-action',
'icon' => 'fas fa-question',
'label' => 'test'
]);
// añadimos el botón después de añadir la pestaña
$this->tab($tabName)->addButton([
'action' => 'test-action',
'icon' => 'fas fa-question',
'label' => 'test'
]);
Pero también debemos poder añadir botones a la propia página, fuera de la pestaña.
$this->addButton([
'action' => 'test-action',
'icon' => 'fas fa-question',
'label' => 'test'
]);
Para prevenir ataques CSRF en cualquier controlador, la clase EditController debería lanzar una excepción cuando recibe datos por POST pero no hay un token asociado o el token no es válido. De este modo nos evitamos tener que hacer esta comprobación en cada método.
Algunos widgets o filtros de las pestañas pueden necesitar enviar y recibir datos del controlador. Para estos casos lo correcto es estandarizar estas llamadas, por ejemplo, que envíen siempre el action=tab-actions, y desde execPreviousAction() podemos llamar al método tabAction(), que recorrería todos los widgets y todos los filtros de la pestaña seleccionada y ejecutaría su método action().
Existen varias situaciones relacionadas con permisos que hay que tener en cuenta:
Esto es una sugerencia a debatir. Está abierta a comentarios, votación y debate en la reunión semanal. Si se aprueba pasará a estado pendiente.