Doctrine Symfony2 – EntityManager Closed – Always enclose flush inside a try catch block and log exceptions

Hi,

This is not a tutorial but a good practice advice, about flush of objects in Doctrine with Symfony2.
A few days ago, we have encountered a strange error: “Entity Manager Closed”.

After several minutes of research, we found the problem.

We were trying to save a NULL value inside a NOT NULL field in our database, making the flush of objects failing.
When flush fails, it creates an exception like this:

 Doctrine\ORM\Exception "The EntityManager is closed".

When this error occurs, then entityManager is voluntarily closed, and following “flush” will not work.

The good practice is to systematically enclose all flush, inside a try catch block, and log the exception message and trace.
Let’s see an example.

    try {
        $em->flush();
    } catch (\Exception $e) {
        $msg = '### Message ### \n'.$e->getMessage().'\n### Trace ### \n'.$e->getTraceAsString();
        $this->container->get('logger')->critical($msg);
        // Here put you logic now you now that the flush has failed and all subsequent flush will fail as well
    }

This is a good practice that you’ll have to use in your further codes.

Doctrine Symfony2 – EntityManager Closed – Always enclose flush inside a try catch block and log exceptions

Symfony2: two fields comparison with custom validation constraints

I’ve already written a post on how to create a custom validation constraint here (https://creativcoders.wordpress.com/2014/07/15/symfony2-tutorial-create-custom-constraint-validation-and-use-it-with-fosuserbundle/), but what if you need to validate two fields or more in your entity?

Well, I have an entity called “Card” that stores credit cards informations and i need to check the card validity by checking the expiration month and expiration year.
The proper way to do this is to create a custom validation constraint that we will append in our “card” entity.
This is how the entity looks:

<?php

namespace EdouardKombo\StripePaymentBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Card
 *
 * @ORM\Table(name="headoo_stripe_credit_card")
 * @ORM\Entity
 */
class Card
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var integer
     *
     * @ORM\Column(name="number", type="integer")
     * @Assert\NotBlank(message="card.number.blank")
     * @Assert\Length(min="16", max="16", minMessage="card.number.length", maxMessage="card.number.length", exactMessage="card.number.length")
     * @Assert\Range(min="1000000000000000", max="9999999999999999", minMessage="card.number.range", maxMessage="card.number.range")
     * @Assert\Regex(pattern="^(([0-9]*)|(([0-9]*).([0-9]*)))$^", match=true, message="card.number.invalid")
     */
    private $number;

    /**
     * @var integer
     *
     * @ORM\Column(name="expirationMonth", type="integer")
     * @Assert\NotBlank(message="card.expiration.month.blank")
     * @Assert\Length(min="2", max="2", minMessage="card.expiration.month.length", maxMessage="card.expiration.month.length", exactMessage="card.expiration.month.length")
     * @Assert\Range(min="1", max="12", minMessage="card.expiration.month.range", maxMessage="card.expiration.month.range")
     * @Assert\Regex(pattern="^(([0-9]*)|(([0-9]*).([0-9]*)))$^", match=true, message="card.expiration.month.invalid")
     */
    private $expirationMonth;

    /**
     * @var integer
     *
     * @ORM\Column(name="expirationYear", type="integer")
     * @Assert\NotBlank(message="card.expiration.year.blank")
     * @Assert\Length(min="2", max="2", minMessage="card.expiration.year.length", maxMessage="card.expiration.year.length", exactMessage="card.expiration.year.length")
     * @Assert\Range(min="0", max="99", minMessage="card.expiration.year.range", maxMessage="card.expiration.month.range")
     * @Assert\Regex(pattern="^(([0-9]*)|(([0-9]*).([0-9]*)))$^", match=true, message="card.expiration.year.invalid")
     */
    private $expirationYear;

    /**
     * @var integer
     *
     * @ORM\Column(name="cvc", type="integer")
     * @Assert\NotBlank(message="card.cvc.blank")
     * @Assert\Length(min="3", max="4", minMessage="card.cvc.short", maxMessage="card.cvc.long", exactMessage="card.cvc.long")
     * @Assert\Range(min="1", max="9999", minMessage="card.cvc.range", maxMessage="card.cvc.range")
     * @Assert\Regex(pattern="^(([0-9]*)|(([0-9]*).([0-9]*)))$^", match=true, message="card.cvc.invalid") 
     */
    private $cvc;  


    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set number
     *
     * @param integer $number
     * @return Card
     */
    public function setNumber($number)
    {
        $this->number = $number;

        return $this;
    }

    /**
     * Get number
     *
     * @return integer 
     */
    public function getNumber()
    {
        return $this->number;
    }   

    /**
     * Set expirationMonth
     *
     * @param integer $expirationMonth
     * @return Card
     */
    public function setExpirationMonth($expirationMonth)
    {
        $this->expirationMonth = $expirationMonth;

        return $this;
    }

    /**
     * Get expirationMonth
     *
     * @return integer 
     */
    public function getExpirationMonth()
    {
        return $this->expirationMonth;
    }

    /**
     * Set expirationYear
     *
     * @param integer $expirationYear
     * @return Card
     */
    public function setExpirationYear($expirationYear)
    {
        $this->expirationYear = $expirationYear;

        return $this;
    }

    /**
     * Get expirationYear
     *
     * @return integer 
     */
    public function getExpirationYear()
    {
        return $this->expirationYear;
    }

    /**
     * Set cvc
     *
     * @param integer $cvc
     * @return Card
     */
    public function setCvc($cvc)
    {
        $this->cvc = $cvc;

        return $this;
    }

    /**
     * Get cvc
     *
     * @return integer 
     */
    public function getCvc()
    {
        return $this->cvc;
    }   
}

