jueves, 21 de agosto de 2014
Symfony - Día 3: El modelo de datos
Aquellos de ustedes locos por abrir el editor de texto y hacer algo de PHP estarán encantados de saber que el tutorial de hoy nos introduce algo en el desarrollo. Vamos a definir el modelo de datos Jobeet, utilizaremos un ORM para interactuar con la base de datos, y construiremos el primer módulo de la aplicación. Pero como Symfony hace una gran parte del trabajo por nosotros, vamos a tener un módulo web totalmente funcional sin tener que escribir mucho código PHP.
El Modelo Relacional
Los casos de uso que escribimos ayer describen los principales objetos de nuestro proyecto: puestos de trabajo, afiliados, y las categorías. Aquí está el correspondiente diagrama de entidad relación:
Además de las columnas descritas en los casos de uso, también hemos añadido un campo
created_at
a algunas tablas. Symfony reconoce estos campos y establece el valor de la hora actual de sistema cuando un registro es creado. Esto es lo mismo para el campo updated_at
: su valor se establece siempre a la hora del sistema en que el registro se actualiza.El Esquema
Para almacenar los puestos de trabajo, los afiliados, y las categorías, es evidente que necesitamos una base de datos relacional.
Pero como Symfony es un Framework Orientado a Objetos, nos gustaría manipular los objetos cada vez que podamos. Por ejemplo, en lugar de escribir sentencias SQL para recuperar los registros de la base de datos, preferimos más usar los objetos.
La información de la base de datos relacional debe ser mapeada a un modelo de objetos. Esto se puede hacer con una herramienta ORM, o Mapeador, y afortunadamente, Symfony tiene incluido dos de ellas: Propel y Doctrine. En este tutorial, usaremos Doctrine.
El ORM necesita una descripción de las tablas y sus relaciones para crear las clases relacionadas. Hay dos maneras de crear este esquema de descripción: ingeniería reversa de una base de datos existente o creándolo a mano.
Como la base de datos no existe todavía, y como queremos mantener la base de datos Jobeet agnóstica, vamos a crear el archivo de esquema a mano editando el archivo vacío
config/doctrine/schema.yml
:# config/doctrine/schema.yml --- JobeetCategory: actAs: { Timestampable: ~ } columns: name: { type: string(255), notnull: true, unique: true } JobeetJob: actAs: { Timestampable: ~ } columns: category_id: { type: integer, notnull: true } type: { type: string(255) } company: { type: string(255), notnull: true } logo: { type: string(255) } url: { type: string(255) } position: { type: string(255), notnull: true } location: { type: string(255), notnull: true } description: { type: string(4000), notnull: true } how_to_apply: { type: string(4000), notnull: true } token: { type: string(255), notnull: true, unique: true } is_public: { type: boolean, notnull: true, default: 1 } is_activated: { type: boolean, notnull: true, default: 0 } email: { type: string(255), notnull: true } expires_at: { type: timestamp, notnull: true } relations: JobeetCategory: { onDelete: CASCADE, local: category_id, foreign: id, foreignAlias: JobeetJobs } JobeetAffiliate: actAs: { Timestampable: ~ } columns: url: { type: string(255), notnull: true } email: { type: string(255), notnull: true, unique: true } token: { type: string(255), notnull: true } is_active: { type: boolean, notnull: true, default: 0 } relations: JobeetCategories: class: JobeetCategory refClass: JobeetCategoryAffiliate local: affiliate_id foreign: category_id foreignAlias: JobeetAffiliates JobeetCategoryAffiliate: columns: category_id: { type: integer, primary: true } affiliate_id: { type: integer, primary: true } relations: JobeetCategory: { onDelete: CASCADE, local: category_id, foreign: id } JobeetAffiliate: { onDelete: CASCADE, local: affiliate_id, foreign: id }
Si decidiste crear las tablas escribiendo las sentencias SQL, puedes generar el archivo de configuración del esquema correspondiente
ejecutando la tarea
schema.yml
,ejecutando la tarea
doctrine:build-schema
:$ php symfony doctrine:build-schema
La anterior tarea requiere que tengas la base de datos configurada en el
databases.yml
. Te mostraremos como configurar la base de datos en un paso posterior. Si tratas de ejecutar esta tarea ahora no funcionará ya que no sabe que base de datos contruir para el esquema.
El esquema es la traducción directa de un diagrama de entidad relación en formato YAML.
El Formato YAML
De acuerdo con el sitio web oficial YAML, YAML es "es una serialización de datos estándar muy amigable para todos los lenguajes de programación"
Dicho de otra manera, YAML es un lenguaje sencillo para describir los datos (strings, integers, dates, arrays, y hashes).
En YAML, la estructura se muestra a través de la sangría, la secuencia de elementos se denotan por un guión, y los pares clave/valor están separados por dos puntos. YAML también tiene una sintaxis abreviada para describir la misma estructura con menos líneas, donde los arrays explícitamente se muestran con
[]
y los hashes o array asociativos con {}
.
Si todavía no están familiarizados con YAML, es hora de empezar con él pues el framework Symfony lo utiliza ampliamente para sus archivos de configuración. Un buen punto de partida es la documentación del componente YAML de Symfony.
Hay una cosa importante que necesitas recordar cuando estás editando un archivo YAML: la indentación debe hacerse con uno o mas espacios en blanco, pero nunca con tabulaciones.
El archivo
schema.yml
contiene la descripción de todas las tablas y sus columnas. Cada columna se describe con la siguiente información:type
: El tipo de columna (boolean
,integer
,float
,decimal
,string
,array
,object
,blob
,clob
,timestamp
,time
,date
,enum
,gzip
)notnull
: Estrue
si deseas que la columna sea obligadoriaunique
: Estrue
si deseas crear un índice único para la columna.
El atributo
relacionados serán automáticamente eliminados de la base de datos
onDelete
define el comportamiento ON DELETE
para claves foráneas, y Doctrine da soporte para CASCADE
, SETNULL
, y RESTRICT
. Por ejemplo, cuando un registro de job
es borrado, todos los registros jobeet_category_affiliate
relacionados serán automáticamente eliminados de la base de datos
Nota del Traductor Si bien no es probable, es posible que se quiera eliminar una categoría completa del sistema.
El campo category_id de la entidad JobeetJob, tiene al final añadida la opción onDelete. La misma está establecida como
CASCADE
, para determinar que cuando una categoría sea eliminada, tambien se eliminen los registros job
(los puestos de trabajo).
Esto se debe a que los registros
job
tienen establecido como campo obligatorio a la categoría asociada (a la que pertenece), por lo que no sería correcto dejarhuerfano
a un registro, ni establecer éste valor a null
.La Base De Datos
El framework Symfony soporta todas las Base De Datos soportadas por PDO (MySQL, PostgreSQL, SQLite, Oracle, MSSQL, ...). PDO es la capa de abstracción de base de datos que viene con PHP.
Vamos a usar MySQL para este tutorial:
$ mysqladmin -uroot -p create jobeet
Enter password: mYsEcret ## La clave se mostrará como ********
Eres libre de elegir otro motor de base de datos si lo deseas. No será difícil adaptar el código que vamos a escribir ya que vamos a utilizar el ORM quien será quien escriba el SQL por nosotros.
Tenemos que decirle a Symfony que base de datos usar para el proyecto Jobeet:
$ php symfony configure:database "mysql:host=localhost;dbname=jobeet" root mYsEcret
La tarea
configure:database
emplea tres argumentos: el PDO DSN, el nombre de usuario, y la clave de acceso a la base de datos. Si no tienes ninguna contraseña de la base en el servidor de desarrollo, basta con omitir el tercer argumento.
La tarea
configure:database
almacena la configuración de la base de datos en el archivo de configuración config/databases.yml
. En lugar de utilizar la tarea, puedes editar este archivo manualmente.
Pasar la clave de la base de datos por linea de comandos es conveniente peroinseguro. Dependiendo de quienes tiene acceso a tu entorno, podría ser mejor editar el
config/databases.yml
para cambiar la clave. Desde luego, para mantener la clave a salvo, el acceso al archivo de configuración debería también ser restringido.El ORM
Gracias al archivo de descripción de la base de datos
schema.yml
, podemos utilizar algunas de las tareas de Doctrine para generar los comandos SQL necesarios para crear tablas de la base de datos:
Primero para generar el SQL debes contruir el modelo apartir de tus archivos esquema.
$ php symfony doctrine:build --model
Ahora que tus modelos existen puedes generar e insertar el SQL.
$ php symfony doctrine:build --sql
La tarea
doctrine:build --sql
genera comandos SQL en el directorio data/sql/
, optimizado para el motor de base de datos que hemos configurado:# snippet from data/sql/schema.sql CREATE TABLE jobeet_category (id BIGINT AUTO_INCREMENT, name VARCHAR(255) NOT NULL COMMENT 'test', created_at DATETIME, updated_at DATETIME, slug VARCHAR(255), UNIQUE INDEX sluggable_idx (slug), PRIMARY KEY(id)) ENGINE = INNODB;
Para realmente crear las tablas en la base de datos, necesitas ejecutar la tarea
doctrine:insert-sql
:$ php symfony doctrine:insert-sql
Como con cualquier herramienta de línea de comandos, las tareas symfony pueden tener argumentos y opciones. Cada tarea viene con un mensaje de ayuda que se puede mostrar ejecutando la tarea
help
:$ php symfony help doctrine:insert-sql
El mensaje de ayuda lista de todos los posibles argumentos y opciones, da los valores por defecto para cada uno de ellos, y proporciona algunos ejemplos útiles.
El ORM también genera las clases PHP que mapea los registros de la tabla con los objetos:
$ php symfony doctrine:build --model
La tarea
doctrine:build --model
genera archivos PHP en el directorio lib/model/
que se pueden utilizar para interactuar con la base de datos. Navegando por los archivos generados, probablemente habrás notado que Doctrine genera cuatro clases por tabla. Para la tablajobeet_job
:JobeetJob
: Un objeto de esta clase representa un único registro de la tablajobeet_job
. La clase está vacía por defecto.BaseJobeetJob
: La clase padre deJobeetJob
. Cada vez que ejecutasdoctrine:build --model
, esta clase es sobreescrita, por lo que todas las personalizaciones se deben hacer en la claseJobeetJob
.JobeetJobTable
: La clase define los métodos que mayormente devuelve coleccionesde objetosJobeetJob
. La clase está vacía por defecto.
Los valores de las columnas de un registro se pueden manipular con el modelo de objetos mediante el uso de algunos métodos
get*()
y métodos set*()
:$job = new JobeetJob(); $job->setPosition('Web developer'); $job->save(); echo $job->getPosition(); $job->delete();
También puedes definir claves foráneas directamente por la vinculación de objetos:
$category = new JobeetCategory(); $category->setName('Programming'); $job = new JobeetJob(); $job->setCategory($category);
La tarea
doctrine:build --all
es un acceso directo para las tareas que han ejecutado en esta sección y algunas más. Por lo tanto, ejecuta esta tarea ahora para generar formularios y validadores para el modelo de clases de Jobeet:$ php symfony doctrine:build --all --no-confirmation
Verás los validadores en acción al final del día y los formularios se explicarán en gran detalle en el día 10.
Los Datos Iniciales
Los tablas se han creado en la base de datos, pero no hay datos en ellas. Para cualquier aplicación web, hay tres tipos de datos:
- Datos Iniciales: Los datos iniciales son necesarios para que la aplicación funcione. Por ejemplo, Jobeet necesita algunas categorías iniciales. Si no, nadie será capaz de envíar un puesto de trabajo. También necesitamos un administrador de usuarios para poder acceder al backend.
- Datos de Prueba: Los datos de prueba son necesarios para que la aplicación sea probada. Como desarrollador, escribes pruebas para asegurarte que Jobeet se comporta como se describe en los casos de uso, y la mejor manera es escribir pruebas automáticas. Por lo tanto, cada vez que ejecutes tus pruebas, necesitas una base de datos limpia con algunos datos nuevos de prueba en ella.
- Los Datos del Usuario: Los datos del usuario son creados por los usuarios durante la vida normal de la aplicación.
Cada vez que Symfony crea las tablas en la base de datos, todos los datos se pierden. Para rellenar la base de datos con algunos los datos iniciales, podriamos crear un script PHP, o ejecutar sentencias SQL con algun programa
mysql
. Pero como la necesidad es bastante común, hay una mejor manera con Symfony: crear archivos YAML en el directorio data/fixtures/
y usar la tarea doctrine:data-load
para cargarlos en la base de datos.
Primero, crea los siguientes archivos de datos:
# data/fixtures/categories.yml JobeetCategory: design: name: Design programming: name: Programming manager: name: Manager administrator: name: Administrator # data/fixtures/jobs.yml JobeetJob: job_sensio_labs: JobeetCategory: programming type: full-time company: Sensio Labs logo: sensio-labs.gif url: http://www.sensiolabs.com/ position: Web Developer location: Paris, France description: | You've already developed websites with symfony and you want to work with Open-Source technologies. You have a minimum of 3 years experience in web development with PHP or Java and you wish to participate to development of Web 2.0 sites using the best frameworks available. how_to_apply: | Send your resume to fabien.potencier [at] sensio.com is_public: true is_activated: true token: job_sensio_labs email: job@example.com expires_at: '2008-10-10' job_extreme_sensio: JobeetCategory: design type: part-time company: Extreme Sensio logo: extreme-sensio.gif url: http://www.extreme-sensio.com/ position: Web Designer location: Paris, France description: | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in. Voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. how_to_apply: | Send your resume to fabien.potencier [at] sensio.com is_public: true is_activated: true token: job_extreme_sensio email: job@example.com expires_at: '2008-10-10'
El archivo de datos de puestos de trabajo hace referencia a dos imágenes. Puedes descargarlas de
(
(
/get/jobeet/sensio-labs.gif
, /get/jobeet/extreme-sensio.gif
) y colocarlas en el directorio uploads/jobs/
.
Esta escrito en YAML, y define el modelo de objetos, etiquetados con un nombre único (por ejemplo, hemos definido dos puestos de trabajo etiquetados con
job_sensio_labs
yjob_extreme_sensio
). Estas etiquetas son de gran utilidad para vincular objetos relacionados sin tener que definir claves primarias (que a menudo son auto-incrementales y no se pueden establecer). Por ejemplo, la categoría de job_sensio_labs
es programming
, que es la etiqueta dada a la categoría 'Programming'.
En un archivo YAML, cuado una cadena tiene saltos de linea (como la columna
description
en el archivo de datos de puestos de trabajo), puedes usar la barra vertical (|
) para indicar que la cadena aparecerá en varias lineas.
A través de un archivo de datos se puede tener objetos de uno o varios modelos, hemos decidido crear un archivo por cada modelo para los datos de Jobeet.
Propel require que los archivos de datos tengan un prefijo númerico que determine el orden en el cual los archivos serán cargados. Con Doctrine esto no es necesario ya que todos los archivos serán cargados y guardados en el correcto orden para asegurar
que las claves foraneas sean adecuadas.
que las claves foraneas sean adecuadas.
En un archivo de datos, no es necesario definir todos los valores de las columnas. Si no que, Symfony utilizará el valor predeterminado definido en el esquema de base de datos. Y como Symfony usa Doctrine para cargar los datos en la base de datos, todos los comportamientos incorporados (como el seteo automático a
created_at
o updated_at
), o los comportamientos personalizados que puedes haber agregado al modelo de las clases son activados.
La carga de los datos iniciales en la base de datos es tan simple como ejecutar la tarea
doctrine:data-load
:$ php symfony doctrine:data-load
La tarea
doctrine:build --all --and-load
es un atajo para la tareadoctrine:build --all
seguida de la tarea doctrine:data-load
.
Ejecuta la tarea
doctrine:build --all --and-load
para asegurarte que todo es generado desde tu esquema. Esto generará tus formularios, filtros, modelos, vaciará tu base de datos y la re-creará de nuevo con todas las tablas.$ php symfony doctrine:build --all --and-load
Miralo en acción en el Navegador
Hemos utilizado la interfaz de línea de comandos mucho, pero eso no es realmente emocionante, especialmente para un proyecto web. Ahora tenemos todo lo que necesitamos para crear páginas Web que interactúan con la base de datos.
Vamos a ver cómo mostrar la lista de puestos de trabajo, cómo editar un trabajo, y cómo eliminar un puesto de trabajo. Como se explicó durante el día 1, un proyecto symfony se hace de las aplicaciones. Cada aplicación está dividida en módulos. Un módulo es un conjunto de código PHP auto-contenido que representa una característica de la aplicación (el módulo API, por ejemplo), o un conjunto de manipulaciones que el usuario puede hacer sobre un objeto del modelo (un módulo job, por ejemplo).
Symfony es capaz de generar automáticamente un módulo para un determinado modelo que proporciona las características básicas de manipulación:
$ php symfony doctrine:generate-module --with-show --non-verbose-templates frontend job JobeetJob
La tarea
doctrine:generate-module
genera módulo job
en la aplicación frontend
para el modeloJobeetJob
. Como con la mayoría de las tareas symfony, algunos archivos y directorios se han creado para ti bajo el directorio apps/frontend/modules/job/
:Directorio | Descripción |
---|---|
actions/ | Acciones del módulo |
templates/ | Plantillas del módulo |
El archivo
actions/actions.class.php
define todas las acciones disponibles para el módulojob
:Nombre de la acción | Descripción |
---|---|
index | Muestra los registros de la tabla |
show | Muestra los campos de un registro |
new | Muestra un formulario para crear un nuevo registro |
create | Crea un nuevo registro |
edit | Muestra un formulario para crear editar un registro existente |
update | Actualiza un registro con los valores que envió el usuario |
delete | Borra un registro de la tabla |
Ahora puedes probar el módulo job en un navegador:
http://www.jobeet.com.localhost/frontend_dev.php/job
Si intentas editar un puesto de trabajo, te darás cuenta que el combo Category id tiene una lista
de todos los nombres de las categorías. El valor de cada opción esta dado por el método
de todos los nombres de las categorías. El valor de cada opción esta dado por el método
__toString()
.
Doctrine tratará de dar un método base
__toString()
adivinandolo de una columna descriptiva de nombre como ser, title
, name
, subject
, etc. Si quieres algo distinto entonces necesitas agregar tus propios métodos __toString()
como se ve más abajo. El modelo JobeetCategory
esta listo para adivinar el método __toString()
usando la columna name
de la tablajobeet_category
.// lib/model/doctrine/JobeetJob.class.php class JobeetJob extends BaseJobeetJob { public function __toString() { return sprintf('%s at %s (%s)', $this->getPosition(), $this->getCompany(), $this->getLocation()); } } // lib/model/doctrine/JobeetAffiliate.class.php class JobeetAffiliate extends BaseJobeetAffiliate { public function __toString() { return $this->getUrl(); } }
Ahora puedes editar y crear puestos de trabajo. Trata de dejar un campo obligatorio en blanco, o tratar de dar una fecha no válida. Así es, Symfony ha creado básicas reglas de validación analizando el esquema de base de datos.
Referencia: http://symfony.com/legacy/doc/jobeet/1_4/es/03?orm=Doctrine
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