Nota
The documentation is currently partially supported in es language for this page.
Por favor, siéntase libre de enviarnos un pull request en Github o utilizar el botón Improve this Doc para proponer directamente los cambios.
Usted puede hacer referencia a la versión en Inglés en el menú de selección superior para obtener información sobre el tema de esta página.
Article
)¶Los modelos son una parte fundamental en CakePHP. Cuando creamos un modelo, podemos interactuar con la base de datos para crear, editar, ver y borrar con facilidad cada ítem de ese modelo.
Los modelos están separados entre los objetos Tabla
(Table
) y Entidad
(Entity
). Los objetos Tabla
proporcionan acceso a la coleción de
entidades almacenada en una tabla específica y va en src/Model/Table. El
fichero que crearemos se guardará en src/Model/Table/ArticlesTable.php. El
fichero completo debería tener este aspecto:
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp');
}
}
Los convenios usados para los nombres son importantes. Llamando a nuestro objeto
Tabla ArticlesTable
, CakePHP deducirá automáticamente que esta Tabla será
utilizada en el controlador ArticlesController, y que se vinculará a una tabla
en nuestra base de datos llamada articles
.
Nota
CakePHP creará dinámicamente un objeto para el modelo si no encuentra el fichero correspondiente en src/Model/Table. Esto significa que si te equivocas al nombrar el fichero (por ejemplo lo llamas articlestable.php —en minúscula— o ArticleTable.php —en singular) CakePHP no va a reconocer la configuración que escribas en ese fichero y utilizará valores por defecto.
Para más información sobre modelos, como callbacks y validaciones echa un vistazo al capítulo del Manual Acceso a la base de datos & ORM.
Articles Controller
)¶Vamos a crear ahora un controlador para nuestros artículos. En el controlador es donde escribiremos el código para interactuar con nuestros artículos. Es donde se utilizan los modelos para llevar a cabo el trabajo que queramos hacer con nuestros artículos. Vamos a crear un nuevo fichero llamado ArticlesController.php dentro del directorio src/Controller. A continuación puedes ver el aspecto básico que debería tener este controlador:
namespace App\Controller;
class ArticlesController extends AppController
{
}
Vamos a añadir una acción a nuestro nuevo controlador. Las acciones representan una función concreta o interfaz en nuestra aplicación. Por ejemplo, cuando los usuarios recuperan la url www.example.com/articles/index (que es lo mismo que www.example.com/articles/) esperan ver un listado de artículos. El código para tal acción sería este:
namespace App\Controller;
class ArticlesController extends AppController
{
public function index()
{
$articles = $this->Articles->find('all');
$this->set(compact('articles'));
}
}
Por el hecho de haber definido el método index()
en nuestro
ArticlesController, los usuarios ahora pueden acceder a su lógica solicitando
www.example.com/articles/index. Del mismo modo, si definimos un método llamado
foobar()
los usuarios tendrán acceso a él desde
www.example.com/articles/foobar.
Advertencia
Puede que tengas la tentación de llamar tus controladores y acciones de cierto modo para obtener una URL en concreto. Resiste la tentación. Sigue las convenciones de CakePHP (mayúsculas, nombre en plural, etc.) y crea acciones comprensibles, que se dejen leer. Luego podrás asignar URLs a tu código utilizando «rutas», que veremos más adelante.
La única instrucción en la acción utiliza set()
para pasar datos desde el
controlador hacia la vista (que crearemos a continuación). La línea en cuestión
asigna una variable en la vista llamada “articles” igual al valor retornado por
el método find('all')
del objeto de tabla Artículos (ArticlesTable
).
Para aprender más sobre los controladores, puedes visitar el capítulo Controladores.
Article Views
)¶Ahora que tenemos nuestros datos fluyendo por el modelo, y que la lógica de nuestra aplicación está definida en nuestro controlador, vamos a crear una vista para la acción índex creada en el paso anterior.
Las vistas en CakePHP únicamente son fragmentos de presentación que encajan
dentro de la plantilla (layout
) de nuestra aplicación. Para la mayoría de
aplicaciones son HTML mezclados con PHP, pero bien podrían acabar siendo XML,
CSV o incluso datos binarios.
Una plantilla es una presentación de código que envuelve una vista. Se
pueden definir múltiples plantillas y puedes cambiar entre ellas pero, por ahora,
utilizaremos la plantilla por defecto (default
).
¿Recuerdas cómo en la sección anterior hemos asignado la variable “articles” a
la vista utilizando el método set()
? Esto asignaría el objeto de consulta
(query object
) a la vista para ser invocado por una iteración foreach
.
Las vistas en CakePHP se almacenan en la ruta /src/Template
y en un
directorio con el mismo nombre que el controlador al que pertenecen (tendremos
que crear una carpeta llamada “Articles” en este caso). Para dar formato a los
datos de este artículo en una bonita tabla, el código de nuestra vista debería
ser algo así:
<!-- File: /templates/Articles/index.php -->
<h1>Artículos</h1>
<table>
<tr>
<th>Id</th>
<th>Title</th>
<th>Created</th>
</tr>
<!-- Aquí es donde iteramos nuestro objeto de consulta $articles, mostrando en pantalla la información del artículo -->
<?php foreach ($articles as $article): ?>
<tr>
<td><?= $article->id ?></td>
<td>
<?= $this->Html->link($article->title,
['controller' => 'Articles', 'action' => 'view', $article->id]) ?>
</td>
<td><?= $article->created->format(DATE_RFC850) ?></td>
</tr>
<?php endforeach; ?>
</table>
Esto debería ser sencillo de comprender.
Como habrás notado, hay una llamada a un objeto $this->Html
. Este objeto es
una instancia de la clase Cake\View\Helper\HtmlHelper
de CakePHP.
CakePHP proporciona un conjunto de ayudantes de vistas (helpers
) para ayudarte a
completar acciones habituales, como por ejemplo crear un enlace o un formulario.
Puedes aprender más sobre esto en Helpers, pero lo que es
importante destacar aquí es que el método link()
generará un enlace HTML con
el título como primer parámetro y la URL como segundo parámetro.
Cuando crees URLs en CakePHP te recomendamos emplear el formato de array. Se
explica con detenimiento en la sección de Rutas (Routes
). Si utilizas las rutas
en formato array podrás aprovecharte de las potentes funcionalidades de
generación de rutas inversa de CakePHP en el futuro. Además puedes especificar
rutas relativas a la base de tu aplicación de la forma
/controlador/accion/param1/param2
o incluso utilizar Using Named Routes.
Llegados a este punto, deberías ser capaz de acceder con tu navegador a http://www.example.com/articles/index. Deberías ver tu vista, correctamente formatada con el título y la tabla listando los artículos.
Si te ha dado por hacer clic en uno de los enlaces que hemos creado en esta
vista (que enlazan el título de un artículo hacia la URL
/articles/view/un\_id
), seguramente habrás sido informado por CakePHP de que
la acción no ha sido definida todavía. Si no has sido infromado, o bien algo
ha ido mal o bien ya la habías definido, en cuyo caso eres muy astuto. En caso
contrario, la crearemos ahora en nuestro controlador de artículos:
namespace App\Controller;
class ArticlesController extends AppController
{
public function index()
{
$this->set('articles', $this->Articles->find('all'));
}
public function view($id = null)
{
$article = $this->Articles->get($id);
$this->set(compact('article'));
}
}
Si observas la función view(), ahora el método set() debería serte familiar.
Verás que estamos usando get()
en vez de find('all')
ya que sólo
queremos un artículo concreto.
Verás que nuestra función view toma un parámetro: el ID del artículo que
queremos ver. Este parámetro se gestiona automáticamente al llamar
a la URL /articles/view/3
, el valor “3” se pasa a la función view como primer
parámetro $id
.
También hacemos un poco de verificación de errores para asegurarnos de que el
usuario realmente accede a dicho registro. Si el usuario solicita
/articles/view
lanzaremos una excepción NotFoundException
y dejaremos al
ErrorHandler tomar el control. Utilizando el método get()
en la tabla
Articles también hacemos una verificación similar para asegurarnos de que el
usuario ha accedido a un registro que existe. En caso de que el artículo
solicitado no esté presente en la base de datos, el método get()
lanzará
una excepción NotFoundException
.
Ahora vamos a definir la vista para esta nueva función “view” ubicándola en templates/Articles/view.php.
<!-- File: /templates/Articles/view.php -->
<h1><?= h($article->title) ?></h1>
<p><?= h($article->body) ?></p>
<p><small>Created: <?= $article->created->format(DATE_RFC850) ?></small></p>
Verifica que esto funciona probando los enlaces en /articles/index
o puedes
solicitándolo manualmente accediendo a /articles/view/1
.
Leer de la base de datos y mostrar nuestros artículos es un gran comienzo, pero permitamos también añadir nuevos artículos.
Lo primero, añadir una nueva acción add()
en nuestro controlador
ArticlesController:
namespace App\Controller;
class ArticlesController extends AppController
{
public $components = ['Flash'];
public function index()
{
$this->set('articles', $this->Articles->find('all'));
}
public function view($id)
{
$article = $this->Articles->get($id);
$this->set(compact('article'));
}
public function add()
{
$article = $this->Articles->newEmptyEntity();
if ($this->request->is('post')) {
$article = $this->Articles->patchEntity($article, $this->request->getData());
if ($this->Articles->save($article)) {
$this->Flash->success(__('Your article has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Unable to add your article.'));
}
$this->set('article', $article);
}
}
Nota
Necesitas incluir el FlashComponent en cualquier controlador donde vayas a usarlo. Si lo ves necesario, inclúyelo en tu AppController.
Lo que la función add() hace es: si el formulario enviado no está vacío, intenta salvar un nuevo artículo utilizando el modelo Articles. Si no se guarda bien, muestra la vista correspondiente, así podremos mostrar los errores de validación u otras alertas.
Cada petición de CakePHP incluye un objeto ServerRequest
que es accesible
utilizando $this->request
. El objeto de petición contiene información útil
acerca de la petición que se recibe y puede ser utilizado para controlar el flujo
de nuestra aplicación. En este caso, utilizamos el método
Cake\Network\ServerRequest::is()
para verificar que la petición es una
petición HTTP POST.
Cuando un usuario utiliza un formulario y efectúa un POST a la aplicación, esta
información está disponible en $this->request->getData()
. Puedes usar la función
pr()
o debug()
para mostrar el contenido de esa variable
y ver la pinta que tiene.
Utilizamos el método mágico __call
del FlashComponent
para guardar un
mensaje en una variable de sesión que será mostrado en la página después de la
redirección. En la plantilla tenemos <?= $this->Flash->render() ?>
que
muestra el mensaje y elimina la correspondiente variable de sesión. El método
Cake\Controller\Controller::redirect
del controlador redirige
hacia otra URL. El parámetro ['action' => 'index']
se traduce a la URL
/articles (p.e. la acción index del controlador de artículos). Puedes echar un
ojo al método Cake\Routing\Router::url()
en la API para ver los formatos en que puedes especificar una
URL para varias funciones de CakePHP.
Al llamar al método save()
, comprobará si hay errores de validación primero
y si encuentra alguno, no continuará con el proceso de guardado. Veremos a
continuación cómo trabajar con estos errores de validación.
CakePHP te ayuda a evitar la monotonía al construir tus formularios y su validación. Todos odiamos teclear largos formularios y gastar más tiempo en reglas de validación de cada campo. CakePHP lo hace más rápido y sencillo.
Para aprovechar estas funciones es conveniente que utilices el FormHelper en tus
vistas. La clase Cake\View\Helper\FormHelper
está disponible en
tus vistas por defecto a través de $this->Form
.
He aquí nuestra vista add
:
<!-- File: templates/Articles/add.php -->
<h1>Añadir Artículo</h1>
<?php
echo $this->Form->create($article);
echo $this->Form->input('title');
echo $this->Form->input('body', ['rows' => '3']);
echo $this->Form->button(__('Guardar artículo'));
echo $this->Form->end();
?>
Hemos usado FormHelper para generar la etiqueta “form”. La ejecución de
$this->Form->create()
genera el siguiente código:
<form method="post" action="/articles/add">
Si create()
no tiene parámetros al ser llamado, asume que estás creando un
formulario que envía vía POST a la acción add()
(o edit()
cuando id
es incluido en los datos de formulario) del controlador actual.
El método $this->Form->input()
se utiliza para crear elementos de formulario
del mismo nombre. El primer parámetro le indica a CakePHP a qué campo
corresponde y el segundo parámetro te permite especificar un abanico muy ámplio
de opciones - en este caso, el número de filas del textarea que se generará. Hay
un poco de introspección y «automagia» aquí: input()
generará distintos
elementos de formulario en función del campo del modelo especificado.
La llamada a $this->Form->end()
cierra el formulario. También generará
campos ocultos si la CSRF/prevención de manipulación de formularios ha sido
habilitada.
Volvamos atrás un minuto y actualicemos nuestra vista templates/Articles/index.php para añadir un enlace de «Añadir Artículo». Justo antes del tag <table> añade la siguiente línea:
<?= $this->Html->link(
'Añadir artículo',
['controller' => 'Articles', 'action' => 'add']
) ?>
Te estarás preguntando: ¿Cómo le digo a CakePHP la forma en la que debe validar
estos datos? Muy sencillo, las reglas de validación se escriben en el modelo.
Volvamos al modelo Articles
y hagamos algunos ajustes:
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class ArticlesTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp');
}
public function validationDefault(Validator $validator)
{
$validator
->notEmpty('title')
->notEmpty('body');
return $validator;
}
}
El método validationDefault()
le dice a CakePHP cómo validar tus datos
cuando se invoca el método save()
. Aquí hemos especificado que ambos campos,
el cuerpo y el título, no pueden quedar vacíos. El motor de validaciones de
CakePHP es potente y con numerosas reglas ya predefinidas (tarjetas de crédito,
direcciones de e-mail, etc.) así como flexibilidad para añadir tus propias
reglas de validación. Para más información en tal configuración, echa un vistazo
a la documentación Validation.
Ahora que ya tienes las reglas de validación definidas, usa tu aplicación para
crear un nuevo artículo con un título vacío y verás cómo funcionan. Como hemos
usado el método Cake\View\Helper\FormHelper::input()
, los
mensajes de error se construyen automáticamente en la vista sin código adicional.
Editando artículos: allá vamos. Ya eres un profesional de CakePHP, así que
habrás cogido la pauta. Crear una acción, luego la vista. He aquí cómo debería
ser la acción edit()
del controlador ArticlesController
:
public function edit($id = null)
{
$article = $this->Articles->get($id);
if ($this->request->is(['post', 'put'])) {
$this->Articles->patchEntity($article, $this->request->getData());
if ($this->Articles->save($article)) {
$this->Flash->success(__('Tu artículo ha sido actualizado.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Tu artículo no se ha podido actualizar.'));
}
$this->set('article', $article);
}
Lo primero que hace este método es asegurarse de que el usuario ha intentado
acceder a un registro existente. Si no han pasado el parámetro $id
o el
artículo no existe lanzaremos una excepción NotFoundException
para que el
ErrorHandler
se ocupe de ello.
Luego verifica si la petición es POST o PUT. Si lo es, entonces utilizamos los
datos recibidos para actualizar nuestra entidad artículo (article
) utilizando
el método “patchEntity”. Finalmente utilizamos el objeto tabla para guardar la
entidad de nuevo o mostrar errores de validación al usuario en caso de haberlos.
La vista sería algo así:
<!-- File: templates/Articles/edit.php -->
<h1>Edit Article</h1>
<?php
echo $this->Form->create($article);
echo $this->Form->input('title');
echo $this->Form->input('body', ['rows' => '3']);
echo $this->Form->button(__('Guardar artículo'));
echo $this->Form->end();
?>
Mostramos el formulario de edición (con los valores actuales de ese artículo), junto a los errores de validación que hubiese.
CakePHP utilizará el resultado de $article->isNew()
para determinar si un
save()
debería insertar un nuevo registro o actualizar uno existente.
Puedes actualizar tu vista índice (index
) con enlaces para editar artículos
específicos:
<!-- File: templates/Articles/index.php (edit links added) -->
<h1>Artículos</h1>
<p><?= $this->Html->link("Añadir artículo", ['action' => 'add']) ?></p>
<table>
<tr>
<th>Id</th>
<th>Title</th>
<th>Created</th>
<th>Action</th>
</tr>
<!-- Aquí es donde iteramos nuestro objeto de consulta $articles, mostrando en pantalla la información del artículo -->
<?php foreach ($articles as $article): ?>
<tr>
<td><?= $article->id ?></td>
<td>
<?= $this->Html->link($article->title, ['action' => 'view', $article->id]) ?>
</td>
<td>
<?= $article->created->format(DATE_RFC850) ?>
</td>
<td>
<?= $this->Html->link('Editar', ['action' => 'edit', $article->id]) ?>
</td>
</tr>
<?php endforeach; ?>
</table>
Vamos a permitir a los usuarios que borren artículos. Empieza con una acción
delete()
en el controlador ArticlesController
:
public function delete($id)
{
$this->request->allowMethod(['post', 'delete']);
$article = $this->Articles->get($id);
if ($this->Articles->delete($article)) {
$this->Flash->success(__('El artículo con id: {0} ha sido eliminado.', h($id)));
return $this->redirect(['action' => 'index']);
}
}
La lógica elimina el artículo especificado por $id y utiliza
$this->Flash->success()
para mostrar al usuario un mensaje de confirmación
tras haber sido redirigidos a /articles
. Si el usuario intenta eliminar
utilizando una petición GET, el “allowMethod” devolvería una Excepción. Las
excepciones que no se traten serán capturadas por el manejador de excepciones
de CakePHP (exception handler
) y una bonita página de error es mostrada.
Hay muchas Excepciones que pueden ser utilizadas
para indicar los varios errores HTTP que tu aplicación pueda generar.
Como estamos ejecutando algunos métodos y luego redirigiendo a otra acción de nuestro controlador, no es necesaria ninguna vista (nunca se usa). Lo que si querrás es actualizar la vista index.php para incluír el ya habitual enlace:
<!-- File: templates/Articles/index.php -->
<h1>Artículos</h1>
<p><?= $this->Html->link("Añadir artículo", ['action' => 'add']) ?></p>
<table>
<tr>
<th>Id</th>
<th>Title</th>
<th>Created</th>
<th>Action</th>
</tr>
<!-- Aquí es donde iteramos nuestro objeto de consulta $articles, mostrando en pantalla la información del artículo -->
<?php foreach ($articles as $article): ?>
<tr>
<td><?= $article->id ?></td>
<td>
<?= $this->Html->link($article->title, ['action' => 'view', $article->id]) ?>
</td>
<td>
<?= $article->created->format(DATE_RFC850) ?>
</td>
<td>
<?= $this->Form->postLink(
'Eliminar',
['action' => 'delete', $article->id],
['confirm' => '¿Estás seguro?'])
?>
<?= $this->Html->link('Editar', ['action' => 'edit', $article->id]) ?>
</td>
</tr>
<?php endforeach; ?>
</table>
Utilizando postLink()
crearemos un
enlace que utilizará JavaScript para hacer una petición POST que eliminará
nuestro artículo. Permitiendo que contenido sea eliminado vía peticiones GET es
peligroso, ya que arañas web (crawlers
) podrían eliminar accidentalmente tu
contenido.
Nota
Esta vista utiliza el FormHelper para pedir confirmación vía diálogo de confirmación de JavaScript al usuario antes de borrar un artículo.
Routes
)¶En muchas ocasiones, las rutas por defecto de CakePHP funcionan bien tal y como están. Los desarroladores que quieren rutas diferentes para mejorar la usabilidad apreciarán la forma en la que CakePHP relaciona las URLs con las acciones de los controladores. Vamos a hacer cambios ligeros para este tutorial.
Para más información sobre las rutas así como técnicas avanzadas revisa Connecting Routes.
Por defecto CakePHP responde a las llamadas a la raíz de tu sitio (por ejemplo
http://www.example.com) usando el controlador PagesController, mostrando una vista
llamada «home». En lugar de eso, lo reemplazaremos con nuestro controlador
ArticlesController
creando una nueva ruta.
Las reglas de enrutamiento están en config/routes.php. Querrás eliminar o comentar la línea que define la raíz por defecto. Dicha ruta se parece a esto:
Router::connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']);
Esta línea conecta la url “/” con la página por defecto de inicio de CakePHP. Queremos conectarla a nuestro propio controlador, así que reemplaza dicha línea por esta otra:
Router::connect('/', ['controller' => 'Articles', 'action' => 'index']);
Esto debería, cuando un usuario solicita “/”, devolver la acción index() del controlador ArticlesController.
Nota
CakePHP también calcula las rutas a la inversa. Si en tu código pasas el
array ['controller' => 'Articles', 'action' => 'index']
a una
función que espera una url, el resultado será “/”. Es buena idea usar
siempre arrays para configurar las URL, lo que asegura que los links
irán siempre al mismo lugar.
Creando aplicaciones de este modo te traerá paz, honor, amor, dinero a carretas e incluso tus fantasías más salvajes. Simple, no te parece? Ten en cuenta que este tutorial es muy básico, CakePHP tiene muchas otras cosas que ofrecer y es flexible aunque no hemos cubierto aquí estos puntos para que te sea más simple al principio. Usa el resto de este manual como una guía para construir mejores aplicaciones.
Ahora que ya has creado una aplicación CakePHP básica, estás listo para la vida real. Empieza tu nuevo proyecto y lee el resto del Cookbook así como la API.
Si necesitas ayuda, hay muchos modos de encontrar la ayuda que buscas - por favor, míralo en la página Donde obtener ayuda. ¡Bienvenido a CakePHP!
Hay varias tareas comunes que la gente que está aprendiendo CakePHP quiere aprender después:
Layouts: Personaliza la plantilla layout de tu aplicación
Elementos Incluír vistas y reutilizar trozos de código
Crear código con Bake: Generación básica de CRUDs
Tutorial Blog - Autenticación y Autorización: Tutorial de autenticación y permisos