Los componentes son paquetes de lógica que se comparten entre los controladores. CakePHP viene un con fantástico conjunto de componentes básicos que puedes usar para ayudar en varias tareas comunes. También puedes crear tus propios componentes. Si te encuentras queriendo copiar y pegar cosas entre componentes, deberías considerar crear tu propio componente que contenga la funcionalidad. Crear componentes mantiene el código del controlador limpio y te permite rehusar código entre los diferentes controladores.
Para más información sobre componentes incluidos en CakePHP, consulte el capítulo de cada componente:
Muchos de los componentes principales requieren configuración. Un ejemplo sería
FormProtection. La configuración para estos
componentes, y para los componentes en general, es usualmente hecho a través loadComponent()
en el método initialize()
del controlador o a través del array $components
:
class PostsController extends AppController
{
public function initialize(): void
{
parent::initialize();
$this->loadComponent('FormProtection', [
'unlockedActions' => ['index'],
]);
$this->loadComponent('Flash');
}
}
También puedes configurar los componentes en tiempo de ejecución usando el método
setConfig()
. A veces, esto es hecho en el método beforeFilter()
del controlador.
Lo anterior podría ser también expresado como:
public function beforeFilter(EventInterface $event)
{
$this->FormProtection->setConfig('unlockedActions', ['index']);
}
Al igual que los helpers, los componentes implementan los métodos getConfig()
y
setConfig()
para leer y escribir los datos de configuración:
// Lee los datos de configuración.
$this->FormProtection->getConfig('unlockedActions');
// Escribe los datos de configuración
$this->Flash->setConfig('key', 'myFlash');
Al igual que con los helpers, los componentes fusionarán automáticamente su propiedad $_defaultConfig
con la configuración del controlador para crear la propiedad $_config
que es
accesible con getConfig()
y setConfig()
.
Una configuración común para usar es la opción className
, que te permite utilizar
componentes de alias. Esta característica es útil cuando quieres reemplazar $this->Flash
u otra referencia común de componente con una implementación personalizada:
// src/Controller/PostsController.php
class PostsController extends AppController
{
public function initialize(): void
{
$this->loadComponent('Flash', [
'className' => 'MyFlash'
]);
}
}
// src/Controller/Component/MyFlashComponent.php
use Cake\Controller\Component\FlashComponent;
class MyFlashComponent extends FlashComponent
{
// Agrega tu código para sobreescribir el FlashComponent principal
}
Lo de arriba haría alias MyFlashComponent
a $this->Flash
en tus controladores.
Nota
El alias de un componente reemplaza esa instancia en cualquier lugar donde se use ese componente, incluso dentro de otros componentes.
Es posible que no necesites todos tus componentes disponibles en cada acción del
controlador. En situaciones como estas, puedes cargar un componente en tiempo de
ejecución usando el método loadComponent()
en tu controlador:
// En una acción del controlador
$this->loadComponent('OneTimer');
$time = $this->OneTimer->getTime();
Nota
Ten en cuenta que los componentes cargados sobre la marcha no perderán devoluciones
de llamadas. Si te basas en que las devoluciones de llamada beforeFilter
o
startup
serán llamadas, necesitarás llamarlas manualmente dependiendo de
cuándo cargas tu componente.
Una vez que hayas incluido algunos componentes a tu controlador, usarlos es bastante
simple. Cada componente que uses se exponen como una propiedad en tu controlador.
Si cargaste el Cake\Controller\Component\FlashComponent
en tu controlador,
puedes acceder a él de esta forma:
class PostsController extends AppController
{
public function initialize(): void
{
parent::initialize();
$this->loadComponent('Flash');
}
public function delete()
{
if ($this->Post->delete($this->request->getData('Post.id')) {
$this->Flash->success('Post deleted.');
return $this->redirect(['action' => 'index']);
}
}
}
Nota
Dado que tanto los modelos como los componentes se agregan a los controladores como propiedades, comparten el mismo “espacio de nombres”. Asegúrate de no dar a un componente y un modelo el mismo nombre.
Advertencia
Los métodos de un componente no tienen acceso a Inyección de Dependencias como lo tienen los controladores. Usa una clase de servicio dentro de las acciones de tu controlador en lugar de en el componente si necesitas ésta funcionalidad.
Supongamos que nuestra aplicación necesita realizar una operación matemática compleja en muchas partes diferentes de la aplicación. Podríamos crear un componente para albergar esta lógica compartida para su uso en muchos controladores diferentes.
El primer paso es crear un nuevo archivo de componente y clase. Crea el archivo en src/Controller/Component/MathComponent.php. La estructura básica para el componente debería verse algo como esto:
namespace App\Controller\Component;
use Cake\Controller\Component;
class MathComponent extends Component
{
public function doComplexOperation($amount1, $amount2)
{
return $amount1 + $amount2;
}
}
Nota
Todos los componentes deben extender de Cake\Controller\Component
.
De lo contrario, se disparará una excepción.
Una vez que nuestro componente está terminado, podemos usarlo en los controladores
de la aplicación cargándolo durante el método initialize()
del controlador.
Una vez cargado, el controlador recibirá un nuevo atributo con el nombre del
componente, a través del cual podemos acceder a una instancia del mismo:
// En un controlador
// Haz que el nuevo componente esté disponible en $this->Math,
// así como el estándar $this->Flash
public function initialize(): void
{
parent::initialize();
$this->loadComponent('Math');
$this->loadComponent('Flash');
}
Al incluir componentes en un controlador, también puedes declarar un conjunto de parámetros que se pasarán al constructor del componente. Estos parámetros pueden ser manejados por el componente:
// En tu controlador.
public function initialize(): void
{
parent::initialize();
$this->loadComponent('Math', [
'precision' => 2,
'randomGenerator' => 'srand'
]);
$this->loadComponent('Flash');
}
Lo anterior pasaría el array que contiene precision y randomGenerator a MathComponent::initialize()
en el parámetro $config
.
A veces, uno de tus componentes necesita usar otro componente. Puedes cargar otros componentes agregándolos a la propiedad $components:
// src/Controller/Component/CustomComponent.php
namespace App\Controller\Component;
use Cake\Controller\Component;
class CustomComponent extends Component
{
// El otro componente que tu componente usa
protected $components = ['Existing'];
// Ejecuta cualquier otra configuración adicional para tu componente.
public function initialize(array $config): void
{
$this->Existing->foo();
}
public function bar()
{
// ...
}
}
// src/Controller/Component/ExistingComponent.php
namespace App\Controller\Component;
use Cake\Controller\Component;
class ExistingComponent extends Component
{
public function foo()
{
// ...
}
}
Nota
A diferencia de un componente incluido en un controlador, no se activarán devoluciones de llamada en el componente de un componente.
Desde dentro de un componente, puedes acceder al controlador actual a través del registro:
$controller = $this->getController();
Los componentes también ofrecen algunas devoluciones de llamadas de ciclo de vida de las solicitudes que les permiten aumentar el ciclo de solicitud.
Es llamado antes que el método beforeFilter() del controlador, pero después del método initialize() del controlador.
Es llamado después del método beforeFilter() del controlador, pero antes de que el controlador ejecute el «handler» de la acción actual
Es llamado después de que el controlador ejecute la lógica de la acción solicitada, pero antes de que el controlador renderize las vistas y el diseño.
Es llamado durante el evento Controller.shutdown
, antes de enviar la salida al navegador.
Es llamado cuando el método de redirección del controlador es llamado pero
antes de cualquier otra acción. Si este método devuelve false
el controlador
no continuará en redirigir la petición. Los parámetros $url y $response permiten
modificar e inspeccionar la ubicación o cualquier otro encabezado en la respuesta.
Para redirigir desde dentro de un método de devolución de llamada de un componente, puedes usar lo siguiente:
public function beforeFilter(EventInterface $event)
{
$event->stopPropagation();
return $this->getController()->redirect('/');
}
Al detener el evento, le haces saber a CakePHP que no quieres ninguna otra devolución
de llamada de componente para ejecutar, y que el controlador no debe manejar la acción
más lejos. A partir de 4.1.0 puedes generar una RedirectException
para señalar
una redirección:
use Cake\Http\Exception\RedirectException;
use Cake\Routing\Router;
public function beforeFilter(EventInterface $event)
{
throw new RedirectException(Router::url('/'))
}
Generar una excepción detendrá todos los demás detectores de eventos y creará
una nueva respuesta que no conserva ni hereda ninguno de los encabezados de la respuesta
actual. Al generar una RedirectException
puedes incluir encabezados adicionales:
throw new RedirectException(Router::url('/'), 302, [
'Header-Key' => 'value',
]);
Added in version 4.1.0.