Ok so to validate the expirationMonth and expirationYear properties with custom validation constraints, we will create one constraint, called “CardHasExpired”.

  • Create these files in your bundle main directory: “Validator/Constraints/CardHasExpired.php” and “Validator/Constraints/CardHasExpiredValidator.php”

This is how will look the CardHasExpired.php field:

<?php

namespace EdouardKombo\StripePaymentBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

/**
 * @Annotation
 */
class CardHasExpired extends Constraint
{
    /**
     *
     * @var string
     */
    public $message = 'Your card has expired.';
    
    /**
     * 
     * @return string
     */
    public function validatedBy()
    {
        return 'card_has_expired';
    }

    /**
     * Get class constraints and properties
     * 
     * @return array
     */
    public function getTargets()
    {
        return array(self::CLASS_CONSTRAINT, self::PROPERTY_CONSTRAINT);
    }    
}

Now, we can open the CardHasExpiredValidator.php file and we can call the properties we want from our card entity.
This way we can validate multiple properties.

Here, i just need to check that the expiration month and year are not outdated.
This how our CardHasExpiredValidator.php file will look like.

<?php

namespace EdouardKombo\StripePaymentBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class CardHasExpiredValidator extends ConstraintValidator
{

    /**
     * Method to validate
     * 
     * @param string                                  $value      Property value    
     * @param \Symfony\Component\Validator\Constraint $constraint All properties
     * 
     * @return boolean
     */
    public function validate($value, Constraint $constraint)
    {
        $date               = getdate();
        $year               = (string) $date['year'];
        $month              = (string) $date['mon'];
        
        $yearLastDigits     = substr($year, 2);
        $monthLastDigits    = $month;
        $otherFieldValue    = $this->context->getRoot()->get('expirationMonth')->getData();
        
        if (!empty($otherFieldValue) && ($value <= $yearLastDigits) && 
                ($otherFieldValue <= $monthLastDigits)) {
            $this->context->addViolation(
                $constraint->message,
                array('%string%' => $value)
            );            
            return false;            
        }
        
        return true;
    }
}

Now, we have to create a service that will load our constraints.

<?php
    edouard_kombo_stripe_payment.card.validator.card_has_expired:
        class: EdouardKombo\StripePaymentBundle\Validator\Constraints\CardHasExpiredValidator
        tags:
            - { name: validator.constraint_validator, alias: card_has_expired } 

And finally, we can use our constraint directly in our card entity, and assign it to $expirationYear property.

<?php

namespace EdouardKombo\StripePaymentBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use EdouardKombo\StripePaymentBundle\Validator\Constraints\CardHasExpired;


