symfony2

Howto use Lazy Loading in Symfony2 (and Drupal 7 / 8)

Lazy Loading is a software design pattern which basically allows our classes and objects to be a lot lighter. Faster code, faster applications, less memory used in runtime. The great Martin Fowler says about Lazy Loading:

An object that doesn't contain all of the data you need but knows how to get it.

http://martinfowler.com/eaaCatalog/lazyLoad.html

The key is that your "real" objects are not going to be instantiated unless they are going to be used. How that can be achieved? Easy, with what it is called a Virtual Proxy. As in every project, you can always code your own implementation, but the key about design patterns and software engineering is to reuse as much as possible. And regarding Lazy Loading, Symfony 2 has a nice implementation called Proxy Manager Bridge, which does exactly that in Symfony 2.

Recently we discovered that our implementation of Lazy Loading was not working properly in Drupal 7. We were using a custom module which we created in Capgemini, Drupal Symfony Inject, to inject our own implementations of Symfony 2 classes in Drupal. The module works great, but for some reason the Lazy Loading was not working, the objects were always having the real implementation of the object itself instead of the proxy (for more info, check the documentation in Symfony 2 about Lazy Services).

The solution is relatively easy (not as much as finding it), and I commited a patch for that, so Drupal Symfony Inject should not have that problem anymore: https://drupal.org/node/2287081.

A different question is, how to use Lazy Loading in our projects when we are not using Symfony 2 framework but its components. One of the errors which we were doing in the Drupal Symfony Inject module was that we were not loading the Proxy Manager Bridge, which makes the integration of Proxy Manager, your code and some other Symfony 2 components.

At that point we were quite lost, not many people has done before that integration between Drupal and Symfony 2 Lazy Loading, so we had very few people to ask, even inside Capgemini itself, and even fewer documentation to consult. Fortunatelly there are some interesting pieces of code in every well designed software. I am talking about test units: https://github.com/symfony/ProxyManagerBridge/blob/master/Tests/LazyProx...

The most interesting part in lines 34 and 37:

$builder->setProxyInstantiator(new RuntimeInstantiator());

That part is essential, it will instantiate the Proxy Manager and substitute your Object with an instance of that Proxy.

We just need to do one last step, indicate that our object will use Lazy Loading:

$builder->getDefinition('your_class')->setLazy(true);

From this point, the next time you load your object:

$builder->get('your_class')

if you ask for the class of this object it will identify itself as a Lazy Proxy:

\ProxyManager\Proxy\LazyLoadingInterface

 That state will remain until you ask the object to return something, basically when you use one of its methods:

$your_object->doSomething();

Now that object will say that he is the real class, something like that:

\ProxyManagerBridgeFooClass

 

More information:

Composer and Symfony2 in Drupal 7

categorias: 

Using Symfony Dependency Injection in Drupal 7: Software Engineering in Drupal

I still remember in a previous job discussing with a partner about doing things the proper way, using coding standards, commenting what you are doing, ... giving your code some love in definitive.

I'm still amazed of his answer, basically he was refusing to doing things the right way (the only way) because using his way he was a lot quicker... Well, probably, but on the meantime modifying some of the crap we had there (not just him, I have to say) could take us like 4 to 10 times more time.

That's basically because you needed to understand what was going on that code... and I promise you, when you have a single function of around 3000 lines, no comments, things happening without too much logic, no standards, dozens of lines commented... that is not easy at all, even for the owners of that code. That's why you use Coding Standards, code reviews, Pull Requests and all that stuff in serious environments of Software Engineering.

Anyway, developing using the Object Oriented paradigm is quite a lot more organized than doing it using simply .modules and .inc files in Drupal. It does not matter how organized you are, at the end you'll end with lots of files and functions in a nightmare to understand for third developers. And, when you are working in Enterprise environments, with lots of people sharing and creating code, things like coding standards and doing things the proper way becomes a very serious thing.

