Noticias

Creando un proyecto base con Symfony y Sonata Project

Como vimos en una introducción anterior a Sonata, el proyecto cuenta con bundles muy interesantes que nos pueden facilitar nuestro trabajo resolviendo problemas habituales y evitándonos horas y horas de trabajo.

En el el post de hoy vamos a crear un proyecto Symfony2 y utilizaremos varios bundles de Sonata para configurar un diseño que nos sirva como código base, desde el que partir más tarde para realizar otros proyectos.

En posteriores entradas del blog, inventaremos un pequeño proyecto con el que iremos trabajando sobre nuestras propias entidades, combinándolas con este código base.


Sea cual sea el proyecto que acometamos, es muy posible que tengamos que gestionar entidades, usuarios, imágenes, videos, tengamos que clasificar contenidos, etc, y para eso vamos a utilizar algunos de los bundles de Sonata que nos proporcionan estas funcionalidades.

En concreto vamos a utilizar:

  • SonataAdminBundle: nor permite generar de forma rápida un backend para gestionar nuestras entidades

  • SonataUserBundle: integra el fantástico bundle FosUserBundle y nos proporciona características adicionales como roles y gestión de grupos.

  • SonataMediaBundle: nos ayudará en la gestión de toda la parte multimedia.

  • SonataClassificationBundle: lo utilizaremos para poder clasificar y etiquetar nuestras entidades.

 

Intención

Aunque puedes seguir la instalación de los diferentes bundles siguiendo la documentación de Sonata, he preferido documentarla en este post, por varias razones:

  • dejo así recogido en este documento la versión exacta con los bundles específicos que utilizaremos en un proyecto posterior.

  • evitar al lector tener que ir navegando por distintas páginas de los bundles relacionados, para poner el marcha el proyecto.

  • si vemos la instalación de los bundles por separado hay mucha información redundante, que en el artículo suprimo.

 

En cualquier caso, si hoy no te sientes especialmente trabajador :-) puedes bajar el proyecto ya configurado desde mi cuenta en github y simplemente seguir las instrucciones de instalación.

Actualmente existen dos ramas del proyecto AdminBundle, la más reciente etiquetada como estable es la 2.3 y es la que vamos a utilizar. La instalación la vamos a realizar en mi equipo actual con una distribución Linux Ubuntu 14.04 LTS pero debería funcionar en cualquier sistema capaz de poner en marcha un sistema LAMP.

Las versiones exactas de php y mysql que voy a utilizar son:

$ php --version
PHP 5.5.11-3+deb.sury.org~precise+1 (cli) (built: Apr 23 2014 12:23:08) 
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
    with Xdebug v2.2.5, Copyright (c) 2002-2014, by Derick Rethans
    with Zend OPcache v7.0.4-dev, Copyright (c) 1999-2014, by Zend Technologies


$ mysql --version
mysql  Ver 14.14 Distrib 5.5.37, for debian-linux-gnu (x86_64) using readline 6.2

 

¿Empezamos?

Instalaremos Sonata en una versión limpia de Symfony2, en concreto sobre la versión 2.3 LTS,  para obtenerla ejecutamos:

composer create-project symfony/framework-standard-edition symfony2-sonata/ "2.3.*"


Para instrucciones más detalladas sobre una instalación de Symfony2 puedes consultar un post anterior

El instalador realizará preguntas sobre la configuración de la base de datos, introduce los válidos para tu sistema.

En el fichero composer.json agregamos:

// composer.json
// …

"sonata-project/doctrine-orm-admin-bundle": "2.3.*",
"sonata-project/admin-bundle": "2.3.*",
"sonata-project/easy-extends-bundle": "~2.1",
"sonata-project/user-bundle": "~2.2",
"sonata-project/intl-bundle": "~2.2",
"sonata-project/media-bundle": "~2.2",
"sonata-project/classification-bundle": "~2.2",
"jms/serializer-bundle": "0.13.*"

// ...