/**
 * Card
 *
 * @ORM\Table(name="headoo_stripe_credit_card")
 * @ORM\Entity
 */
class Card
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var integer
     *
     * @ORM\Column(name="number", type="integer")
     * @Assert\NotBlank(message="card.number.blank")
     * @Assert\Length(min="16", max="16", minMessage="card.number.length", maxMessage="card.number.length", exactMessage="card.number.length")
     * @Assert\Range(min="1000000000000000", max="9999999999999999", minMessage="card.number.range", maxMessage="card.number.range")
     * @Assert\Regex(pattern="^(([0-9]*)|(([0-9]*).([0-9]*)))$^", match=true, message="card.number.invalid")
     */
    private $number;

    /**
     * @var integer
     *
     * @ORM\Column(name="expirationMonth", type="integer")
     * @Assert\NotBlank(message="card.expiration.month.blank")
     * @Assert\Length(min="2", max="2", minMessage="card.expiration.month.length", maxMessage="card.expiration.month.length", exactMessage="card.expiration.month.length")
     * @Assert\Range(min="1", max="12", minMessage="card.expiration.month.range", maxMessage="card.expiration.month.range")
     * @Assert\Regex(pattern="^(([0-9]*)|(([0-9]*).([0-9]*)))$^", match=true, message="card.expiration.month.invalid")
     */
    private $expirationMonth;

    /**
     * @var integer
     *
     * @ORM\Column(name="expirationYear", type="integer")
     * @Assert\NotBlank(message="card.expiration.year.blank")
     * @Assert\Length(min="2", max="2", minMessage="card.expiration.year.length", maxMessage="card.expiration.year.length", exactMessage="card.expiration.year.length")
     * @Assert\Range(min="0", max="99", minMessage="card.expiration.year.range", maxMessage="card.expiration.month.range")
     * @Assert\Regex(pattern="^(([0-9]*)|(([0-9]*).([0-9]*)))$^", match=true, message="card.expiration.year.invalid")
     * @CardHasExpired(message="card.expiration.expired")
     */
    private $expirationYear;

    /**
     * @var integer
     *
     * @ORM\Column(name="cvc", type="integer")
     * @Assert\NotBlank(message="card.cvc.blank")
     * @Assert\Length(min="3", max="4", minMessage="card.cvc.short", maxMessage="card.cvc.long", exactMessage="card.cvc.long")
     * @Assert\Range(min="1", max="9999", minMessage="card.cvc.range", maxMessage="card.cvc.range")
     * @Assert\Regex(pattern="^(([0-9]*)|(([0-9]*).([0-9]*)))$^", match=true, message="card.cvc.invalid") 
     */
    private $cvc;  


    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set number
     *
     * @param integer $number
     * @return Card
     */
    public function setNumber($number)
    {
        $this->number = $number;

        return $this;
    }

    /**
     * Get number
     *
     * @return integer 
     */
    public function getNumber()
    {
        return $this->number;
    }   

    /**
     * Set expirationMonth
     *
     * @param integer $expirationMonth
     * @return Card
     */
    public function setExpirationMonth($expirationMonth)
    {
        $this->expirationMonth = $expirationMonth;

        return $this;
    }

    /**
     * Get expirationMonth
     *
     * @return integer 
     */
    public function getExpirationMonth()
    {
        return $this->expirationMonth;
    }

    /**
     * Set expirationYear
     *
     * @param integer $expirationYear
     * @return Card
     */
    public function setExpirationYear($expirationYear)
    {
        $this->expirationYear = $expirationYear;

        return $this;
    }

    /**
     * Get expirationYear
     *
     * @return integer 
     */
    public function getExpirationYear()
    {
        return $this->expirationYear;
    }

    /**
     * Set cvc
     *
     * @param integer $cvc
     * @return Card
     */
    public function setCvc($cvc)
    {
        $this->cvc = $cvc;

        return $this;
    }

    /**
     * Get cvc
     *
     * @return integer 
     */
    public function getCvc()
    {
        return $this->cvc;
    }   
}

Congratulations, you’ve done it, you’re a champion.

Symfony2: two fields comparison with custom validation constraints

Symfony2 tutorial: Create custom constraint validation and use it with FOSUserBundle