Solution? As I said, using Object Oriented programming paradigm. In Drupal is easy enough since Drupal 7 allows you to add directly classes included in .php, .inc/* files. Simply do:

files[] = lib/MyClass.php

Well, that's easy enough... but there is a better more sophisticated way of doint it: Symfony Dependency Injection.

There are some good advantages of doing this that way. First and maybe most important, you are starting to be ready for Drupal 8. Second one, you objects can use Lazy loading. That's a great thing, basically means that your objects will be using the Proxy Pattern, which allows your code to load things only when they are going to be needed.

That's a good trick to improve your Drupal site performance from your own code (a big big percentage of performance problems are derivated from bad coding practices).

Want more advantages? Did I say that you make your code ready to Drupal 8? Well, that's just the begining. Starting to use Symfony components like Drupal Symfony inject open us a world of thousands of bundles ready to use from Symfony, and just using them through Composer. Win win :-).

We will need basically two modules, Drupal Symfony inject which a college is developing here, in Capgemini, and Composer Manager as it is a dependancy:

It is important you install them using drush, so the vendors folder will be created for you (with all the dependencies that the module has):

drush pm-enable drupal_symfony_inject --y

Once installed, DSI makes you available three new hooks:

  • _namespace_register()
  • _symfony_yaml_config()
  • _symfony_yaml_config_params()

The first one, _namespace_register, allows you to register your new classes like that:

Now we will need to add the config.yaml, where we will declare the classes we will be using, parameters, etc...

Now you need to define your classes in the yaml file. Create the file in config/yaml/config.yaml and declare there the classes you'll be using:

And finally, the parameters:

That's it, Drupal is ready to use the Symfony Dependency Injection component. Lets move to create a new class, for example, CruiseHunter:

That's one of the Abstract classes I use in CruiseHunter from which I inherit to fetch the different contents the Cruise crawler uses. It is a lot more organized, it declares a common mechanism to fetch information from the crawler, etc...

Well, now that we have the class, we need to be able to import it and use it. And here is where the Dependency Injection appears:

Once you have all in place, you have to rebuild the symfony components you have just built:

drush rsc

If you see some error, you made a mistake, probably in your yaml. If you see something like this:

Symfony DI container compiled and cached successfully !!!

Congratulations, you are ready to use your new Symfony components :-).

Related articles:

Solving error 500 in Symfony2

Having the typical error 500 in Symfony2? Well, it is very tipical, not just in Symfony but in Drupal and other web frameworks and CMS. Solving it can be a big headache... if you don't know where to see.

In Drupal it is easy to solve it using show all errors in php (on index.php of Drupal, for example). In Symfony2 you can use this tip:

Go to the root of your app. Then execute a clean cache:

{syntaxhighlighter brush: as3;fontsize: 100; first-line: 1; }php app/console cache:clear{/syntaxhighlighter}What we achieve with that is being able to see error messages on console, after you execute this command. Messages that you will not allways see en error.logs of apache or ngnix.

categorias: 

Symfony2: Not a git repository

Ok, you want to install a bundly in Symfony2, and you are going to use git. You go to your root directory... but, here is the problem

Command:

{syntaxhighlighter brush: as3;fontsize: 100; first-line: 1; }git submodule add git://github.com/simplepie/simplepie.git vendor/simplepie{/syntaxhighlighter}

Yes, it's Simplepie bundles for Symfony2. Ok, the error:

 

{syntaxhighlighter brush: as3;fontsize: 100; first-line: 1; }fatal: Not a git repository (or any of the parent directories): .git{/syntaxhighlighter}

It's quite simple. You don't have initialized git:

{syntaxhighlighter brush: as3;fontsize: 100; first-line: 1; }$git init
> Initialized empty Git repository in /var/www/symlabs/[project]/.git/
{/syntaxhighlighter}

Try again and... Yes, you welcome :-)

 

categorias: 

Crear una nueva tabla en una base de datos con Symfony2

 

Añadimos una nueva entidad en CruiseHunter/CrucerosBundle/Entity/Naviera.php

actualizamos los datos:

- php app/console doctrine:generate:entities CruiseHunter/CrucerosBundle/Entity/Naviera

 

- php app/console doctrine:generate:entities CruiseHunter/CrucerosBundle/Entity/Naviera

 

Cuidado. si creamos el fichero en minúsculas, al hacer el doctrine:generate nos va a generar uno en mayúsculas, y nos dará el dichoso error de que no se puede crear dos veces una clase con el mismo nombre.

Más datos importantes, la cabecera con ORM es importante para generar la tabla automáticamente en nuestra base de datos:

 

/**

 * CruiseHunter\CrucerosBundle\Entity\Naviera

 * @ORM\Table(name="naviera")

 * @ORM\Entity

 */

class Naviera

{

 

 

y, por último, debemos tener una variable key, tipo id:

 

    /**

     * @ORM\Id

     * @ORM\Column(type="integer")

     * @ORM\GeneratedValue(strategy="AUTO")

     */

    private $id;

 

 

Hasta aquí todo. Sólo nos queda generar la base de datos:

 

- php app/console doctrine:schema:update --force

Esto último lo que hace es revisar nuestro Entity, y comprobar que campos llevan un @ORM con un \Column asociado. Justamente este campo es el que el comando update usará para crear el campo en la base de datos. 

Por ejemplo:

 