Ahora ejecutamos

cd symfony2-sonata
composer update

y composer nos instalará el bundle y sus dependencias. Si no tienes instalado composer en su página oficial tienes información para hacerlo.

Una vez concluida la instalación hay que activar los bundles y establecer su configuración.

// app/AppKernel.php
<?php

public function registerBundles()
{
    return array(
        // ...

        // Add your dependencies
        new Sonata\CoreBundle\SonataCoreBundle(),
        new Sonata\BlockBundle\SonataBlockBundle(),
        new Knp\Bundle\MenuBundle\KnpMenuBundle(),
        new FOS\UserBundle\FOSUserBundle(),
        new JMS\SerializerBundle\JMSSerializerBundle(),


        //...

        // If you haven't already, add the storage bundle
        // This example uses SonataDoctrineORMAdmin but
        // it works the same with the alternatives
        new Sonata\DoctrineORMAdminBundle\SonataDoctrineORMAdminBundle(),

        // Then add SonataAdminBundle
        new Sonata\AdminBundle\SonataAdminBundle(),
        new Sonata\EasyExtendsBundle\SonataEasyExtendsBundle(),
        new Sonata\UserBundle\SonataUserBundle('FOSUserBundle'),
        new Sonata\IntlBundle\SonataIntlBundle(),
        new Sonata\MediaBundle\SonataMediaBundle(),
        new Sonata\ClassificationBundle\SonataClassificationBundle(),


        // ...
    );
}

Ahora a por la configuración

Dentro de la carpeta app/config creamos un directorio llamado sonata y dentro de este creamos tres ficheros que contendrán la configuración de cada bundle, serán: sonata_block.yml, sonata_media.yml y sonata_classification.yml:

// app/config/sonata/sonata_admin.yml
sonata_admin:
    title: Backend project
// app/config/sonata/sonata_block.yml
sonata_block:
    default_contexts: [cms]
    blocks:
        # Enable the SonataAdminBundle block
        sonata.admin.block.admin_list:
            contexts:   [admin]
// app/config/sonata/sonata_media.yml
sonata_media:
    default_context: default
    db_driver: doctrine_orm # or doctrine_mongodb, doctrine_phpcr
    contexts:
        default:  # the default context is mandatory
            providers:
                - sonata.media.provider.dailymotion
                - sonata.media.provider.youtube
                - sonata.media.provider.image
                - sonata.media.provider.file

            formats:
                small: { width: 100 , quality: 70}
                big:   { width: 500 , quality: 70}

    cdn:
        server:
            path: /uploads/media # http://media.sonata-project.org/

    filesystem:
        local:
            directory:  %kernel.root_dir%/../web/uploads/media
            create:     true

    providers:
        image:
            resizer: sonata.media.resizer.square

 

// app/config/sonata/sonata_classification.yml
sonata_classification:

 

luego en app/config.yml importamos el contenido en app/config/config.yml

imports:
    // ...
    # Sonata conf
    - { resource: sonata/sonata_admin.yml }
    - { resource: sonata/sonata_block.yml }
    - { resource: sonata/sonata_media.yml }
    - { resource: sonata/sonata_classification.yml }
    // ...

En el mismo config.yml hay que agregar el tipo Json en su sección correspondiente e informar a doctrine para que maneje las entidades, le activaremos el auto_mapping para que trate las nuevas entidades de forma automática

doctrine:

// ...
doctrine:

    dbal:
        // ...
        types:
            json: Sonata\Doctrine\Types\JsonType

    orm:
        auto_generate_proxy_classes: "%kernel.debug%"
        default_entity_manager: default
        entity_managers:
            default:
                auto_mapping: true

// ...

 

Siguiendo en el mismo fichero hay agregar la configuración del bundle de gestión de usuarios, FosUserBundle:

# app/config/config.yml