Today, we will learn how to create a custom validation constraint with Symfony2 and how to use it with FOSUserBundle.
Let’s start.

We have a user registration form in which we want the username to be automatically validated.
Create your formType and your controller classically, we will focus on constraints.

Let’s name our new constraint: “ContainsUser”.
Create a “ContainsUser.php” file in Validator/Contraints directory at your bundle root.

This file should look like this:

<?php

namespace VendorName\UserBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

/**
 * @Annotation
 */
class ContainsUser extends Constraint
{
    public $message = 'The user "%string%" already exists.';
    
    
    public function validatedBy()
    {
        return 'contains_user';
    }

    public function getTargets()
    {
        return array(self::CLASS_CONSTRAINT, self::PROPERTY_CONSTRAINT);
    }    
}
  • ContainsUser will be our constraint.
  • ValidatedBy() function returns an alias that will help defines a service.
  • In getTargets() function, we specify the targets linked to our constraint (properties and/or classes).

Ok, now we have defined our constraint, we need to create the constraintValidator, it is the file containing the validation logic we want.
Create a “ContainsUserValidator.php” file, always in Validator/Constraints directory.

The file should like this:

<?php

namespace VendorName\UserBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class ContainsUserValidator extends ConstraintValidator
{
    protected $em;
    
    public function __construct(\Doctrine\ORM\EntityManager $em)
    {
        $this->em = $em;
    }
    
    public function validate($value, Constraint $constraint)
    {
        $repository     = $this->em->getRepository('HeadooUserBundle:User');
        $user           = $repository->findOneByUsername($value);
        
        if ($user) {
            $this->context->addViolation(
                $constraint->message,
                array('%string%' => $value)
            );
        }
    }
}

We only loaded a Doctrine Orm dependency and, in the “validate” method, simply checked if the user already exists and generate a context violation.

In order to make it work, we must create a service that injects specified dependencies, just like that:

    vendorname.user.validator.contains_user:
        class: VendorName\UserBundle\Validator\Constraints\ContainsUserValidator
        arguments:
            - @doctrine.orm.entity_manager        
        tags:
            - { name: validator.constraint_validator, alias: contains_user }  

Note that the alias must match the validateBy function return value in our constraint class.

Ok, we have now a constraint with a working validator.
The last thing to do is to call the constraint in FOSUserBundle validation file.
You must override the FOSUserBundle “validation.xml” file in your “Resources/config” directory.

We just need to add our constraint for the username property, so we only keep in this file, the username property constraints.

This is how our constraint must looks like:

<constraint name="VendorName\UserBundle\Validator\Constraints\ContainsUser">
    <option name="message">fos_user.username.already_used</option>
    <option name="groups">
        <value>Registration</value>
        <value>Profile</value>
    </option>                
</constraint>

if you want to keep the message specified in the constraint class, just delete the name=’message’ option.

The complete file must look like this.

<?xml version="1.0" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping
        http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">  
    
    <class name="FOS\UserBundle\Model\User">

        <property name="username">
            <constraint name="NotBlank">
                <option name="message">fos_user.username.blank</option>
                <option name="groups">
                    <value>Registration</value>
                    <value>Profile</value>
                </option>
            </constraint>
            <constraint name="Headoo\UserBundle\Validator\Constraints\ContainsUser">
                <option name="message">fos_user.username.already_used</option>
                <option name="groups">
                    <value>Registration</value>
                    <value>Profile</value>
                </option>                
            </constraint>                      
            <constraint name="Length">
                <option name="min">2</option>
                <option name="minMessage">fos_user.username.short</option>
                <option name="max">255</option>
                <option name="maxMessage">fos_user.username.long</option>
                <option name="groups">
                    <value>Registration</value>
                    <value>Profile</value>
                </option>
            </constraint>
        </property>
    </class>

</constraint-mapping>

And that’s it, you have a fully functional customed constraint validation.

Have fun !

Symfony2 tutorial: Create custom constraint validation and use it with FOSUserBundle

Symfony2 tutorial: Log a user manually by creating your own listener

I had to develop a user registration form with specific roles. The problem was that the form was too big and needed to be simplified.
I then decided to create my own bundle for multi-step registrations, and I needed to log a user manually after form registration, and I want to share what I’ve learned with you.