    /**

     * @var string $descripcion

     * @ORM\Column(type="string",length=200)

     * 

     */

    private $descripcion;

    /**

     * @var string $barco

     * @ORM\Column(type="string",length=100)

     */

    private $barco;

    /**

     * @var string $enlace

     * @ORM\Column(type="string",length=200)

     */

    private $enlace;

 

Symfony2: warning open_basedir restriction in effect

Error al subir una aplicación al sevidor en el que va a estar en producción:

symfony ErrorException: Warning: file_exists(): open_basedir restriction in effect

Después de estrujarme mucho los sesos, hize un

rm -rf app/cache/* 

y "evoilá" :-).

Después pueden salir algunos errores como:

UnexpectedValueException: The stream or file "/var/www/vhosts/*/app/logs/prod.log" could not be opened; it may be invalid or not writable.

lo que simplemente se arregla corrigiendo con los permisos adecuados los directorios que nos indique el propio mensaje, en este caso el directorio de logs:

chmod a+rw -R logs/

 

eliminar una base de datos en Symfony2 / Doctrine2

eliminamos el php que contiene la definición de la tabla actual, por ejemplo:

rm src/CruiseHunter/CrucerosBundle/Entity/Crucero.php

Ejecutamos el comando:

sudo php app/console doctrine:schema:drop --force

Mas información:

http://www.doctrine-project.org/docs/orm/2.0/en/reference/tools.html sobre todo la sección 23.1.4. Command Overview

 

Y ya estamos en condiciones de volver a ejecutar:

sudo php app/console doctrine:schema:update --force

sin que nos de un error porque exista ya la clase, lo que suele indicar que hay un .php en Entities que no debería...

Bases de datos en Symfony2: Doctrine2

Me encanta aprender lenguajes nuevos y tecnologías nuevas a pesar de lo desesperante de algunos momentos en los que te encuentras atascado. Es el caso.

Trabajar con Bases de datos en Symfony2 es sencillo, casi divertido. Si ya tenemos creado el Bundle es tan fácil como lo siguiente (si no lo tenemos creado os recomiendo leer el primer post de este tutorial sobre symfony2, Hola Mundo en Symfony2, una pista: php app/console generate:bundle --namespace=Acme/StoreBundle)

Lo siguiente es asegurarnos de que los datos de configuración de nuestra base de datos son correctos (tipo de base de datos, password, puerto, ...). Si hemos seguido los pasos de instalación correctamente, lo más normal es que ya esté configurada, y el fichero app/config/parameters.ini contiene esos datos.

(i) Normalmente lo primero es crear una base de datos sobre la que podamos trabajar. En Symfony2 no es necesario, el propio framework puede hacerlo por nosotros:

php app/console doctrine:database:create

Ojo con los permisos, en ubuntu probablemente deberemos usar el comando "sudo" antes del comando anterior para poder ejecutarlo con permisos de superusuario.

(ii) Lo siguiente es crear una estructura para nuestra base de datos. Antes de ponernos a picar código es importante diseñar bien la aplicación, independientemente del lenguaje, framework o "bicho" que estemos usando para plasmar nuestros sueños sobre la pantalla.

En Symfony vamos a pensar en Entidades. Una entidad es básicamente una representación de un objeto de nuestra base de datos. Por ejemplo, si tenemos una tienda o un ecommerce, una entidad sería un producto, o un crucero por ejemplo si nos dedicamos al turismo. Así, la entidad sería Producto.php y Crucero.php respectivamente.

Una vez tengamos clara la estructura vamos a crear un directorio nuevo dentro del directorio de nuestra app src/NombredenuestraApp/NombredelBundle/Entity/Objeto.php

Por ejemplo: src/CruiseHunter/CrucerosBundle/Entity/Cruceros.php

El fichero seguirá este formato:

----------------------------------------------

 

<?php

// src/Acme/StoreBundle/Entity/Product.php

namespace CruiseHunter\CrucerosBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**

 * @ORM\Table(name="cruceros")

 * @ORM\Entity

 */

class Crucero

{

    /**

     * @ORM\Id

     * @ORM\Column(type="integer")

     * @ORM\GeneratedValue(strategy="AUTO")

     */

    protected $id;

    /**

     * @ORM\Column(type="string", length=100)

     */

    protected $nombre;

    /**

     * @ORM\Column(type="integer")

     */

    protected $precio;

    /**

     * @ORM\Column(type="string", length=100)

     */