fos_user:
    db_driver:      orm # can be orm or odm
    firewall_name:  main
    user_class:     Sonata\UserBundle\Entity\BaseUser

    group:
        group_class:   Sonata\UserBundle\Entity\BaseGroup
        group_manager: sonata.user.orm.group_manager                    # If you're using doctrine orm (use sonata.user.mongodb.user_manager for mongodb)

    service:
        user_manager: sonata.user.orm.user_manager                      # If you're using doctrine orm (use sonata.user.mongodb.group_manager for mongodb)


 

Cargar rutas.

Ahora toca exponer las rutas de cada bundle, para ello debemos agregarlas al fichero de configuración correspondiente:

# app/config/routing.yml

// ...
admin:
    resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml'
    prefix: /admin

_sonata_admin:
    resource: .
    type: sonata_admin
    prefix: /admin

sonata_user_security:
    resource: "@SonataUserBundle/Resources/config/routing/sonata_security_1.xml"

sonata_user_resetting:
    resource: "@SonataUserBundle/Resources/config/routing/sonata_resetting_1.xml"
    prefix: /resetting

sonata_user_profile:
    resource: "@SonataUserBundle/Resources/config/routing/sonata_profile_1.xml"
    prefix: /profile

sonata_user_register:
    resource: "@SonataUserBundle/Resources/config/routing/sonata_registration_1.xml"
    prefix: /register

sonata_user_change_password:
    resource: "@SonataUserBundle/Resources/config/routing/sonata_change_password_1.xml"
    prefix: /profile

sonata_user:
    resource: '@SonataUserBundle/Resources/config/routing/admin_security.xml'
    prefix: /admin

gallery:
    resource: '@SonataMediaBundle/Resources/config/routing/gallery.xml'
    prefix: /media/gallery

media:
    resource: '@SonataMediaBundle/Resources/config/routing/media.xml'
    prefix: /media

 

SonataAdminBundle viene con una gestión de roles propia, además hay que informar a Symfony de las nuevas reglas de control de acceso para proteger la gestión del backend, para ello hay que modificar el fichero security.yml. Así es como ha quedado mi fichero.

// app/config/security.yml
security:

    encoders:
        FOS\UserBundle\Model\UserInterface: sha512
  
    access_control:
        # URL of FOSUserBundle which need to be available to anonymous users
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }

        # Admin login page needs to be access without credential
        - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY }

        # Secured part of the site
        # This config requires being logged for the whole site and having the admin role for the admin part.
        # Change these rules to adapt them to your needs
        - { path: ^/admin/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
        - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }

    role_hierarchy:
        ROLE_ADMIN:   	[ROLE_USER, ROLE_SONATA_ADMIN]
        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
        SONATA:
            - ROLE_SONATA_PAGE_ADMIN_PAGE_EDIT  # if you are using acl then this line must be commented

    providers:
        fos_userbundle:
            id: fos_user.user_manager

    firewalls:
        # Disabling the security for the web debug toolbar, the profiler and Assetic.
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        # -> custom firewall for the admin area of the URL
        admin:
            pattern:        	/admin(.*)
            context:        	user
            form_login:
                provider:   	fos_userbundle
                login_path: 	/admin/login
                use_forward:	false
                check_path: 	/admin/login_check
                failure_path:   null
            logout:
                path:       	/admin/logout
            anonymous:      	true
        # -> end custom configuration

        # default login area for standard users

        # This firewall is used to handle the public login area
        # This part is handled by the FOS User Bundle
        main:
            pattern:         	.*
            context:         	user
            form_login:
                provider:   	fos_userbundle
                login_path: 	/login
                use_forward:	false
                check_path: 	/login_check
                failure_path:   null
            logout:         	true
            anonymous:      	true

 

Extendiendo el bundle

Para poder modificar de forma adecuada el comportamiento de los bundles podemos utilizar los mecanismos que proporciona Symfony2 para poder sobreescribir controladores y configuración, puedes ver información aquí y aquí. Felizmente Sonata nos proporciona un comando que nos facilita este trabajo.