This is how you can perform it.
First of all, you have to know some symfony base principles:
– Current user informations are stored in the security context token, and, can be retrieved from here.
– The security context token listens to security interactiveLogin event that requires user object and token.

So, to log manually a user, we have to:
– create a user object and generate a token (we will use UsernamePasswordToken for that).
– Listen to interactiveLogin event and sends him the token with the current request
– Dispatch InteractiveLogin event to the security context.

Right, now let’s go!

Instead of creating a controller, we’ll create a listener “LoginListener” in a Listener directory.
This is how our code will looks like.

<?php

/**
 * Main docblock
 *
 * PHP version 5
 *
 * @category  Listener
 * @package   MultiStepFormsBundle
 * @author    Edouard Kombo <edouard.kombo@gmail.com>
 * @license   http://www.opensource.org/licenses/mit-license.php MIT License
 * @version   GIT: 1.0.0
 * @link      https://creativcoders.wordpress.com
 * @since     0.0.0
 */
namespace VendorName\UserBundle\Listener;

use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\SecurityContext;

/**
 * Listen to interactive login event and log in a user manually
 *
 * @category Listener
 * @package  MultiStepFormsBundle
 * @author   Edouard Kombo <edouard.kombo@gmail.com>
 * @license  http://www.opensource.org/licenses/mit-license.php MIT License
 * @link     https://creativcoders.wordpress.com
 */
class LoginListener
{
    /** 
     * @var \Symfony\Component\Security\Core\SecurityContext 
     */
    protected $securityContext;
        
    /**
     *
     * @var string $username
     */
    protected $username;
    
    /**
     *
     * @var object $em
     */
    protected $em;
    
    /**
     *
     * @var string $firewall
     */
    protected $firewall; 
    
    /**
     *
     * @var string $userEntity
     */
    protected $userEntity;     
    
    /**
     *
     * @var object $container
     */
    protected $container;    
    
    /**
     * Constructor
     * 
     * @param \Doctrine\ORM\EntityManager $em              Doctrine orm
     * @param object                      $container       Container Object
     * @param SecurityContext             $securityContext Security Management
     * 
     * @return \EdouardKombo\MultiStepFormsBundle\Listener\LoginListener
     */
    public function __construct(\Doctrine\ORM\EntityManager $em, $container, 
            SecurityContext $securityContext)
    {
        $this->em               = (object) $em; 
        $this->container        = (object) $container;
        $this->securityContext  = (object) $securityContext;
        
        return $this;
    }
    
    /**
     * Set the username
     * 
     * @param string $username Username of the current user
     * 
     * @return \EdouardKombo\MultiStepFormsBundle\Listener\LoginListener
     */
    public function setUsername($username)
    {
        $this->username = (string) $username;
        return $this;
    }
    
    /**
     * Set the firewall
     * 
     * @param string $firewall Actual security firewall
     * 
     * @return \EdouardKombo\MultiStepFormsBundle\Listener\LoginListener
     */
    public function setFirewall($firewall)
    {
        $this->firewall = (string) $firewall;
        return $this;
    }
    
    /**
     * Define the user entity
     * 
     * @param string $userEntity Project User entity
     * 
     * @return \EdouardKombo\MultiStepFormsBundle\Listener\LoginListener
     */
    public function setUserEntity($userEntity)
    {
        $this->userEntity = (string) $userEntity;
        return $this;
    }    
    
    /**
     * Login a user and dispatch the event.
     * 
     * @return object
     * @throws UsernameNotFoundException
     */
    public function secureInteractiveLogin()
    {
        if (!$this->securityContext->isGranted('IS_AUTHENTICATED_FULLY')) {
            
            $repository  = $this->em->getRepository($this->userEntity);       
            $user        = $repository->findOneByUsername($this->username);

            $request     = $this->container->get('request');
                
            if (!$user) {
                throw new UsernameNotFoundException("User not found");

            } else {
                //We need the name of the firewall
                $token = new UsernamePasswordToken($user, null, $this->firewall, 
                    $user->getRoles());

                $this->securityContext->setToken($token);

                //Now, login the user
                $event = new InteractiveLoginEvent($request, $token);
                return $this->container->get("event_dispatcher")->dispatch(
                    "security.interactive_login", 
                    $event
                );
            }            
        } else {
            $user = $event->getAuthenticationToken()->getUser();
        }
        
        return $user;
    }    
}

