Curso Symfony 2: Instalando Bundles de Terceros
En reiteradas ocaciones existe la necesidad de implementar librerías de terceros en nuestros proyectos con el objetivo primordial de aprovecharlas, reutilizar código y mejorar nuestros tiempos de entrega; como ya saben en Symfony 2 todo se distribuye en forma de “Bundles” y las librerías de terceros no son la excepción, además en symfony2bundles.org (actualmente http://knpbundles.com/) podrás encontrar miles de bundles que podrías necesitar.
En este capítulo nos concentraremos en la instalación de uno de los Bundles más atractivos para incluir en nuestros proyectos, se trata del StofDoctrineExtensionsBundle por Christophe Coevoet el cual hace una implementación del complemento Doctrine2 behavioral extensions creado por Gediminas Morkevicius, cuyo propósito es proveer de los aclamados Comportamientos (behaviors) de Doctrine; en general explicaremos la configuración de 3 comportamientos ampliamente utilizados, como lo son Timestampable, Sluggable y Loggable, reiteramos que el objetivo del capítulo es la instalación de Bundles de Terceros en Symfony2 y no el de profundizar en todos los comportamientos que provee el StofDoctrineExtensionsBundle.
Paso 1: Instalando el Bundle
En Symfony 2 existen varias formas para la instalación de los bundles de terceros, entre ellas (y la más práctica) es la instalación por medio de submodule en GIT, si no sabes que es GIT te recomiendo visitar http://progit.org/book/es/ y tratar de instalarlo en tu sistema.
Comenzamos instalando la librería principal de Doctrine Behavioral Extensions, para ello accedemos a nuestra consola, nos ubicamos en la raíz del proyecto de symfony y ejecutamos:
~$ git submodule add git://github.com/l3pp4rd/DoctrineExtensions.git vendor/gedmo-doctrine-extensions
[tipexperto titulo = “Nota”]Si tienes problemas puedes descargar manualmente el paquete, sólo debes descomprimir su contenido y copiarlo al directorio /ruta_hacia_proyecto/vendor/gedmo-doctrine-extensions[/tipexperto]
Ahora si añadimos el StofDoctrineExtensionsBundle:
~$ git submodule add git://github.com/stof/StofDoctrineExtensionsBundle.git vendor/bundles/Stof/DoctrineExtensionsBundle
[tipexperto titulo = “Nota”]Si tienes problemas puedes descargar manualmente el paquete, sólo debes descomprimir su contenido y copiarlo al directorio /ruta_hacia_proyecto/vendor/bundles/Stof/DoctrineExtensionsBundle.[/tipexperto]
Registramos los Namespaces en nuestro app/autoload.php:
<?php use Symfony\Component\ClassLoader\UniversalClassLoader; use Doctrine\Common\Annotations\AnnotationRegistry; $loader = new UniversalClassLoader(); $loader->registerNamespaces(array( 'Symfony' => array(__DIR__.'/../vendor/symfony/src', __DIR__.'/../vendor/bundles'), 'Sensio' => __DIR__.'/../vendor/bundles', 'JMS' => __DIR__.'/../vendor/bundles', 'Doctrine\\Common' => __DIR__.'/../vendor/doctrine-common/lib', 'Doctrine\\DBAL' => __DIR__.'/../vendor/doctrine-dbal/lib', 'Doctrine' => __DIR__.'/../vendor/doctrine/lib', 'Monolog' => __DIR__.'/../vendor/monolog/src', 'Assetic' => __DIR__.'/../vendor/assetic/src', 'Metadata' => __DIR__.'/../vendor/metadata/src', // Aquí registramos: 'Stof' => __DIR__.'/../vendor/bundles', 'Gedmo' => __DIR__.'/../vendor/gedmo-doctrine-extensions/lib', )); // ... resto del archivo
Añadimos el Bundle a nuestro app/AppKernel.php:
<?php use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\Config\Loader\LoaderInterface; class AppKernel extends Kernel { public function registerBundles() { $bundles = array( new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), new Symfony\Bundle\SecurityBundle\SecurityBundle(), new Symfony\Bundle\TwigBundle\TwigBundle(), new Symfony\Bundle\MonologBundle\MonologBundle(), new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(), new Symfony\Bundle\DoctrineBundle\DoctrineBundle(), new Symfony\Bundle\AsseticBundle\AsseticBundle(), new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(), new MDW\BlogBundle\MDWBlogBundle(), new MDW\DemoBundle\MDWDemoBundle(), // Aquí Añadimos: new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(), ); // ... resto del archivo
Básicamente es todo lo que se realiza para incluir un Bundle.
Paso 2: Configurando el Bundle
En nuestro caso el StofDoctrineExtensionsBundle para funcionar requiere agregar configuración adicional al archivo app/config/config.yml de la aplicación (la mayoría de los bundles pueden detallar tales configuraciones en su documentación), para ello agregamos estos segmentos:
En la sección Doctrine Configuration, añadimos al final el mapping para StofDoctrineExtensions Bundle:
# Doctrine Configuration doctrine: dbal: driver: %database_driver% host: %database_host% port: %database_port% dbname: %database_name% user: %database_user% password: %database_password% charset: UTF8 orm: auto_generate_proxy_classes: %kernel.debug% auto_mapping: true # Añadimos el Mapping para StofDoctrineExtensionsBundle: ------------- mappings: StofDoctrineExtensionsBundle: ~ # ... resto del archivo
Luego añadimos al final del mismo archivo la siguiente configuración:
# Añadimos las configuraciones específicas para el StofDoctrineExtensionsBundle stof_doctrine_extensions: default_locale: en_US orm: default: sluggable: true timestampable: true loggable: true #demás behaviors para activar
Donde default: representa la configuración para todos los entornos, eso quiere decir que puedes añadir una configuración específica para cada entorno.
Paso 3: Utilizando los Comportamientos (Behaviors) en los modelos
En este tutorial nos concentraremos en los comportamientos sluggable, timestampable y loggable, para hacer las cosas más fáciles se recomienda añadir el siguiente Namespace a cada una de nuestras entidades en donde queramos añadir los comportamientos:
// añadimos luego del Namespace de la Entidad: use Gedmo\Mapping\Annotation as Gedmo;
Algunos Behaviors como Loggable disponen de Entidades propias que requieren crearse en base de datos, para garantizar ello solo debemos ejecutar en consola el comando siguiente, de este modo Doctrine creará las tablas necesarias para almacenar los datos generados por los comportamientos que lo requieran:
~$ php app/console doctrine:schema:update --force
Sluggable: Permite que Doctrine cree automáticamente el “slug” o la típica cadena optimizada para buscadores utilizada comúnmente al indexar artículos de un blog. Para definir el slug debemos tener un campo destino que es donde se almacenará y uno o más campos origen (los Sluggable) de los cuales se construirá el slug y es tan simple como agregar los siguientes Metadatos a nuestros campos del modelo:
// … dentro de una Entidad // Campo origen: /** * @var string $title * * @ORM\Column(name="title", type="string", length=255) * @Gedmo\Sluggable() */ private $title; // … otras variables // Campo Destino: /** * @var string $slug * * @ORM\Column(name="slug", type="string", length=255) * @Gedmo\Slug(style="camel", separator="_", updatable=false, unique=true) */ private $slug; // … dentro de una Entidad
Note que el campo desde donde se creará el slug ($title) tiene el Metadato @Gedmo\Sluggable(), de hecho puede definir más de uno. En cambio el campo de destino ($slug) tiene el Metadato @Gedmo\Slug(…) y por convención debe ser uno solo, los argumentos style, separator, updatable y unique son opcionales y se detallan en la documentación propia del autor, en este ejemplo se tiene una forma básica de configuración.
Cada vez que se cree un registro de la entidad, Doctrine automáticamente generará el slug y lo aplicará al campo destino, en el caso de modificaciones depende del valor del argumento updatable.
Timestampable: permite que Doctrine gestione la actualización del Timestamp en campos específicos al realizar operaciones de inserción y/o actualización. Para definir un campo con Timestampable solo debemos añadir el Metadato Gedmo\Timestampable(on=”action”), donde action puede ser created o updated respectivamente.
// … dentro de una Entidad // Campo created: /** * @var date $created * * @ORM\Column(name="created", type="date") * @Gedmo\Timestampable(on="create") */ private $created; // Campo updated: /** * @var datetime $updated * * @ORM\Column(name="updated", type="datetime") * @Gedmo\Timestampable(on="update") */ private $updated; // … dentro de una Entidad
Doctrine automáticamente aplicará un nuevo Date al campo definido on=”create” al crear un nuevo registro de la entidad y actualizará el Timestamp del campo definido on=”update” al actualizar el registro de la entidad.
Loggable: permite que Doctrine lleve un control de Versiones sobre los campos indicados, permitiendo consultar las versiones y revertir hacia una versión anterior.
Para crear campos con log (control de versión) solo debemos añadir a cada campo el Metadato @Gedmo\Versioned(), además de añadir el Metadato @Gedmo\Loggable() a la Entidad correspondiente:
// ... encabezados del archivo // definimos el Metadato @Gedmo\Loggable() a la Entidad: /** * MDW\BlogBundle\Entity\Articles * * @ORM\Table() * @ORM\Entity(repositoryClass="MDW\BlogBundle\Entity\ArticlesRepository") * @Gedmo\Loggable() */ class Articles { // … dentro de una Entidad // Campo $content será Versionable: /** * @var text $content * * @ORM\Column(name="content", type="text") * @Gedmo\Versioned() */ private $content; // … demás contenido de la entidad
Doctrine automáticamente supervisará los updates hacia los atributos marcados como Versioned de la entidad y llevará un control de versiones en la Entidad (Stof\DoctrineExtensionsBundle\Entity\LogEntry), y gracias al Repositorio de dicha entidad (\Gedmo\Loggable\Entity\Repository\LogEntryRepository) podremos consultar las Versiones e incluso Revertir los cambios (función $logRepositoryInstance->revert($Entity, $version);), aquí apreciamos un ejemplo de un controlador que lista los cambios:
// Ejemplo dentro de un Controller: public function updateArticleAction($id) { $em = $this->getDoctrine()->getEntityManager(); $article = $em->getRepository('MDWBlogBundle:Articles')->findOneBy(array('id' => $id)); $article->setContent('editado'); $em->persist($article); $em->flush(); $content = ''; // ver cambios $log = $em->getRepository('Stof\DoctrineExtensionsBundle\Entity\LogEntry'); /* @var $log \Gedmo\Loggable\Entity\Repository\LogEntryRepository */ $query_changues = $log->getLogEntriesQuery($article); //use $log->getLogEntries() para un result directo $changues = $query_changues->getResult(); /* @var $version Stof\DoctrineExtensionsBundle\Entity\LogEntry */ foreach ($changues as $version) { $fields = $version->getData(); $content.= ' fecha: ' . $version->getLoggedAt()->format('d/m/Y H:i:s') . ' accion: "' . $version->getAction() . '"'. ' usuario: "' . $version->getUsername() . '"'. ' objeto: "' . $version->getObjectClass() . '"'. ' id: "' . $version->getObjectId() . '"'. ' Version: "' . $version->getVersion() . '"'. ' datos:'; foreach ($fields as $field => $value) { $content.= "-- " . $field . ': '. $value . ''; } } // generamos una salida básica $r = new \Symfony\Component\HttpFoundation\Response(); $r->setContent($content); return $r; }
De esta forma podemos aprovecharnos de algunos de los comportamientos más utilizados de Doctrine, reutilizar código y automatizar tareas en nuestros modelos.
Resumen Final
Como pudimos apreciar con Symfony2 disponemos de una amplia variedad de bundles de terceros a incluir para extender las capacidades de nuestras aplicaciones, aprovechar y reutilizar código mejorando considerablemente el tiempo en el desarrollo de nuestros proyectos; aprendimos que existen diversas formas de incluir nuestros bundles y que en dado caso podemos hacer instalaciones a mano, también de lo importante que es seguir la documentación de cada bundle para agregarlo en el Autoload o Kernel según corresponda y aplicar las configuraciones requeridas por el mismo.
[…] Bundles de terceros VN:F [1.9.17_1161]please wait…Rating: 0.0/5 (0 votes cast)Se ha publicado el capítulo 12 de la guía de Symfony2. En este capítulo @maycolalvarez habla sobre como se instalan los bundles […]
Excelente articulo!, gracias a el al fin encontré como implementar el timestampable como en doctrine1. Excelente curso, sigan asi, para cuando lo tendremos en PDF?
Una vez publicado el último capítulo, faltan aproximadamente 2 semanas, saludos 😉
Consulta, despues de probar este capitulo me encontre con el siguiente error, no se por que sera:
Fatal error: Call to a member function getContent() on a non-object in ..\Symfony\vendor\bundles\Symfony\Bundle\AsseticBundle\Factory\Resource\FileResource.php on line 54
no tengo idea a que se debe,pero me aparece siempre que quiero llamar a cualquier funcion, por ejemplo new, listar de articulos, etc.
Si me dan una mano a que se podría tratar se los agradeceria.
Saludos
Mmm, revisa tu AppKernel.php si no falta el AsseticBundle, también verifica que versión de Doctrine (2.1 o 2.2) se ha venido con la versión de Symfony 2 que has descargado, porque al parecer hay cambios relevantes entre las versiones de Doctrine por lo cual hay que seleccionar la Branch (rama) 2.2.x en el Doctrine Behavioral Extensions (https://github.com/l3pp4rd/DoctrineExtensions/tree/2.2.x), no lo he probado aún, cuando se elaboró el tutorial se hizo sobre Symfony 2.0, en cuando disponga de tiempo hago pruebas con la versión más actual y posteo los cambios, si se requiere, saludos 😉
Muy buen artículo. ¿Hay algún motivo por el cual prefieras usar submodules en lugar del archivo archivo deps y el script en bin/vendors?
Saludos!
Ninguno en especial, usar submodules es como lo describe el bundle y uno de los primeros métodos para hacerlo, y el que usé al momento de crear el tutorial, obviamente existen muchas formas, incluso se pueden descargar a mano, puedes hacerlo como prefieras 😉
Hola
Buen capitulo pero tengo un problema
Estoy siguiendo el capitulo punto por punto pero no se conseguir este error
Paso 1 estoy bajando los contoladores porque el comando con GIT no funciona descomprimo y re nombro las carpetas como del ejemplo modifico archivo autoload.php – tdo bien Appernel- todo bien, y llego hasta config.yml agrego esto
stof_doctrine_extensions:
default_locale: en_US
orm:
default:
sluggable: true
timestampable: true
loggable: true
#demás behaviors para activar
leo todo para slugable, timestampable y logable
llego al capituloo seguridad de acceso creo User.php y Role.php – todo bien
en el momento que meto el comando app/console doctrine:schema:update –force me saca este error
Fatal error: Class Doctrine\Comon\Persistence\Mapping\Driver\DefaultFilelocator not found in C:\Xampp\htdocs\webPage\vendor\gedmo-doctrine-extenions\lib\gedmo\ExtensionMetadateFactory.php on line 170 pero lo estraño es que cuando cambio la configuracion con false
stof_doctrine_extensions:
default_locale: en_US
orm:
default:
sluggable: false
timestampable: false
loggable: false
#demás behaviors para activar
me deja a crear solo crud de Role alguien puede ayudar me ¿?
he conseguido a quitar el error con (https://github.com/l3pp4rd/DoctrineExtensions/tree/2.2.x), esta extension
y me da el error cuando creo el crud de User – error Cass MDW\webBundle\Entity\User is not valid ntity or mapped super class
Muchas gracias por vuestro Curso. Como los aprendices somos muy tontos, muy tontos, por lo menos yo, necesitamos a veces más un Tutorial paso a paso (mejor palmo a palmo), pensado no para expertos PHP, sino para duros de mollera!
Puntualmente, he consguido instalar perfectamente este Bundle en mi Symfony 2.0.16… (en mi Ubuntu, imposible en un XP)… pero acabo de descubrir la nueva fase de Symfony 2.1.0, utilizando Composer, y ya me ha sido imposible instalar el Gedmo-doctrine-extensions, aún siguiendo los pasos de https://github.com/l3pp4rd/DoctrineExtensions/blob/master/doc/symfony2.md .
Sería muy de agradecer un artículo de como actualizarse o conseguir la nueva instalación en la nueva Symfony 2.1.
Muchas gracias de nuevo!
Hola a todos.
Lo primero muchísimas gracias por este tutorial tan completo.
Tengo una duda… Ya tengo la aplicación en mi localhost funcionando a la perfección en Symfony 2.1… ¿Cómo hago para subirlo y que funcione en un servidor compartido? Alguien sabe de algún tutorial sencillo (o me puede explicar en este hilo) cómo hacerlo para tontitos como yo?
Un millón de gracias y enhorabuena por este tutorial en serio.