Ripcurl
Ripcurl

Reputation: 59

How to manage a ManyToOne relation with symfony 2 and doctrine

I’m currently discovering symfony 2 and training myself on this wonderful framework. So I have newbie questions...

I followed this tutorial in order to manage relations with doctrine : http://symfony.com/doc/current/book/doctrine.html

Well, during my tests I had a question and a problem.

First the question: Symphony best practices says that it’s better to use annotations for doctrine mapping instead of yaml in an other .orm.yml file. Good, I totally agree with that. "Using annotations is convenient and allows you to define everything in the same file". So if we choose to use annotations, I guess we do not need an .orm.yml mapping file anymore? Suppose I have a table named userType. I have created an entity class userType.php and added mapping information with annotations directly in this class. Anything I try, adding the statement:

use Doctrine\ORM\Mapping as ORM;

or not in my userType.php class, symfony display this error:

No mapping file found named 'UserType.orm.yml' for class 'AppBundle\Entity\UserType

if I don‘t create the UserType.orm.yml file.

So this my question: If we choose to use only annotations, do we still need an .orm.yml mapping file? If yes, what are the minimum information that we must put in this file?

Second point my problem: I have my users roles in a different table (userType) than the user table.

+-------------------------------------------+-------------------------------------------+
|user                                       |   userType                                |
|id    username    password    userType_id  |   userType_id  label          role        |
|1     testuser    something   1            |   1            Administrator  ROLE_ADMIN  |
+-------------------------------------------+-------------------------------------------+

So I suppose I need to add a manyToOne relation in my user.php entity class (many users can have the only same one role).

So I added thoses lines in my User.orm.yml mapping file

(…)
manyToOne:
        userType:
            targetEntity: UserType
            joinColumn:
                name: userType_id
                referencedColumnName: userType_id
    lifecycleCallbacks: {  }
(…)

And declared the var userType in my user.php entity class like this

/**
*
*/
private $userType;

This is my userType.php entity class file:

    namespace AppBundle\Entity;

    use Doctrine\ORM\Mapping as ORM;

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

        /**
         * @ORM\Column(type="string", name="label")
         */
        private $label;


        /**
         * @ORM\Column(type="simple_array", name="role")
         */
        private $role;


        public function getId()
        {
            return $this->id;
        }

        /**
         *
         */
        public function getLabel()
        {
            return $this->label;
        }
        public function setLabel($label)
        {
            $this->label = $label;
        }

        /**
         *
         */
        public function getRole()
        {
            return $this->role;
        }
        public function setRole($role)
        {
            $this->role = $role;
        }
}

And my UserType.orm.yml file:

AppBundle\Entity\UserType:
    type: entity
    table: userType
    id:
        id:
            type: integer
            id: true
            column: userType_id
            generator:
                strategy: AUTO
    fields:
        label:
            type: string
            length: '150'
            column: label
        role:
            type: simple_array
            length: '100'
            column: role
    lifecycleCallbacks: {  }

Ok it seems the $userType var of my user.php entity is an object containing the userType data. But if I do a

var_export($this->userType, true);

of the $userType object I got this :

Proxies\__CG__\AppBundle\Entity\UserType::
__set_state(array( '__initializer__' => Closure::
__set_state(array( )), '__cloner__' =>
Closure::__set_state(array( )), '__isInitialized__' =>
false, 'id' => 1, 'label' => NULL, 'role' => NULL, ))

Only the field id is filled others are NULL. How can I also get the label and role field values in the object $userType? What is my mistake?

Many thanks for your help.

Upvotes: 2

Views: 1266

Answers (2)

Ripcurl
Ripcurl

Reputation: 59

Thanks for your advices. They helped me a lot.

I solved my 2 troubles.

For my .orm files problem : indeed as @chalasr advised, after deleting the Resources/config/doctrine folder, Symphony stopped showing me error regarding missing .orm files. By following @chalasr other tips I managed to stop using orm files. Thanks.

But I still had my other problem (only the "id "field was filled in the $userType object others where NULL). I notice that the $userType object was not initialized and that it was in fact a doctrine “lazy-loading-issue ».

These 2 posts helped me to solve my problem:

Get entities from a unidirectional many to many relation with Doctrine2 and Symfony2

doctrine2 association is not initialized

I added the option fetch="EAGER" to my ManyToOne relation and now all $userType object fields are perfectly filled.

This my actual ManyToOne relation (made with annotations ;-))

/**
     * @ORM\ManyToOne(targetEntity="UserType", fetch="EAGER")
     * @ORM\JoinColumn(name="users_types_id", referencedColumnName="users_types_id")
     */
    private $userType;

And this how I get the role field value

/**
     * Returns the roles or permissions granted to the user for security.
     */
    public function getRoles()
    {

        $accessor_user_role = PropertyAccess::createPropertyAccessor();

        $roles = $accessor_user_role->getValue($this->userType, 'role');

        // guarantees that a user always has at least one role for security
        if (empty($roles)) {
            $roles[] = 'ROLE_USER';
        }

        return $roles;
    }

I hope that I respect Symfony good practices by proceeding in this way.

Many thanks for your help.

Upvotes: 1

chalasr
chalasr

Reputation: 13167

No, you have to do a choice between this two formats.

There are many problems in your entity.

You have omitted the @ORM\Table statement, use :

/**
 * @ORM\Entity
 * @ORM\Table(name="user_types")
 */
class UserType
{
    //...
}

Then, you must rename your file corresponding to the name of the class it contains.

Your class is called UserType but the file that contains it is named userType.php.
You must use UserType.php instead, according to the PSR-0 standards

Each namespace separator is converted to a DIRECTORY_SEPARATOR when loading from the file system.
[...]
The fully-qualified namespace and class is suffixed with .php when loading from the file system.

Example : App\AcmeBundle\Entity\User will become src/App/AcmeBundle/Entity/User.php

Also, I think you are omitting a part of your namespace in the declaration of your entity, add the missing first part of your bundle namespace (as stated by PSR, it should be the name of the first folder between src and your AppBundle directories).

If you have begin to work with YAML before use annotations, do the following process :

  • Remove the whole Resources/config/doctrine directory of your bundle (or just the mapping file corresponding to the entity).
  • Clear the cache of your entities metadata using php app/console doctrine:cache:clear-metadata
  • Clear your application cache using php app/console cache:clear

After doing this, update your database schema with the correct mapping by doing :
php app/console doctrine:schema:update --force

Upvotes: 0

Related Questions