Last step is to create a service to inject dependencies on object construct.

//Change depending on your needs
services:
    multistep_forms.login_listener:
        class: %multistep_forms.login_listener.class%
        arguments:
            - @doctrine.orm.entity_manager
            - @service_container
            - @security.context
Symfony2 tutorial: Log a user manually by creating your own listener

Symfony2 differences between doctrine:schema:update and doctrine:migrations

When you have to create or update your database schema from entities, you are used to type these commands:

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

Doctrine offers another extension called Migrations that helps you:
– Save your different database schemas during the life of your project
– Move tables and databases with their content, what a simple doctrine:schema:update can’t do.
– Generate accessible migrations queries from the difference between your entities and your database schema.
– Include your own migration queries.

To use doctrine migrations, you will often use these commands:

php app/console doctrine:migrations:diff #Generate migrations queries
php app/console doctrine:migrations:migrate #Refresh your database schema from migrations queries

Finally, for rapid schema updates, use doctrine:schema:update, but, for complex modifications in your database, prefer doctrine:migrations.

Symfony2 differences between doctrine:schema:update and doctrine:migrations

How to install and configure FOSUserBundle with Symfony 2.5.*

When it comes to start multiple projects, configuring all the necessary vendors become an automatism.
Here is a guideline to help you configure new symfony projects with FOSUserBundle.

 
First, target FOSUserBundle dependency in your composer.json

"require": {
    "friendsofsymfony/user-bundle": "2.0.*@dev"
}

 
Install all your dependencies

php composer.phar update

 
Then, generate a new bundle we will name userBundle

php bin/console generate:bundle

 
In the userBundle.php generated file, extend FOSUserBundle like this

class UserBundle extends Bundle
 {
    public function getParent()
    {
      return 'FOSUserBundle';
    }
 }

 
We can now configure FOSUserBundle in our security.yml in “app/config”.
We will change default encoder

encoders:
  FOS\UserBundle\Model\UserInterface: sha512

 
We change the default provider

providers:       
    fos_userbundle:
        id: fos_user.user_provider.username

 
We then the new firewall of our application

main:
  pattern: ^/
     form_login:
         provider: fos_userbundle
         csrf_provider: form.csrf_provider
     logout:       true
     anonymous:    true

 
Once the firewall is configured, we have to secure our application urls

- { path: ^/(_(profiler|wdt)|css|images|js)/, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, role: ROLE_ADMIN }

  
Ok, right, we have configured the basic security options and created a userBundle that extends FOSUserBundle.
But Symfony doesn’t actually know who is FOSUserBundle, and, our firewall doesn’t know where to find the user entity.

 
We have to call FOSUserBundle in the appKernel.

$bundles = array(
         new FOS\UserBundle\FOSUserBundle(),
 );

 
Generate an empty User entity.

php bin/console doctrine:generate:entity --entity=VendorUserBundle:User

 
in the User entity, extend FOSUserBundle and turn id attribute from private to protected.

<?php

namespace Vendor\UserBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Entity\User as BaseUser;

/**
 * User
 *
 * @ORM\Table()
 * @ORM\Entity
 */
class User extends BaseUser
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;


    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }
}

 
Ok, now we must specify FOSUserBundle where to find the user entity in app/config/config.yml, and activate framework locale by uncommenting it.

fos_user:
  db_driver: orm
  firewall_name: main
  user_class: Vendor\UserBundle\Entity\User

framework:
    translator:      { fallback: "%locale%" }

 
To finish, just add FOSUserBundle routes in our app/config/routing.yml file…

fos_user_security:
   resource: "@FOSUserBundle/Resources/config/routing/security.xml"
 
 fos_user_profile:
   resource: "@FOSUserBundle/Resources/config/routing/profile.xml"
   prefix: /profile
 
 fos_user_register:
   resource: "@FOSUserBundle/Resources/config/routing/registration.xml"
   prefix: /register
 
 fos_user_resetting:
   resource: "@FOSUserBundle/Resources/config/routing/resetting.xml"
   prefix: /resetting
 
 fos_user_change_password:
    resource: "@FOSUserBundle/Resources/config/routing/change_password.xml"
    prefix: /profile

 
