Un Gran Agradecimiento
Una gran cantidad de cosas impresionantes, se han añadido recientemente a la proxima gran entrega de symfony 1.2. Fabien ha trabajado muy duro para agregar sin dudas las más sofisticadas características que cualquier otro framework PHP pudiera tener hoy en día. No sólo son características agradables sino que fueron implementadas Orientada a Objeto por lo que ha sido sencillo para mi implmentar algunas características con otro ORM, Doctrine.Todo esto se hizo con muy poco trabajo de mi parte. Por lo tanto, doy muchas gracias a él y disfruta de este artículo.
Ejemplo del mundo real
En este artículo voy a empezar desde el principio con un nuevo proyecto symfony 1.2 para que puedas ponerte en marcha con Doctrine. Vamos a utilizar un esquema típico de tu sistema de gestión de contenidos. El esquema consta de los artículos, los autores y categorías donde los artículos son internacionalizados.
Comienza tu Proyecto
En primer lugar necesitas inicializar un nuevo proyecto symfony 1.2 y inicializar un app backend. Asegúrate de que estás utilizando el último código del svn pues beta1 no incluye esta funcionalidad de Doctrine.
Generar tu proyecto
mkdir cms cd cms symfony generate:project cms
Generar la aplicación backend
symfony generate:app backend
Todo el mundo encienda su Doctrine
Ahora tenemos que habilitar Doctrine y desactivar Propel 🙂 Edita tu config/ProjectConfiguration.class.php
y agregue el código siguiente a tu función setup()
.
public function setup() { $this->enablePlugins(array('sfDoctrinePlugin')); $this->disablePlugins(array('sfPropelPlugin')); }
Ahora que Doctrine está habilitado podemos enumerar las tareas disponibles de Doctrine:
./symfony list doctrine Available tasks for the "doctrine" namespace: :build-all Generates Doctrine model, SQL and initializes the database (doctrine-build-all) :build-all-load Generates Doctrine model, SQL, initializes database, and load data (doctrine-build-all-load) :build-all-reload Generates Doctrine model, SQL, initializes database, and load data (doctrine-build-all-reload) :build-all-reload-test-all Generates Doctrine model, SQL, initializes database, load data and run all test suites (doctrine-build-all-reload-test-all) :build-db Creates database for current model (doctrine-build-db) :build-filters Creates filter form classes for the current model :build-forms Creates form classes for the current model (doctrine-build-forms) :build-model Creates classes for the current model (doctrine-build-model) :build-schema Creates a schema from an existing database (doctrine-build-schema) :build-sql Creates SQL for the current model (doctrine-build-sql) :data-dump Dumps data to the fixtures directory (doctrine-dump-data) :data-load Loads data from fixtures directory (doctrine-load-data) :dql Execute a DQL query and view the results (doctrine-dql) :drop-db Drops database for current model (doctrine-drop-db) :generate-admin Generates a Doctrine admin module :generate-migration Generate migration class (doctrine-generate-migration) :generate-migrations-db Generate migration classes from existing database connections (doctrine-generate-migrations-db, doctrine-gen-migrations-from-db) :generate-migrations-models Generate migration classes from an existing set of models (doctrine-generate-migrations-models, doctrine-gen-migrations-from-models) :generate-module Generates a Doctrine module (doctrine-generate-crud, doctrine:generate-crud) :generate-module-for-route Generates a Doctrine module for a route definition :insert-sql Inserts SQL for current model (doctrine-insert-sql) :migrate Migrates database to current/specified version (doctrine-migrate) :rebuild-db Creates database for current model (doctrine-rebuild-db)
El Esquema
Ahora empieza la diversión. Tenemos Doctrine habilitado por lo que la primera cosa que necesitamos es definir nuestro esquema del CMS en config/doctrine/schema.yml
.
--- Article: actAs: Timestampable: I18n: fields: [title, content] columns: author_id: integer status: type: enum values: [Draft, Published] notnull: true title: type: string(255) notnull: true content: type: clob notnull: true is_on_homepage: boolean published_at: timestamp relations: Author: foreignAlias: Articles Categories: class: Category refClass: ArticleCategory foreignAlias: Articles Category: columns: name: type: string(255) notnull: true Author: columns: name: type: string(255) notnull: true about: string(1000) ArticleCategory: columns: article_id: integer category_id: integer relations: Article: foreignAlias: ArticleCategories Category: foreignAlias: ArticleCategories
Datos Fixtures
Tenemos nuestro esquema, ahora necesitamos algunos datos para ponerlo a prueba por lo que copia de los siguientes YAML en data/fixtures/data.yml
--- Article: Article_1: Author: jwage status: Published is_on_homepage: true published_at: '' Categories: [article, ontheedge] Translation: en: title: symfony 1.2 and Doctrine content: Article about the new Doctrine integration in symfony 1.2 fr: title: symfony 1.2 et doctrine content: Article sur l'intégration de Doctrine dans symfony 1.2 Author: jwage: name: Jonathan H. Wage about: Jonathan is the lead developer of the Doctrine project and is also a core contributor to the symfony project. Category: article: name: Article tutorial: name: Tutorial ontheedge: name: Living on the edge
Desarrollo y Pruebas
Ahora que tenemos nuestro esquema y los datos tenemos todo lo que necesitamos para inicializar nuestra base de datos, modelos, formularios, datos, etc. Esto puede ser hecho con el muy simplemente comando siguiente:
./symfony doctrine:build-all-reload --no-confirmation >> doctrine dropping databases >> doctrine creating databases >> doctrine generating model classes >> doctrine generating sql for models >> doctrine generating form classes >> doctrine generating filter form classes >> doctrine created tables successfully >> doctrine loading data fixtures from "/Us...ymfony12doctrine/data/fixtures"
Eso fue demasiado fácil, cuando se va a poner difícil? Ahora vamos a hacer la inspección con DQL para ver los datos que se cargaron correctamente.
./symfony doctrine:dql "FROM Article a, a.Author a2, a.Translation t" >> doctrine executing dql query DQL: FROM Article a, a.Author a2, a.Translation t found 1 results - id: '1' author_id: '1' status: Published is_on_homepage: true published_at: '2008-11-06 04:37:11' created_at: '2008-11-06 16:37:11' updated_at: '2008-11-06 16:37:11' Author: id: '1' name: 'Jonathan H. Wage' about: 'Jonathan is the lead developer of the Doctrine project and is also a core contributor to the symfony project.' Translation: en: id: '1' title: 'symfony 1.2 and Doctrine' content: 'Article about the new Doctrine integration in symfony 1.2' lang: en fr: id: '1' title: 'symfony 1.2 et doctrine' content: 'Article sur l''intégration de Doctrine dans symfony 1.2' lang: fr
Esto puede ser su primer gusto del Doctrine Query Language, también conocido como DQL. Se parece mucho a SQL no? Cierre la boca, que estás babeando.
Para aquellos de ustedes que no quieran escribir cadenas de DQL, no te preocupes tenemos un completo objeto Doctrine_Query
para la construcción de tus consultas.
$q = Doctrine_Query::create() ->from('Article a, a.Author a2, a.Translation t'); $articles = $q->execute();
Generadores de Admin
Ahora que tenemos todo construido, podemos empezar a generar algunas cosas con la magia symfony. Vamos a empezar por la definición de la Colecciones de Rutas para la gestión de nuestros artículos, autores y categorías. Abre apps/backend/config/routing.yml
en tu editor y pega las siguientes rutas dentro.
articles: class: sfDoctrineRouteCollection options: model: Article module: articles with_show: true collection_actions: { filter: post, batch: post } categories: class: sfDoctrineRouteCollection options: model: Category module: categories with_show: true collection_actions: { filter: post, batch: post } authors: class: sfDoctrineRouteCollection options: model: Author module: authors with_show: true collection_actions: { filter: post, batch: post }
Estas rutas nos permitirán generar un módulo generador de administrador para la gestión de los datos a través de cada uno de los modelos de Doctrine. Ejecute los siguientes comandos para generar los tres módulos.
./symfony doctrine:generate-admin backend articles $ ./symfony doctrine:generate-admin backend categories $ ./symfony doctrine:generate-admin backend authors
Ahora, cuando tiene acceso al modulo categories
desde el backend deberías ver lo siguiente.
Ahora deseas, perrsonalizar un poco los artículos generados de admin para mostrar sólo una serie de campos en la lista y los filtros del formulario. Puedes hacerlo editando el generator.yml
situado en apps/backend/modules/categories/config/generator.yml
.
config: list: display: [title, published_at, is_on_homepage, status] filter: class: ArticleFormFilter display: [author_id, status, is_on_homepage, published_at, categories_list]
Puedes personalizar los otros módulos de la misma forma.
Ahora, si cambia la URL ?sf_culture=fr
en su URL el título se mostrará la versión en francés. Debería ver lo siguiente en su navegador cuando levantes hacia arriba el módulo de artículos.
Edición de Traducciones
Ahora, ¿cómo trabaja la edición de esas traducciones? Si te acuerdas de esto con los antiguos generadores de admin, era casi imposible. Ahora, se trata de una cuestión de añadir una línea de código a nuestro ArticleForm
. Todo que tenemos que hacer es embeber/integrar/fusionar el formulario ArticleTranslation
. Esto se puede hacer editando lib/form/doctrine/ArticleForm.class.php
y añadiendo el código siguiente al configure()
public function configure() { $this->embedI18n(array('en', 'fr')); }
Ahora, cuando editas un artículo, verás que tienes la capacidad de editar directamente las traducciones dentro del artículo.
Ahora que está muy bien pero que si deseas editar el Author
directamente dentro del Article
como así y que se cree un nuevo Author
si no existe y el uso del actual Author
si ese nombre existe. Esto es simple.
Editar / Añadir autor
Con el fin de añadir la funcionalidad descrita anteriormente tenemos que añadir un poco de código en tres lugares diferentes. En primer lugar necesitas embeber el formulario Author
en el formulario Article
editando lib/form/doctrine/ArticleForm.class.php
y agregue el código siguiente.
public function configure() { unset($this['author_id']); $authorForm = new AuthorForm($this->getObject()->getAuthor()); unset($authorForm['about']); $this->embedForm('Author', $authorForm); $this->embedI18n(array('en', 'fr')); }
Optionally Optimize Queries
Ahora podemos modificar las acciones de la clase
articles
para que se le sumen elAutor
y laTraducción
a fin de que los datos se sumen a la consulta principal y no perezosamente cargado por Doctrine invocando adicionales consultas a la base de datos. Este paso es opcional. Abreapps/backend/modules/articles/actions/actions.class.php
y sobreescribe la funciónexecuteEdit()
.public function executeEdit(sfWebRequest $request) { $this->article = Doctrine_Query::create() ->from('Article a') ->leftJoin('a.Author a2') ->leftJoin('a.Translation t') ->where('a.id = ?', $request->getParameter('id')) ->fetchOne(); $this->form = $this->configuration->getForm($this->article); }
Ahora, al ver la la edición y la creación de un artículo verá el formulario incorporado para el autor con la información existente.
Ahora el último paso es decir a Doctrine para que busque objetos existentes de Author
la hora de establecer el nombre para que un nombre duplicado de Author
no sea creado. Edita lib/model/doctrine/Author.class.php
y sobreescribe el name
mutator añadiendo el siguiente código.
public function setName($name) { $name = trim($name); $found = Doctrine_Query::create() ->select('a.id') ->from('Author a') ->where('a.name = ?', $name) ->fetchOne(array(), Doctrine::HYDRATE_ARRAY); if ($found) { $this->assignIdentifier($found['id']); } else { $this->_set('name', $name); } }
El código anterior será para verificar si un Author
con el nombre pasado ya existe, Si existe se asignará el identificador del registro encontrado de lo contrario es lo que normalmente haría, establecer el nombre para el objeto.
Los métodos
_set()
y_get()
deben utilizarse para evitar un bucle infinito cuando se sobrescriben accessors y mutators.
Wow, lo que realmente acaba de construir todo un backend para gestionar el contenido de un simple sistema de gestión de contenido en menos de una hora? Estamos seguros de que sí. Pruébalo y disfruta el desarrollo de ricas funcionalidad basada en web utilizando symfony y Doctrine.
La presente, es una traducción al castellano realizada por Roberto G. Puentes Diaz,
sobre el artículo new-in-symfony-1-2-doctrine-goodies de Jonathan Wage.
|
Este trabajo esta licenciado bajo una Licencia Creative Commons Atribución-No Comercial-Sin Obras Derivadas 3.0 Unported. |