php app/console sonata:easy-extends:generate SonataUserBundle -d src
php app/console sonata:easy-extends:generate SonataMediaBundle -d src
php app/console sonata:easy-extends:generate SonataClassificationBundle -d src

Este comando extiende cada bundle de Sonata moviendo la configuración de las mismos en un espacio de nombres accesible bajo la carpeta src\Application, facilitando agregar nuevos campos en las entidades o modificar configuraciones.

En la configuración inicial informamos a FosUser que utilizara las clases de usuario y grupo bases de Sonata, ahora hay que actualizar estos datos para que utilice las clases recién extendidas.

Abrimos app/config/config.yml y modificamos las clases para user_clas y group_class:

// app/config/config.yml
fos_user:
    user_class:     Application\Sonata\UserBundle\Entity\User
// ...

    group:
        group_class:   Application\Sonata\UserBundle\Entity\Group

// ...

Todavía queda activar estos nuevos bundles en el kernel:

 

<?php

// AppKernel.php

class AppKernel {
    public function registerbundles()
    {
        return array(
            
            // ...

            // SonataApplication Bundles
            new Application\Sonata\UserBundle\ApplicationSonataUserBundle(),
            new Application\Sonata\MediaBundle\ApplicationSonataMediaBundle(),
            new Application\Sonata\ClassificationBundle\ApplicationSonataClassificationBundle(),

            // ...

        )
    }
}

Como todo ha ido bien, ahora sólo queda crear y actualizar la base de datos:

Instalamos los recursos con:

app/console assets:install web --symlink
app/console doctrine:database:create
app/console doctrine:schema:update --dump-sql --force


Control de acceso
En pantallas anteriores modificamos el fichero security.yml para proteger el acceso al backend, pues bien ahora es necesario agregar un usuario administrador al sistema para poder autenticarnos en el mismo:

app/console fos:user:create admin --super-admin

 

Configurar host

Hasta aquí la configuración básica. En estos momentos sólo quedaré configurar un virtual host en apache para poder acceder y ver el proyecto en marcha.

sudo vim /etc/apache2/sites-available/030-symfony-sonata.conf
<VirtualHost *:80>
  ServerName local.sonatablog.es
  ServerAdmin adminl@mysite.com
  DocumentRoot "/var/www/html/test/symfony2-sonata/web"
  <Directory "/var/www/html/test/symfony2-sonata/web">
    Options Indexes FollowSymLinks MultiViews
    AllowOverride None
    allow from all
    <IfModule mod_rewrite.c>
        RewriteEngine On
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteRule ^(.*)$ app_dev.php [QSA,L]
    </IfModule>
   </Directory>
</VirtualHost>

 

Activar el virtual host:

sudo a2ensite 030-symfony-sonata.conf

Y editar nuestro host para que resuelva:

sudo vim /etc/hosts
127.0.0.1       local.sonatablog.es

 

Finalizando

Si ahora accedemos a http://local.sonatablog.es/app_dev.php/admin debemos ver el dashboard de Sonata. Es hora de tomar un descanso, enviar un whatsapp a nuestros amigos y salir juntos a tomarnos un café con leche con pincho de tortilla por el trabajo bien hecho.

 

* Es posible que veas que las traducciones en el backend no están actuando, si es tu caso fíjate que tengas activado el servicio translator:
 

# app/config/config.yml
framework:
    translator:      { fallback: "%locale%" }

 

Creo que ha sido bastante trabajo por hoy. Si deseas estar al tanto de los próximos post sígueme en twitter recuerda también que puedes bajar el proyecto desde github.

Si eres impaciente y quieres seguri avanzando, lo siguiente sería trabajar con entidades propias y configurarlas para que se muestren en el backend, para ello puedes estudiar la documentación de SonataAdminBundle, concretamente el apartado "2.2 STEP 2: CREATE AN ADMIN CLASS".

Hasta la próxima.

 

 


Symfony2 php Sonata Project CMS


Compartir mola!!