And finally, upgrade your database schema

php bin/console doctrine:schema:update --dump-sql
php bin/console doctrine:schema:update --force

You have a ready to use FOSUserBundle installation.

How to install and configure FOSUserBundle with Symfony 2.5.*

Symfony2 // [Symfony\Component\DependencyInjection\Exception\InvalidArgumentException] unable to parse file…

When reorganizing my bundles, I decided to load my services from my bundle dependency injection.
My bundle name is “UserBundle” and my vendor name “xxx”.

So, I created an extension file from my bundle called “xxxUserExtension.php” in “UserBundle/DependencyInjection/”

This was my content file:

<?php

namespace xxx\UserBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;

/**
 * This is the class that loads and manages your bundle configuration
 *
 * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
 */
class xxxUserExtension extends Extension
{
    /**
     * {@inheritDoc}
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('user.yml');
        $loader->load('forms.yml');        
        $loader->load('validation.xml');       
    }
}

And of course, I’ve got this error:

    [Symfony\Component\DependencyInjection\Exception\InvalidArgumentException]
    Unable to parse file "C:\wamp\www\xxx\src\Headoo\UserBundle\DependencyInjection/../Resources/config\user.yml".

Maybe you want to know the cause, right?

This error simply occured because I was loading an yml file with an xmlFileLoader. Also, i was loading an xml file, we never have to load two different file types with a single file type loader.

Look here:

        $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('user.yml');
        $loader->load('forms.yml');
        $loader->load('validation.xml'); 

So to solve this error, you have to use only one extension for your services and choose the correct loader.
This is the correct file:

<?php

namespace xxx\UserBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;

/**
 * This is the class that loads and manages your bundle configuration
 *
 * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
 */
class xxxUserExtension extends Extension
{
    /**
     * {@inheritDoc}
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('user.yml');
        $loader->load('forms.yml');               
    }
}

Hope, this helped.

Symfony2 // [Symfony\Component\DependencyInjection\Exception\InvalidArgumentException] unable to parse file…

Symfony2 // How to organize your bundles

When creating a new project, you probably already asked yourself this question, how can I organize my bundles?

The answer is that there is no standard good practice, it all depends of the nature of your project.

For small projects in which you don’t need to reuse some codes, you can decide to put all your sources in a single “appBundle”.
By against, for bigger projects that are frequently evolving, it is a good practice to organize more properly your bundles.

Usually we proceed like this: one bundle per module and one controller per functionality.
So, you’ll have to clearly identify your modules and make them bundles like this:

- MySiteName
    - UserBundle
        - RegistrationController
        - ChangePasswordController
    - ContactBundle
        - ContactController
    - PaymentBundle
        - PaypalController
        - MoneyBookerController

One advantage of this method is that you could publicly share some bundles to the community in public platforms.

More informations on the official Symfony Doc: http://symfony.com/doc/current/quick_tour/the_architecture.html

Symfony2 // How to organize your bundles

Symfony2 // Differences between Roles and Acl

Let’s imagine you have a blog with two users (Ana and Edouard), and want to manage specific actions like:

  • authorize Edouard to add a new contributor to the blog
  • authorize Ana to create, edit and delete her own blog post
  • authorize Edouard to delete Ana’s blog post

What is a Role

A role is a set of permissions hard coded in your application, and you can hard code yourselves. in your application. When checking if Edouard is able to delete Ana’s blog, your application checks in your code the current role the user has..

ACLs

Access Control Lists are useful when you need to take an authorization decision based on a Role + a domain object. Explicitely, they allow to give specific permissions to a specific object for a specific user.

Ana is allowed to edit blog entries written by her only. To check this authorization, you need Ana’s Roles and the Post model she’s trying to edit.

So, Edouard is allowed to all blog entries because he has the ROLE_ADMIN. The decision here is only based on a Role.

 

See Symfony documentation on Security/Roles and Symfony’s documentation on ACLs.

Symfony2 // Differences between Roles and Acl