lunes, 25 de agosto de 2014
Symfony - Día 12: El Generador de Admin
Con la cambios que se hizo ayer en Jobeet, la aplicación frontend esta ahora completamente utilizable por los oferentes y demandantes de empleo. Es hora de hablar un poco acerca de la aplicación backend.
Hoy, gracias a la funcionalidad del Generador de Admin de symfony, vamos a desarrollar una completa interfaz para el backend de Jobeet en sólo una hora.
Creación del Backend
El primer paso es crear la aplicación backend. Si tu memoria te sirve bien, deberías recordar cómo hacerlo con la tarea
generate:app
:$ php symfony generate:app backend
La aplicación backend ya está disponible en
http://www.jobeet.com.localhost/backend.php/
para el entorno prod
, and at http://www.jobeet.com.localhost/backend_dev.php/
para el entorno dev
.
Cuando creaste la aplicación frontend, el controlador frontal de producción fue llamado
index.php
. Como sólo puede tener un index.php
por directorio, symfony crea un index.php
para el primer controlador frontal de producción y nombra a los otros con el nombre de la aplicación.
Si intentas volver a cargar los datos con la tarea
doctrine:data-load
, no va a funcionar más. Esto se debe a que el método JobeetJob::save()
necesita tener acceso al archivo de configuración app.yml
de la aplicación frontend
. Como tenemos ahora dos aplicaciones, symfony usa la primera que encuentra, que es ahora backend
.
Pero como se ha visto durante el día 8, los ajustes se pueden configurar en distintos niveles. Al mover el contenido del archivo
apps/frontend/config/app.yml
a config/app.yml
, los ajustes serán compartidos entre todas las aplicaciones y el problema será corregido. Vamos hacer el cambio ahora ya que vamos a utilizar mucho el modelo de clases en el Generador de Admin, y así tendremos las variables definidas en app.yml
en la aplicación backend.
La tarea
doctrine:data-load
también tiene una opción --application
. Así que, si necesitas algunos ajustes específicos de una u otra aplicación, esta es la forma de hacerlo:$ php symfony doctrine:data-load --application=frontend
Módulos del Backend
Para la aplicación frontend, la tarea
doctrine:generate-module
se ha utilizado para inicializar un módulo básico CRUD basado en una clase del modelo. Para el backend, la tareadoctrine:generate-admin
se utilizará, para generar una completa y funcional interfaz para una clase del modelo:$ php symfony doctrine:generate-admin backend JobeetJob --module=job
$ php symfony doctrine:generate-admin backend JobeetCategory --module=category
Estos dos comandos crean un módulo
job
y uno category
para las clases JobeetJob
yJobeetCategory
del modelo respectivamente.
La opción (opcional)
--module
sobreescribe el nombre del módulo
generado por defecto por la tarea (que habría sido de lo contrario jobeet_job
para la clase JobeetJob
).
Detrás de las escenas, la tarea también ha creado una ruta personalizada para cada módulo:
# apps/backend/config/routing.yml jobeet_job: class: sfDoctrineRouteCollection options: model: JobeetJob module: job prefix_path: job column: id with_wildcard_routes: true
No es de extrañar que la ruta de la clase utilizada por el Generador de Admin es
sfDoctrineRouteCollection
, ya que el principal objetivo de una interfaz de administración es la gestión del ciclo de vida de los objetos del modelo.
Definición de la ruta también define algunas opciones que no hemos visto antes:
prefix_path
: Define el prefijo de la url para la ruta generada (por ejemplo, la página editar será algo así como/job/1/edit
).column
: Define la columna de la tabla a usar en la URL por los enlaces que hace referencia a un objeto.with_wildcard_routes
: Como la interfaz de administrador tendrá más que el clásico de operaciones CRUD, esta opción permite definir una mayor colección de objetos y acciones sin editar la ruta.
Como siempre, es una buena idea leer la ayuda antes de usar una nueva tarea.
$ php symfony help doctrine:generate-admin
Te dará de la tarea, todos los argumentos y opciones, así como algunos clásicos Ejemplos de Uso.
Aspecto del Backend
De buenas a primeras, ya puedes utilizar los módulos generados:
http://www.jobeet.com.localhost/backend_dev.php/job
http://www.jobeet.com.localhost/backend_dev.php/category
Los módulos de administración tienen muchas más funciones que los simples módulos que hemos generado en los días anteriores. Sin escribir una sola línea de PHP, cada módulo proporciona estas características:
- La lista de objetos esta paginada
- La lista es ordenable
- La lista puede ser filtrada
- Los Objetos pueden ser creados, editedos, y eliminados
- Los objetos seleccionados pueden ser eliminados en batch
- La validation esta habilitada
- Los Mensajes Flash dan información inmediata al usuario
- ... y mucho mucho más
El Generador de Admin proporciona todas las funciones que necesitas para crear una interfaz backend en un paquete fácil de configurar.
If you have a look at our two generated modules, you will notice there is no activated webdesign whereas the symfony built-in admin generator feature has a basic graphic interface by default. For now, assets from the
sfDoctrinePlugin
are not located under the web/
folder. We need to publish them under the web/
folder thanks to the plugin:publish-assets
task:$ php symfony plugin:publish-assets
Para hacer la experiencia de los usuarios un poco mejor, necesitamos personalizar el backend por defecto. También vamos a añadir un menú simple para que sea fácil de navegar entre los diferentes módulos.
Sustituye el contenido por defecto del
layout.php
con el siguiente:// apps/backend/templates/layout.php <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>Jobeet Admin Interface</title> <link rel="shortcut icon" href="/favicon.ico" /> <?php use_stylesheet('admin.css') ?> <?php include_javascripts() ?> <?php include_stylesheets() ?> </head> <body> <div id="container"> <div id="header"> <h1> <a href="<?php echo url_for('homepage') ?>"> <img src="/legacy/images/logo.jpg" alt="Jobeet Job Board" /> </a> </h1> </div> <div id="menu"> <ul> <li> <?php echo link_to('Jobs', 'jobeet_job') ?> </li> <li> <?php echo link_to('Categories', 'jobeet_category') ?> </li> </ul> </div> <div id="content"> <?php echo $sf_content ?> </div> <div id="footer"> <img src="/legacy/images/jobeet-mini.png" /> powered by <a href="/"> <img src="/legacy/images/symfony.gif" alt="symfony framework" /></a> </div> </div> </body> </html>
Este layout usa una hoja de estilos
admin.css
. Este archivo debe estar presente en web/css/
ya que se instaló con el resto de las hojas de estilo durante el día 4.
Eventualmente, cambia la página de inicio por defecto en symfony en
routing.yml
:# apps/backend/config/routing.yml homepage: url: / param: { module: job, action: index }
El Cache de Symfony
Si eres lo suficientemente curioso, probablemente ya has abierto los archivos generados por la tarea bajo el directorio
apps/backend/modules/
. Si no, por favor abrelos ahora. ¡Sorpresa! Los directorios templates
están vacíos, y los archivos actions.class.php
están bastante vacíos y:// apps/backend/modules/job/actions/actions.class.php require_once dirname(__FILE__).'/../lib/jobGeneratorConfiguration.class.php'; require_once dirname(__FILE__).'/../lib/jobGeneratorHelper.class.php'; class jobActions extends autoJobActions { }
¿Cómo es posiblemente que funcione? Si hechas un vistazo más de cerca, te darás cuenta de que la clase
jobActions
hereda de autoJobActions
. La clase autoJobActions
es generada automáticamente por symfony si no existe. Que se encuentra en el directoriocache/backend/dev/modules/autoJob/
, la cual contiene el módulo "real":// cache/backend/dev/modules/autoJob/actions/actions.class.php class autoJobActions extends sfActions { public function preExecute() { $this->configuration = new jobGeneratorConfiguration(); if (!$this->getUser()->hasCredential( $this->configuration->getCredentials($this->getActionName()) )) { // ...
La forma en que el Generador de Admin trabaja debería recordarte algun conocido comportamiento. De hecho, es bastante similar a lo que ya hemos aprendido sobre el modelo y las clases de formulario. Basado en el modelo de la definición de esquema, symfony genera el modelo y las clases de formulario. Para el Generador de Admin, el módulo generado se puede configurar editando el archivo
config/generator.yml
que se encuentra en el módulo:# apps/backend/modules/job/config/generator.yml generator: class: sfDoctrineGenerator param: model_class: JobeetJob theme: admin non_verbose_templates: true with_show: false singular: ~ plural: ~ route_prefix: jobeet_job with_doctrine_route: true config: actions: ~ fields: ~ list: ~ filter: ~ form: ~ edit: ~ new: ~
Cada vez que actualizas el archivo
generator.yml
, symfony regenera el caché. Como se verá hoy, la personalización de los módulos generados de administración es fácil, rápida y divertida.NOTA La generación automática de archivos de cache sólo se produce en el entorno de desarrollo En producción, tendrás que borrar la caché manualmente con la tareacache:clear
(o borrar todo lo que encuentres en /cache).
La Configuración del Backend
Un módulo de administración puede ser personalizado con la edición de la clave
config
del archivo generator.yml
. La configuración está organizada en siete secciones:actions
: Configuración por defecto de las acciones se encuentran en la lista como en los formilariosfields
: Configuración por defecto para los camposlist
: Configuración de la listafilter
: Configuración de los filtrosform
: Configuración del fomulario new/editedit
: Configuración específica para la página editnew
: Configuración específica para la página new
Vamos a comenzar la personalización.
Configuración del Título
Los títulos de las secciones
list
, edit
, y new
del módulo category
se puede personalizar mediante la definición de una opción title
:# apps/backend/modules/category/config/generator.yml config: actions: ~ fields: ~ list: title: Category Management filter: ~ form: ~ edit: title: Editing Category "%%name%%" new: title: New Category
El
title
para la sección edit
contiene valores dinámicos: todas las cadenas encerradas entre %%
se sustituyen por los correspondientes valores de las columnas del objeto.
La configuración para el módulo
job
es muy similar:# apps/backend/modules/job/config/generator.yml config: actions: ~ fields: ~ list: title: Job Management filter: ~ form: ~ edit: title: Editing Job "%%company%% is looking for a %%position%%" new: title: Job Creation
La Configuración de los Campos
Las diferentes vistas (
list
, new
, y edit
) se componen de campos. Un campo puede ser una columna de la clase del modelo, o una columna virtual como veremos más adelante.
La configuración por defecto de los campos pueden ser personalizada con la sección
fields
:# apps/backend/modules/job/config/generator.yml config: fields: is_activated: { label: Activated?, help: Whether the user has activated the job, or not } is_public: { label: Public?, help: Whether the job can also be published on affiliate websites, or not }
La sección
fields
sobreecribe la configuración de los campos para todas las vistas, lo que significa que label
para is_activated
se modificó para las vistas list
, edit
, y new
.
La Configuración del Generador de Admin se basa en el principio de una configuración en cascada. Por ejemplo, si deseas cambiar un label solo para la vista
list
, define una opciónfields
bajo la sección list
:# apps/backend/modules/job/config/generator.yml config: list: fields: is_public: { label: "Public? (label for the list)" }
Cualquier configuración que se establece en la sección principal
fields
puede ser sobreecrita por la configuración de una vista específica. The overriding rules are the following:new
yedit
heredan deform
el cual hereda defields
list
hereda defields
filter
hereda defields
Para las secciones form (
form
, edit
, y new
), las opciones label
y help
sobreescriben las definidas en las clases form.Configuración de la vista List
display
De forma predeterminada, las columnas de la vista List son todas las columnas del modelo, en el orden del archivo de esquema. La opción
display
sobreescribe lo predefinido ordenando las columnas a mostrar:# apps/backend/modules/category/config/generator.yml config: list: title: Category Management display: [=name, slug]
El signo
=
antes del nombre
de la columna es una convención para convertir la cadena en un enlace.
Vamos a hacer lo mismo para el módulo
job
para que sea más legible:# apps/backend/modules/job/config/generator.yml config: list: title: Job Management display: [company, position, location, url, is_activated, email]
layout
La lista puede ser visualizada en diferentes layouts. Por defecto, el layout es
tabular
,lo que significa que cada valor de columna está en su propia columna de la tabla. Pero para el módulojob
, sería mejor utilizar el layout stacked
, que es el otro layout de serie:# apps/backend/modules/job/config/generator.yml config: list: title: Job Management layout: stacked display: [company, position, location, url, is_activated, email] params: | %%is_activated%% <small>%%category_id%%</small> - %%company%% (<em>%%email%%</em>) is looking for a %%=position%% (%%location%%)
En
stacked
, cada objeto está representado por una única cadena, que se define por la opciónparams
.
La opción
display
sigue siendo necesaria ya que define las columnas que ordenarán según el criterio del usuario.Las Columnas "Virtuales"
Con esta configuración, el segmento
%%category_id%%
será sustituida por la clave principal de la categoría. Pero sería más útil mostrar el nombre de la categoría. Siempre que utilices la notación%%
, la variable no tiene por qué corresponder a una columna en el actual esquema de base de datos. El Generador de Admin solo necesita encontrar un metodo get asociado en la clase del modelo.
Para mostrar el nombre de la categoría, podemos definir un método
getCategoryName()
en la clase JobeetJob
y sustituir %%category_id%%
por %%category_name%%
.
Sin embargo, la clase
JobeetJob
ya tiene un método getJobeetCategory()
que devuelve el objeto de la categoría relacionada. Y si usas %%jobeet_category%%
, este funcionará ya que la clase JobeetCategory
tiene un método mágico __toString()
que convierte el objeto a una cadena.# apps/backend/modules/job/config/generator.yml %%is_activated%% <small>%%jobeet_category%%</small> - %%company%% (<em>%%email%%</em>) is looking for a %%=position%% (%%location%%))
sort
Como administrador, probablemente estes más interesado en ver las últimos puestos de trabajo envíados. Puede configurar la columna de ordenación por defecto al añadir una opción
sort
:# apps/backend/modules/job/config/generator.yml config: list: sort: [expires_at, desc]
max_per_page
De forma predeterminada, la lista es paginada, y cada página contiene 20 items. Esto puede cambiarse con la opción
max_per_page
:# apps/backend/modules/job/config/generator.yml config: list: max_per_page: 10
batch_actions
En una lista, una acción se puede ejecutar sobre varios objetos. Estas acciones por lote no son necesarias para el módulo
category
, así, vamos a eliminarlas:# apps/backend/modules/category/config/generator.yml config: list: batch_actions: {}
La opción
batch_actions
define la lista de acciones por lote. Un array vacío permite eliminar las características. De forma predeterminada, cada módulo tiene una acción por lote delete
definida por el framework, pero para el módulo job
, supongamos que necesitamos una manera de extender la validez de determinados puestos de trabajo para otros 30 días:# apps/backend/modules/job/config/generator.yml config: list: batch_actions: _delete: ~ extend: ~
Todas las acciones que comienzan con un
_
son acciones provistas por el framework. Si actualizas tu navegador y seleccionas las acciones extendidas, symfony arrojarán una excepción diciendote que crees un método executeBatchExtend()
:// apps/backend/modules/job/actions/actions.class.php class jobActions extends autoJobActions { public function executeBatchExtend(sfWebRequest $request) { $ids = $request->getParameter('ids'); $q = Doctrine_Query::create() ->from('JobeetJob j') ->whereIn('j.id', $ids); foreach ($q->execute() as $job) { $job->extend(true); } $this->getUser()->setFlash('notice', 'The selected jobs have been extended successfully.'); $this->redirect('jobeet_job'); } }
Las claves primarias seleccionadas son almacenados en el parámetro
ids
de la petición. Para cada puesto de trabajo seleccionado, el método JobeetJob::extend()
se llama con un argumento extra para eludir algunos controles realizados en el método. Tenemos que actualizar el método extend()
con un argumento extra para pasar la comprobacion de expiración.
Actualiza el método
extend()
para tomar este nuevo argumento en cuenta:// lib/model/doctrine/JobeetJob.class.php class JobeetJob extends BaseJobeetJob { public function extend($force = false) { if (!$force && !$this->expiresSoon()) { return false; } $this->setExpiresAt(date('Y-m-d', time() + 86400 * sfConfig::get('app_active_days'))); $this->save(); return true; } // ... }
Después de que todos los puestos de trabajo se han ampliado, el usuario es redirigido al módulo
job
.
object_actions
En la lista, hay una columna adicional para las acciones que puede ejecutarse en un único objeto. Para el módulo
category
, vamos a eliminarlos ya que tenemos un enlace con el nombre de la categoría para editarlo, y que realmente no necesitamos ser capaces de borrar una directamente de la lista:# apps/backend/modules/category/config/generator.yml config: list: object_actions: {}
Para el módulo
job
, vamos a mantener las acciones existentes y añadir una nueva acción extend
similar a la que hemos añadido como batch action:# apps/backend/modules/job/config/generator.yml config: list: object_actions: extend: ~ _edit: ~ _delete: ~
Como para las batch actions, las acciones
_delete
y _edit
son las definidas por el framework. Tenemos que definir la acción listExtend()
para hacer que el enlace extend
funcione:// apps/backend/modules/job/actions/actions.class.php class jobActions extends autoJobActions { public function executeListExtend(sfWebRequest $request) { $job = $this->getRoute()->getObject(); $job->extend(true); $this->getUser()->setFlash('notice', 'The selected jobs have been extended successfully.'); $this->redirect('jobeet_job'); } // ... }
actions
Ya hemos visto la forma de vincular la acción a una lista de objetos o un objeto único. La opción
actions
define las acciones que no tienen objeto, como la creación de un nuevo objeto. Vamos a eliminar la acción predeterminada new
y añadir una nueva acción, que suprime todos los puestos de trabajo que no se han activado por el usuario por más de 60 días:# apps/backend/modules/job/config/generator.yml config: list: actions: deleteNeverActivated: { label: Delete never activated jobs }
Hasta ahora, todas las acciones que hemos definido tenian
~
, lo que significa que symfony ya configuró la acción automáticamente. Cada acción puede ser personalizada mediante la definición de un array de parámetros. La opción label
sobreescribe el label por defecto generado por symfony.
Por defecto, la acción ejecutada cuando haces click en el enlace es el nombre de la acción con prefijo
list
.
Crea la acción
listDeleteNeverActivated
en el módulo job
:// apps/backend/modules/job/actions/actions.class.php class jobActions extends autoJobActions { public function executeListDeleteNeverActivated(sfWebRequest $request) { $nb = Doctrine::getTable('JobeetJob')->cleanup(60); if ($nb) { $this->getUser()->setFlash('notice', sprintf('%d never activated jobs have been deleted successfully.', $nb)); } else { $this->getUser()->setFlash('notice', 'No job to delete.'); } $this->redirect('jobeet_job'); } // ... }
Hemos reutilizado el método
JobeetJobTable::cleanup()
definido el día de ayer. Es otro gran ejemplo de la reutilización proporcionada por el patrón MVC.
También puede cambiar la acción a ejecutar pasando un parámetro
action
:deleteNeverActivated: { label: Delete never activated jobs, action: foo }
table_method
El número de consultas a la base de datos para mostrar el listado de los puestos de trabajo es 13, como muestra la barra de depuración web.
Si haces clic en ese número, se verá que la mayoría de las peticiones son para recuperar el nombre de la categoría para cada trabajo.
Para reducir el número de consultas, podemos cambiar el método utilizado para obtener los puestos de trabajo utilizando la opción
table_method
:# apps/backend/modules/job/config/generator.yml config: list: table_method: retrieveBackendJobList
Ahora debes crear el método
retrieveBackendJobList
en JobeetJobTable
situado enlib/model/doctrine/JobeetJobTable.class.php
.// lib/model/doctrine/JobeetJobTable.class.php class JobeetJobTable extends Doctrine_Table { public function retrieveBackendJobList(Doctrine_Query $q) { $rootAlias = $q->getRootAlias(); $q->leftJoin($rootAlias . '.JobeetCategory c'); return $q; } // ...
El método
retrieveBackendJobList()
agrega un join entre las tablas job
y category
y crea automáticamente el objeto categoría relacionado con cada puesto de trabajo.
The number of requests is now down to three:
Configuración de las Vistas Form
La Configuración de la Vista Form se realiza en tres secciones:
form
, edit
, y new
. Todas tienen la misma configuración y la capacidad de la sección form
sólo existe como un mensaje para las secciones edit
y new
.
display
En cuanto a la lista, puedes cambiar el orden de los campos que se muestran con la opción
display
. Pero, como el formulario mostrado se define por una clase, no trates de eliminar un campo, ya que podría dar lugar a errores de validación inesperados.
La opción
display
para vistas form también se puede utilizar para organizar los campos en grupos:# apps/backend/modules/job/config/generator.yml config: form: display: Content: [category_id, type, company, logo, url, position, location, description, how_to_apply, is_public, email] Admin: [_generated_token, is_activated, expires_atxpires_at]
La configuración anterior define dos grupos (
Content
y Admin
), each que contienen un subconjunto de los campos del formulario.
El Generador de Admin tiene soporte para la relación muchos a muchos. En el formilario categoría, tienes un input para el nombre, uno para el slug, y un cuadro desplegable para los afiliados relacionados. Como no tiene sentido editar esta relación en esta página, vamos a eliminarla:
// lib/form/doctrine/JobeetCategoryForm.class.php class JobeetCategoryForm extends BaseJobeetCategoryForm { public function configure() { unset($this['created_at'], $this['updated_at'], $this['jobeet_affiliates_list']); } }
Columnas "Virtuales"
En las opciónes
display
para el formulario de puestos de trabajo, el campo _generated_token
comienza con un guión bajo (_
). Esto significa que la visualización de este campo será manejado por un partial personalizado de nombre _generated_token.php
:
Crea este partial con el siguiente contenido:
// apps/backend/modules/job/templates/_generated_token.php <div class="sf_admin_form_row"> <label>Token</label> <?php echo $form->getObject()->getToken() ?> </div>
En el partial, tienes acceso al actual form (
$form
) y el objeto relacionado es accesible a través del método getObject()
.
También puede delegar la visualización a un componente con el prefijo de un tilde (
~
) al nombre del campo.
class
Como el formulario será utilizado por los administradores, hemos mostrado más información que para el usuario del formulario job. Pero por ahora, algunos de ellos no aparecen en la formulario ya que se han eliminado en la clase
JobeetJobForm
.
Para tener diferentes formularios para el frontend y el backend, tenemos que crear dos clases form. Vamos a crear una clase
BackendJobeetJobForm
que herede de la clase JobeetJobForm
. Como no tienen los mismos campos ocultos, también tenemos que refactorizar la claseJobeetJobForm
un poco para mover la declaración unset()
en un método que será sobreescrito en BackendJobeetJobForm
:// lib/form/doctrine/JobeetJobForm.class.php class JobeetJobForm extends BaseJobeetJobForm { public function configure() { $this->removeFields(); $this->validatorSchema['email'] = new sfValidatorAnd(array( $this->validatorSchema['email'], new sfValidatorEmail(), )); // ... } protected function removeFields() { unset( $this['created_at'], $this['updated_at'], $this['expires_at'], $this['is_activated'], $this['token'] ); } } // lib/form/doctrine/BackendJobeetJobForm.class.php class BackendJobeetJobForm extends JobeetJobForm { public function configure() { parent::configure(); } protected function removeFields() { unset( $this['created_at'], $this['updated_at'], $this['token'] ); } }
La clase predeterminado form utilizado por el Generador de Administración puede ser sobreescrita con el ajuste de la opción
class
:# apps/backend/modules/job/config/generator.yml config: form: class: BackendJobeetJobForm
Como hemos añadido una nueva clase, no te olvides de limpiar el cache.
El formulario
edit
todavía tiene una pequeña molestia. El logotipo subido no aparece en ningun lugar y no podrás quitar el actual. El widget sfWidgetFormInputFileEditable
añade capacidades de edición a un simple widget input file:// lib/form/doctrine/BackendJobeetJobForm.class.php class BackendJobeetJobForm extends JobeetJobForm { public function configure() { parent::configure(); $this->widgetSchema['logo'] = new sfWidgetFormInputFileEditable(array( 'label' => 'Company logo', 'file_src' => '/uploads/jobs/'.$this->getObject()->getLogo(), 'is_image' => true, 'edit_mode' => !$this->isNew(), 'template' => '<div>%file%<br />%input%<br />%delete% %delete_label%</div>', )); $this->validatorSchema['logo_delete'] = new sfValidatorPass(); } // ... }
El widget
sfWidgetFormInputFileEditable
tiene varias opciones para modificar sus características y visualización:file_src
: La ruta web del archivo subidois_image
: Si estrue
, el archivo será mostrado como una imagenedit_mode
: Si el formulario está en modo de edición o nowith_delete
: Si se desea mostrar una casilla de verificación para eliminartemplate
: La plantilla a utilizar para mostrar el widget
El aspecto del Generador de Admin puede ser ajustado muy fácilmente ya que las plantillas definen una gran cantidad de atributos
class
y id
. Por ejemplo, el logotipo puede ser personalizado utilizando sf_admin_form_field_logo
. Cada campo también tiene una clase, dependiendo de el tipo de campo comosf_admin_text
o sf_admin_boolean
.
La opción
edit_mode
utiliza el método sfDoctrineRecord::isNew()
.
Devuelve
true
si el objeto del formulario es nuevo, y false
de lo contrario. Esto es de gran ayuda cuando es necesario que tengas diferentes widgets o validadores, dependiendo del estado del objeto invocado.Configuración de Filtros
La configuración de los filtros es la misma que la configuración de las vistas forms. Como cuestión de hecho, los filtros son sólo forms. Y como para los forms, las clases se han generado por la tarea
doctrine:build --all
. También puedes volver a generarlas con la tareadoctrine:build --filters
.
Estas clases se encuentran en el directorio
lib/filter/
y cada uno de clase del modelo tiene asociada una clase de filtros (JobeetJobFormFilter
para JobeetJobForm
).
Vamos a eliminarlas por completo para el módulo
category
:# apps/backend/modules/category/config/generator.yml config: filter: class: false
Para el módulo
job
, vamos a eliminar algunos de ellos:# apps/backend/modules/job/config/generator.yml filter: display: [category_id, company, position, description, is_activated, is_public, email, expires_at]
Como los filtros son siempre opcional, no hay necesidad de sobreescribir clase filtro para configurar los campos que se mostrarán.
Acciones Personalizadas
Cuando la configuración no es suficiente, puedes agregar nuevos métodos a la clase de acciones, como hemos visto con la característica de
extend
, pero también puede sobreescribir la acción de los métodos generados:Método | Descripción |
---|---|
executeIndex() | La acción de la vista list |
executeFilter() | Actualiza los filtros |
executeNew() | La acción de la vista new |
executeCreate() | Crea un nuevo Job |
executeEdit() | La acción de la vista edit |
executeUpdate() | Actualiza un Job |
executeDelete() | Borra un Job |
executeBatch() | Ejecuta una acción por lote |
executeBatchDelete() | Ejecuta la acción por lote _delete |
processForm() | Procesa el formualrio Job |
getFilters() | Devuelve los filtros actuales |
setFilters() | Establece los filtros |
getPager() | Devuelve el paginador de la lista |
getPage() | Obtiene la página de la lista |
setPage() | Establece la página de la lista |
buildCriteria() | Construye el Criteria para la lista |
addSortCriteria() | agrega un Criteria ordenado para la lista |
getSort() | Devuelve la columna utilizada para ordenar |
setSort() | Establece la columna utilizada para ordenar |
Como cada método generado hace solo una cosa, es fácil cambiar un comportamiento sin tener que copiar y pegar código demasiado.
Personalización de Plantillas
Hemos visto cómo personalizar las plantillas generadas gracias a los atributos
class
y id
añadidos por el Generador de administrador en el código HTML.
En cuanto a las clases, también puedes sobreescribir las plantillas originales. Como las plantillas son simples archivos PHP y no clases PHP , una plantilla puede ser sobreescritapor creando una plantilla del mismo nombre en el módulo (por ejemplo, en el directorio
apps/backend/modules/job/templates/
para el módulo de administración job
):Plantilla | Descripción |
---|---|
_assets.php | Muestra CSS y JS a usar por las plantillas |
_filters.php | Muestra los filtros |
_filters_field.php | Muestra un único filtro de campo |
_flashes.php | Muestra los mensajes flash |
_form.php | Muestra el formulario |
_form_actions.php | Muestra las acciones del formulario |
_form_field.php | Muestra un único campo de formulario |
_form_fieldset.php | Muestra un fieldset de formulario |
_form_footer.php | Muestra el pie de página del formulario |
_form_header.php | Muestra cabecera del formulario |
_list.php | Muestra la lista |
_list_actions.php | Muestra las acciones de lista |
_list_batch_actions.php | Muestra la lista de acciones por lotes |
_list_field_boolean.php | Muestra un único campo booleano en la lista |
_list_footer.php | Muestra el pie de página de lista |
_list_header.php | Muestra la cabecera de lista |
_list_td_actions.php | Muestra las acciones de objeto para una fila |
_list_td_batch_actions.php | Muestra la casilla de verificación para una fila |
_list_td_stacked.php | Muestra el stacked layout para una fila |
_list_td_tabular.php | Muestra un único campo de lista |
_list_th_stacked.php | Muestra un solo nombre de columna para la cabecera |
_list_th_tabular.php | Muestra un solo nombre de columna para la cabecera |
_pagination.php | Muestra la paginación de lista |
editSuccess.php | Muestra la vista edit |
indexSuccess.php | Muestra la vista list |
newSuccess.php | Muestra la vista new |
Configuración Final
La configuración final de la administración Jobeet es la siguiente:
# apps/backend/modules/job/config/generator.yml generator: class: sfDoctrineGenerator param: model_class: JobeetJob theme: admin non_verbose_templates: true with_show: false singular: ~ plural: ~ route_prefix: jobeet_job with_doctrine_route: true config: actions: ~ fields: is_activated: { label: Activated?, help: Whether the user has activated the job, or not } is_public: { label: Public? } list: title: Job Management layout: stacked display: [company, position, location, url, is_activated, email] params: | %%is_activated%% <small>%%JobeetCategory%%</small> - %%company%% (<em>%%email%%</em>) is looking for a %%=position%% (%%location%%) max_per_page: 10 sort: [expires_at, desc] batch_actions: _delete: ~ extend: ~ object_actions: extend: ~ _edit: ~ _delete: ~ actions: deleteNeverActivated: { label: Delete never activated jobs } table_method: retrieveBackendJobList filter: display: [category_id, company, position, description, is_activated, is_public, email, expires_at] form: class: BackendJobeetJobForm display: Content: [category_id, type, company, logo, url, position, location, description, how_to_apply, is_public, email] Admin: [_generated_token, is_activated, expires_at] edit: title: Editing Job "%%company%% is looking for a %%position%%" new: title: Job Creation # apps/backend/modules/category/config/generator.yml generator: class: sfDoctrineGenerator param: model_class: JobeetCategory theme: admin non_verbose_templates: true with_show: false singular: ~ plural: ~ route_prefix: jobeet_category with_doctrine_route: true config: actions: ~ fields: ~ list: title: Category Management display: [=name, slug] batch_actions: {} object_actions: {} filter: class: false form: actions: _delete: ~ _list: ~ _save: ~ edit: title: Editing Category "%%name%%" new: title: New Category
Con tan sólo estos dos archivos de configuración, hemos desarrollado una excelente interfaz backend para Jobeet en cuestión de minutos.
Ya sabes que cuando algo es configurable en un archivo YAML, hay también la posibilidad de usar código PHP. Para el Generador de administrador, puedes editar
apps/backend/modules/job/lib/jobGeneratorConfiguration.class.php
. Te da las mismas opciones que YAML pero con un archivo PHP. Para aprender los nombre de los métodos, echar un vistazo a la clase base generada encache/backend/dev/modules/autoJob/lib/BaseJobGeneratorConfiguration.class.php
.
Suscribirse a:
Enviar comentarios
(
Atom
)
Sígueme en las Redes Sociales
Donaciones
Datos personales
Entradas populares
-
En este apartado vamos a explicar como ejercutar archivos PHP a través del terminal de Ubuntu. Lo primero que tendríamos que hacer es inst...
-
En este blog voy a comentar un tema que se utilizan en casi todas las páginas web que existen, y es el tema de la paginación. La paginaci...
-
Este post trata de la integración de la librería PHPExcel en Codeigniter, aunque se podría aplicar a cualquier librería, como por ejemplo mP...
-
Ejemplo para añadir o sumar un número determinado de hora/s, minuto/s, segundo/s a una fecha en php. Con la función strtotime se puede ...
-
Este tema es uno de los temas primordiales sobre el framework Codeigniter, ya que en alguna ocación nos hemos visto obligados a recoger dato...
© Espacio Daycry - Espacio de programación 2013 . Powered by Bootstrap , Blogger templates and RWD Testing Tool
No hay comentarios :
Publicar un comentario