    protected $descripcion;

}

----------------------------------------------

 

Una vez en este punto, quedaría plasmar esta estructura en nuestra base de datos. Pero, un momento, hasta ahora no hemos tenido que tirar del comando mysql ni tan siquiera abrir phpmyadmin... no será esta una excepción:

sudo php app/console doctrine:generate:entities CruiseHunter/CrucerosBundle/Entity/Crucero

Con esto acabamos de crear las Entidades, que se usarán, entre otras cosas, para crear las tablas que vamos a necesitar en la base de datos.

Cuidado con repetir este último comando porque nos dará un error de redeclaración de la clase Crucero (o la que sea en tu caso) y tendremos un problema ( soy un poco manazas, lo se :-( ).

(iii) Bueno, allá vamos:

sudo php app/console doctrine:schema:update --force

y tendremos una respuesta parecida a esto:

 

Updating database schema...

Database schema updated successfully! "1" queries were executed

 

Si todo ha ido bien, ahora tenemos unas bonitas tablas nuevas en nuestra base de datos reflejando esta estructura. Fácil, verdad? :-)

 

Veamos, error clásico, olvidar el <?php del inicio del documento. Resultado? Este:

 

  [RuntimeException]                                                                            

  Namespace "CruiseHunter\CrucerosBundle\Entity\Crucero" does not contain any mapped entities.  

 

Si nos encontramos con este dichoso error no os paséis horas y horas como yo buscando la solución. Lo más obvio, lo que nadie te dice al principio, olvida los config, routing, kernel, etc, etc... Fíjate si has olvidado el puñe***o <?php de declaración de inicio de un fichero php. Ale, de nada :-).

En el siguiente capítulo veremos como acceder a los datos almacenados en la base de datos.

Tutorial de Symfony2: Hola mundo en Symfony

Una vez tenemos instalado Symfony2 en nuestro servidor (algo sencillo como vimos en el capítulo anterior) vamos a comenzar a trabajar.

(i) En primer lugar tenemos que crear un bundle, que básicamente es la semilla de nuestra aplicación, el germen de lo que vamos a construir.

php app/console generate:bundle --namespace=Acme/HelloBundle --format=yml

Básicamente le estamos indicando a Symfony2, crea un Bundle, cuyo formato es yml y al que le vamos a dar el nombre de Hello (HelloBundle en terminología Symfony2) dentro del entorno Acme.

(ii) Vamos a crear el clásico "Hola Mundo" que todo hijo de vecino crea cuando está aprendiendo un nuevo lenguaje de programación. Para ello iremos al fichero /app/config/routing.yml dentro de nuestra aplicación:

 

  • hola:  
  • pattern:  /hola/{name}
  • defaults: { _controller: AcmeHelloBundle:Hello:index }

 

 

Aquí hay que llevar especial cuidado en no meter tabuladores en lugar de espacios, ya que Symfony lo detectará como un error y podemos volvernos locos buscando qué es lo que hemos hecho mal.

Un pequeño truco es usar un default, un valor por defecto para {name}. De esta forma estamos creando también una página para el "root" o directorio raíz:

  • hola:  
  • pattern:  /hola/
  • defaults: { _controller: AcmeHelloBundle:Hello:index , name:Mundo}

 

 

El default es "name:Mundo", separado por una coma.

Si intentamos ahora mismo acceder a nuestro servidor recibiremos un error muy específico y claro: nos falta un controlador.

localhost/DIRECTORIO-SYMFONY2/web/app_dev.php/hola/mundo

 

(iii) Nada más sencillo de solucionar. Creamos un fichero que se llamará, teniendo en cuenta cómo hemos llamado el Bundle, de la siguiente forma:

// src/Acme/HelloBundle/Controller/HelloController.php

namespace Acme\HelloBundle\Controller;

use Symfony\Component\HttpFoundation\Response;

class HelloController{

    public function indexAction($name)    {

       return new Response('<html><body>Hello '.$name.'!</body></html>');  

 }

}

 

Dentro de HelloController.php hay dos declaraciones fundamentales, la clase HelloController, en la linea 4: class HelloController{

y la función indexAction, una línea más abajo. A esta función hemos hecho referencia desde el routing.yml, justamente en la linea que habla del controller:


  • defaults: { _controller: AcmeHelloBundle:Hello:index , name:Mundo}

En realidad le estamos dando la ruta al Bundle, AcmeHello, y al Action que vamos a ejecutar: index. Ambos, Bundle y Action llevarán el postfijo después de su nombre, es decir, AcmeHelloBundle e indexAction. Si por ejemplo el action se llamara NuevoCliente, la función en el controller se llamaría NuevoClienteAction, y haríamos referencia a ella desde el fichero yml (pronunciado "YAMEL") de esta forma:


 

  • defaults: { _controller: AcmeHelloBundle:Hello:NuevoCliente}