Symfony Iterating over ArrayCollection - symfony

In my app I have 2 entities; User & Booking.
Booking entity:
namespace App\Entity;
/**
* #ORM\Table(name="booking")
* #ORM\Entity(repositoryClass="App\Repository\BookingRepository")
*/
class Booking
{
/**
* #ORM\Column(type="boolean")
* #Assert\NotBlank()
*/
private $isActive;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="bookings")
*/
private $user;
User entity:
/**
* #ORM\Table(name="app_user")
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #UniqueEntity(fields="email", message="This email address is already in use")
*/
class User implements AdvancedUserInterface
{
/**
* #ORM\Column(type="string", length=255, unique=true)
* #Assert\NotBlank()
* #Assert\Email()
*/
private $email;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Booking", mappedBy="user")
* #Expose
*/
private $bookings;
/**
* User constructor.
*/
public function __construct()
{
$this->bookings = new ArrayCollection();
}
I tried to add a function to my user entity that returns the active booking, I tried this:
/**
* #return mixed
*/
public function getActiveBooking()
{
foreach( $this->bookings as $booking ) {
if( $booking->getIsActive() ) {
return $booking;
}
}
}
But I get the following error: Error: Call to a member function getRoom() on null
When I call it using $user->getActiveBooking()->getRoom()->getId()

Make sure that the user you are working with has an active booking.
getActiveBooking() is returning null because it seems user does not have an active booking.
That's why you are getting an error that you cannot call getRoom() on null because the previous function has returned null.

Have you tried to add a joinColumn like this:
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="bookings")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
*/
private $user;

Related

API PLATFROM - Query Cannot return null for non-nullable field User.isActive

I'm facing issues with easy query to API-PLATFROM.
GraphQL Playground:
{
users{
edges{
node{
id
email
isActive
}
}
}
}
error:
"debugMessage": "Cannot return null for non-nullable field User.isActive.", same error I've got for createdAt filed.
Symfony entity:
<?php
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #ApiResource(
* normalizationContext={"groups"={"read"}},
* denormalizationContext={"groups"={"write"}}
* )
*/
class User implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
* #Assert\NotNull
* #Assert\Email
* #Groups({"read","write"})
*/
private $email;
/**
* #ORM\Column(type="boolean")
* #Assert\NotNull
* #Groups({"read","write"})
*/
private $isActive;
public function getIsActive(): bool
{
return $this->isActive;
}
public function setIsActive(bool $isActive): self
{
$this->isActive = $isActive;
return $this;
}
}
Of course, I've got other getters and setters. When I change the query for example to return id and email, everything works correctly.

recursive symfony 3.4 complex form embed

Having this schema:
I Would like make a form like that
I don't know how can I build the form.
A company haves many persons.
In a company we have a corporate hierarchy : employee, boss, subordinate
A worker can have subordinate worker It's why I have added a recurve link inside company_person for parent child notion link.
The Role is here for saying what is the role of the worker.
Company
class Company
{
/**
* #ORM\Column(type="string", length=45, nullable=true)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="CompanyPerson", mappedBy="company")
*/
private $companypersons;
}
Person
class Person
{
/**
* #ORM\Column(type="string", length=45, nullable=true)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="CompanyPerson", mappedBy="person", cascade={"persist"})
* #var ArrayCollection
*/
private $companypersons;
}
Status
class Status
{
/**
* #ORM\Column(type="string", length=45, nullable=true)
*/
private $libelle;
/**
* #ORM\OneToMany(targetEntity="CompanyPerson", mappedBy="statut")
*/
protected $companypersons;
}
CompanyPerson
class CompanyPerson
{
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $dateAdded;
/**
* #ORM\ManyToOne(targetEntity="Company", inversedBy="companypersons",
cascade={"persist"})
*/
private $company;
/**
* #ORM\ManyToOne(targetEntity="Person",
inversedBy="companypersons", cascade={"persist"})
*/
private $person;
/**
* #ORM\ManyToOne(targetEntity="Status",
inversedBy="companypersons", cascade={"persist"})
*/
private $status;
/**
* #ORM\ManyToOne(targetEntity="CompanyPerson", inversedBy="childrens")
*/
private $parent;
/**
* #ORM\OneToMany(targetEntity="CompanyPerson", mappedBy="parent")
*/
private $childrens;
public function __construct(Company $companay= null, Person $person =
null, CompanyPerson $parent = null, Status $status = null)
{
$this->companay = $companay;
$this->person = $person;
$this->parent = $parent;
$this->status = $status;
$this->childrens = new ArrayCollection();
}
}
I worked on embed form with collections, but I can not create this form, because it is't very complex for me.
You have rational problems in your mapping. you should use the parent-children pattern inside Person , not CompanyPerson. a CompanyPerson here, is a joint-entity. It is there to persist things like:
$dateAdded : the date at which a relation between a Company and a Person and an Status was stablished.
Person
class Person
{
/**
* #ORM\Column(type="string", length=45, nullable=true)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="CompanyPerson", mappedBy="person", cascade={"persist"})
* #var ArrayCollection
*/
private $companypersons;
/**
* #ORM\ManyToOne(targetEntity="Person", inversedBy="childrens")
*/
private $parent;
/**
* #ORM\OneToMany(targetEntity="Person", mappedBy="parent")
*/
private $children;
}
Note to keep children without s, as it is already in plural form.
Also keep in mind to use cascade operations on mapping definition on the inverse side of association. So the
class CompanyPerson
{
/**
* #ORM\ManyToOne(targetEntity="Company", inversedBy="companypersons", cascade={"persist"})
*/
private $company;
/**
* #ORM\ManyToOne(targetEntity="Person", inversedBy="companypersons", cascade={"persist"})
*/
private $person;
}
should be
class CompanyPerson
{
/**
* #ORM\ManyToOne(targetEntity="Company", inversedBy="companypersons")
*/
private $company;
/**
* #ORM\ManyToOne(targetEntity="Person", inversedBy="companypersons")
*/
private $person;
}
Your Question
To get around the so called, complex form! you may use mapped=false setting attribute in your fields. using this, you may tell the symfony that this field, is not mapped to a property of the target entity of your form.
In your case you have CompanyType form (with Company as target entity) and want to create all those stuff in one go! So you may add unmapped fields to your form and capture theme inside your controller and parse theme as you wish.
Using this method, you should create joint-entities (like Company-Person) by your self (again inside your controller).

symfony2, doctrine2, class inheritance

I'm playing a little with doctrine and symfony and i encountered a problem with this model.
I setup a base class ( entity ) and then a child class ( entity ), so far everything works smooth after that i wanted to add more fields to my table and i used doctrine:generate:entities BundleName. And here is the problem. If i do not set the variables from base class to protected symfony / doctrine says it cannot access that variable but if i set it as protected and generate the entities again it creates getters and setters for the protected variables from base class in my child class which i find it weird so i googled a little but i only found one topic about this problem and didn't had an answer for it.
The Base Class got set the annotation MappedSuperclass but .. didn't had any effect on it.
Base class which implements AdvancedUserInterface
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
/**
* #ORM\MappedSuperclass
*/
class BaseUser implements AdvancedUserInterface
{
/**
* #var string
* #ORM\Column(name="username", type="string", length=255, nullable=true)
*/
protected $username;
/**
* #var string
* #ORM\Column(name="password", type="string", length=255)
*/
protected $password;
/**
* #var bool
* #ORM\Column(name="isActive", type="boolean")
*/
protected $isActive = '0';
/**
* [$isAccountNonExpired description].
*
* #var bool
* #ORM\Column(name="isAccountNonExpired", type="boolean")
*/
protected $isAccountNonExpired = '1';
/**
* [$isAccountNonLocked description].
*
* #var bool
* #ORM\Column(name="isAccountNonLocked", type="boolean")
*/
protected $isAccountNonLocked = '0';
/**
* [$isCredentialsNonExpired description].
*
* #var bool
* #ORM\Column(name="isCredentialsNonExpired", type="boolean")
*/
protected $isCredentialsNonExpired = '0';
/**
* Set password
*
* #param string $password
* #return User
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set username
*
* #param string $username
* #return User
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Get username
*
* #return string
*/
public function getUsername()
{
return $this->username;
}
/**
* USER INTERFACE IMPLEMENTATION.
*/
public function getSalt()
{
// you *may* need a real salt depending on your encoder
// see section on salt below
}
public function getRoles()
{
return array('ROLE_ADMIN');
}
public function eraseCredentials()
{
return $this->plainPassword = null;
}
/**
* ADVANCED USER INTERFACE IMPLEMENTATION.
*/
/**
* Checks whether the user's account has expired.
*
* Internally, if this method returns false, the authentication system
* will throw an AccountExpiredException and prevent login.
*
* #return bool true if the user's account is non expired, false otherwise
*
* #see AccountExpiredException
*/
public function isAccountNonExpired()
{
return $this->isAccountNonExpired;
}
/**
* Checks whether the user is locked.
*
* Internally, if this method returns false, the authentication system
* will throw a LockedException and prevent login.
*
* #return bool true if the user is not locked, false otherwise
*
* #see LockedException
*/
public function isAccountNonLocked()
{
return $this->isAccountNonLocked;
}
/**
* Checks whether the user's credentials (password) has expired.
*
* Internally, if this method returns false, the authentication system
* will throw a CredentialsExpiredException and prevent login.
*
* #return bool true if the user's credentials are non expired, false otherwise
*
* #see CredentialsExpiredException
*/
public function isCredentialsNonExpired()
{
return $this->isCredentialsNonExpired;
}
/**
* Checks whether the user is enabled.
*
* Internally, if this method returns false, the authentication system
* will throw a DisabledException and prevent login.
*
* #return bool true if the user is enabled, false otherwise
*
* #see DisabledException
*/
public function isEnabled()
{
return $this->isActive;
}
/**
* Set isActive
*
* #param boolean $isActive
* #return BaseUser
*/
public function setIsActive($isActive)
{
$this->isActive = $isActive;
return $this;
}
/**
* Get isActive
*
* #return boolean
*/
public function getIsActive()
{
return $this->isActive;
}
/**
* Set isAccountNonExpired
*
* #param boolean $isAccountNonExpired
* #return BaseUser
*/
public function setIsAccountNonExpired($isAccountNonExpired)
{
$this->isAccountNonExpired = $isAccountNonExpired;
return $this;
}
/**
* Get isAccountNonExpired
*
* #return boolean
*/
public function getIsAccountNonExpired()
{
return $this->isAccountNonExpired;
}
/**
* Set isAccountNonLocked
*
* #param boolean $isAccountNonLocked
* #return BaseUser
*/
public function setIsAccountNonLocked($isAccountNonLocked)
{
$this->isAccountNonLocked = $isAccountNonLocked;
return $this;
}
/**
* Get isAccountNonLocked
*
* #return boolean
*/
public function getIsAccountNonLocked()
{
return $this->isAccountNonLocked;
}
/**
* Set isCredentialsNonExpired
*
* #param boolean $isCredentialsNonExpired
* #return BaseUser
*/
public function setIsCredentialsNonExpired($isCredentialsNonExpired)
{
$this->isCredentialsNonExpired = $isCredentialsNonExpired;
return $this;
}
/**
* Get isCredentialsNonExpired
*
* #return boolean
*/
public function getIsCredentialsNonExpired()
{
return $this->isCredentialsNonExpired;
}
}
And child class
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Gedmo\Mapping\Annotation as Gedmo;
use AppBundle\Entity\BaseUser;
/**
* User
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Zenith\UserBundle\Entity\UserRepository")
* #UniqueEntity(fields="email", message="Email already taken")
* #UniqueEntity(fields="username", message="Username already taken")
*/
class User extends BaseUser
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=255)
* #Assert\Email()
* #Assert\NotBlank()
*/
private $email;
/**
* #var string
* #Assert\NotBlank()
* #Assert\Length(max = 4096)
*/
protected $plainPassword;
/**
* Date when the user needs to change his password
* for security reasons.
*
* #var datetime
*
* #ORM\Column(name="credentialsExpireAt", type="datetime", nullable=true)
*/
private $credentialsExpireAt;
/**
* Date when the account expires
* This can be used for temporary accounts.
*
* #var datetime
*
* #ORM\Column(name="accountExpireAt", type="datetime", nullable=true)
*/
private $accountExpireAt;
/**
* Saves last login date of the user.
*
* #var datetime
* #ORM\Column(name="lastLogin", type="datetime", nullable=true)
*/
private $lastLogin;
/**
*
*
* #ORM\Column(name="createdAt", type="datetime")
* #Gedmo\Timestampable(on="create")
*
* #var datetime
*/
private $createdAt;
/**
* #ORM\Column(name="editedAt", type="datetime")
* #Gedmo\Timestampable(on="update")
*
* #var datetime
*/
private $editedAt;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set email
*
* #param string $email
* #return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set credentialsExpireAt
*
* #param \DateTime $credentialsExpireAt
* #return User
*/
public function setCredentialsExpireAt($credentialsExpireAt)
{
$this->credentialsExpireAt = $credentialsExpireAt;
return $this;
}
/**
* Get credentialsExpireAt
*
* #return \DateTime
*/
public function getCredentialsExpireAt()
{
return $this->credentialsExpireAt;
}
/**
* Set accountExpireAt
*
* #param \DateTime $accountExpireAt
* #return User
*/
public function setAccountExpireAt($accountExpireAt)
{
$this->accountExpireAt = $accountExpireAt;
return $this;
}
/**
* Get accountExpireAt
*
* #return \DateTime
*/
public function getAccountExpireAt()
{
return $this->accountExpireAt;
}
/**
* Set lastLogin
*
* #param \DateTime $lastLogin
* #return User
*/
public function setLastLogin($lastLogin)
{
$this->lastLogin = $lastLogin;
return $this;
}
/**
* Get lastLogin
*
* #return \DateTime
*/
public function getLastLogin()
{
return $this->lastLogin;
}
/**
* Set createdAt
*
* #param \DateTime $createdAt
* #return User
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt
*
* #return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Set editedAt
*
* #param \DateTime $editedAt
* #return User
*/
public function setEditedAt($editedAt)
{
$this->editedAt = $editedAt;
return $this;
}
/**
* Get editedAt
*
* #return \DateTime
*/
public function getEditedAt()
{
return $this->editedAt;
}
}
So what is the question? Why the command do so? Or how to use it in this case?
doctrine:generate:entities generates an entity class from it's mapping. The mapping may be described in different formats, and PHP annotation is only one of them (along with xml and yml). I suppose the logic works that way - first from annotations mapping is creating, and later from mapping (regardless how this mapping was described, and if properties were inherited on no) is generated a class.
So don't worry, remove unnecessary code and move on. This is only a tool that saves some time.

Symfony2/Doctrine2 Inheritance

I'm attempting to accomplish BASIC inheritance in Doctrine 2, but I'm running into several major issues. Such a task should not be so complicated. Let's get down to business...
I have three classes, BaseFoodType, Drink, and Snack. My BaseFoodType has the following class definition:
/** #ORM\MappedSuperclass */
class BaseFoodType {
/**
* #ORM\Column(type="integer", length=7)
*/
public $budget = 0;
}
Which follows the instructions for inheritance on the doctrine website: http://docs.doctrine-project.org/en/2.0.x/reference/inheritance-mapping.html
Here is what the sub-classes look like prior to generating my entities:
namespace MySite\MainBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* MySite\MainBundle\Entity\EventDrink
*
* #ORM\Table(name="drink")
* #ORM\Entity
*/
class Drink extends BaseFoodType {
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="integer", length=5, nullable=true)
*/
public $people_count;
}
Both Drink, and Snack inherit from this base class but I'm running into numerous issues when attempting to build my entities using the doctrine:generate:entities command. First, Symfony inserts a private "budget" property into each subclass, along with getters and setters (THIS DEFEATS THE PURPOSE INHERITANCE)
/**
* #var integer
*/
private $budget;
/**
* Set budget
*
* #param integer $budget
*/
public function setBudget($budget)
{
$this->budget = $budget;
return $this;
}
/**
* Get budget
*
* #return integer
*/
public function getBudget()
{
return $this->budget;
}
Second, I'm getting a fatal error:
Fatal error: Access level to MySite\MainBundle\Entity\Drink::$budget
must be public (as in class MySite\MainBundle\Entity\BaseFoodType) in
C:\xampp\htdocs\MySite\src\MySite\MainBundle\Entity\Drink.php on line
197
I could probably make the generated properties public and be on my way, but again, that defeats the purpose of inheritance!
Thanks in advance for any insight.
Doctrine provides the means to specify the visibility of generated fields. Either protected or private. The default is private.
The problem is that the Symfony command that invokes Doctrine offers no way to change this.
Creating your own subclass of the standard Symfony command will allow you more control over the generation process. This might help you along.
namespace Foo\Bundle\FooBundle\Command;
use Doctrine\Bundle\DoctrineBundle\Command as DC;
use Doctrine\ORM\Tools\EntityGenerator;
class GenerateEntitiesDoctrineCommand extends DC\GenerateEntitiesDoctrineCommand
{
protected function configure()
{
parent::configure();
$this->setName('foo:generate:entities');
}
/**
* get a doctrine entity generator
*
* #return EntityGenerator
*/
protected function getEntityGenerator()
{
$entityGenerator = new EntityGenerator();
$entityGenerator->setGenerateAnnotations(true);
$entityGenerator->setGenerateStubMethods(true);
$entityGenerator->setRegenerateEntityIfExists(false);
$entityGenerator->setUpdateEntityIfExists(true);
$entityGenerator->setNumSpaces(4);
$entityGenerator->setAnnotationPrefix('ORM\\');
$entityGenerator->setFieldVisibility($entityGenerator::FIELD_VISIBLE_PROTECTED);
return $entityGenerator;
}
}
This does two things. It sets the property visibility to protected. This prevents php errors.
$entityGenerator->setFieldVisibility($entityGenerator::FIELD_VISIBLE_PROTECTED);
It also copies the annotations from mapped super class into the entity class.
$entityGenerator->setGenerateAnnotations(true);
Here's some example code where properties are inherited from a base class and their visibility and annotations copy correctly into the inheriting class
/** #ORM\MappedSuperclass */
class DataSuper {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Campaign", inversedBy="data")
* #ORM\JoinColumn(name="campaign_id", referencedColumnName="id")
* #Exclude
*/
protected $campaign;
/**
* #ORM\Column(type="text", nullable=true, name="data")
*/
protected $data;
/**
* #ORM\Column(type="datetime")
*/
protected $createdDate;
}
/**
* #ORM\Entity(repositoryClass="Foo\Bundle\FooBundle\Entity\DataRepository")
* #ORM\Table(name="data")
* #ExclusionPolicy("none")
*/
class Data extends DataSuper
{
}
After generation the Data class looks like:
class Data extends DataSuper
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", precision=0, scale=0, nullable=false, unique=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="data", type="text", precision=0, scale=0, nullable=true, unique=false)
*/
protected $data;
/**
* #var \DateTime
*
* #ORM\Column(name="createdDate", type="datetime", precision=0, scale=0, nullable=false, unique=false)
*/
protected $createdDate;
/**
* #var \Foo\Bundle\FooBundle\Entity\Campaign
*
* #ORM\ManyToOne(targetEntity="Foo\Bundle\FooBundle\Entity\Campaign", inversedBy="data")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="campaign_id", referencedColumnName="id", nullable=true)
* })
*/
protected $campaign;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set data
*
* #param string $data
* #return Data
*/
public function setData($data)
{
$this->data = $data;
return $this;
}
/**
* Get data
*
* #return string
*/
public function getData()
{
return $this->data;
}
/**
* Set createdDate
*
* #param \DateTime $createdDate
* #return Data
*/
public function setCreatedDate($createdDate)
{
$this->createdDate = $createdDate;
return $this;
}
/**
* Get createdDate
*
* #return \DateTime
*/
public function getCreatedDate()
{
return $this->createdDate;
}
/**
* Set campaign
*
* #param \Foo\Bundle\FooBundle\Entity\Campaign $campaign
* #return Data
*/
public function setCampaign(\Foo\Bundle\FooBundle\Entity\Campaign $campaign = null)
{
$this->campaign = $campaign;
return $this;
}
/**
* Get campaign
*
* #return \Foo\Bundle\FooBundle\Entity\Campaign
*/
public function getCampaign()
{
return $this->campaign;
}
}
And the table structure is correct once you do:
php app/console doctrine:schema:update --force
The exception is being thrown because BaseFoodType::budget is a public property and doctrine:generate:entities created a private property in your Drink / Snack classes extending BaseFoodType ( which is not correct but the way the command works by now ).
Property visibility in a subclass can only be the same level or more liberate ( private -> protected -> public ) but never more restrictive.
doctrine:generate:entities did not take superclass's public property into account when generating the getters/setters as the implementation with a public property is non-standard.
Therefore you will have to adjust the generated class manually.
I recommend using private/protected properties combined with getters & setters.

RoleInterface throws "call on a non-object" error

I'm working over Symfony 2.0.16
I have in my UserProvider the method getRoles
public function getRoles()
{
/**
* #var \Doctrine\Common\Collections\ArrayCollection $rol
*/
return $this->rol->toArray();
}
and my Rol entity has the role interface
class Rol implements \Symfony\Component\Security\Core\Role\RoleInterface
//...
public function getRole()
{
return $this->getName();
}
but when I try to login I get the following error
Fatal error: Call to a member function getRole() on a non-object in C:\Users\julian\Code\parqueadero\vendor\symfony\src\Symfony\Bundle\SecurityBundle\DataCollector\SecurityDataCollector.php on line 57
Reading the class SecurityDataCollector, the error is thrown by a Closure
array_map(function ($role){ return $role->getRole();}, $token->getRoles()
Now I change this to
array_map(function ($role){ var_dump($role); return $role->getRole();}, $token->getRoles()
To my surprise, $role is a object Rol but I can't understand why I get the error.
I found the solution the problem is a bug in PHP 5.4 (the php i'm using) serialize method the github user yoannch proposed this solution, is overwrite the serialize/unserialize methods using json_encode/json_decode methods
class User implements \Serializable
//...
/**
* Serializes the content of the current User object
* #return string
*/
public function serialize()
{
return \json_encode(
array($this->username, $this->password, $this->salt,
$this->rol, $this->id));
}
/**
* Unserializes the given string in the current User object
* #param serialized
*/
public function unserialize($serialized)
{
list($this->username, $this->password, $this->salt,
$this->rol, $this->id) = \json_decode(
$serialized);
}
only need change the correct name properties
I had the same problem (Windows, PHP 5.4.5), updated to 5.4.7 and it still did not work. Nevertheless, I came up with a workaround that needs less maintenance (when overwriting the serialization functions as described in the article you mentioned, you'll have to keep them up to date when adding/removing fields). So far it works for me, I hope there are no other problems resulting from the workaround I might have forgotten. Just alter the User's getRoles() function like that:
/**
* #inheritDoc
*/
public function getRoles()
{
$roles = array();
foreach ($this->userRoles as $role) {
$roles[] = $role->getRole();
}
return $roles;
}
Note that $role->getRole() returns the role name as a String (e.g. ROLE_ADMIN).
solution of this problem is very simple.
All problems associated with circular references at yours User and Role objects.
So you have not to serialize User::$roles and Role::$users fields.
Look at Symfony\Component\Security\Core\Authentication\Token\AbstractToken::__construct() and Symfony\Component\Security\Core\Authentication\Token\AbstractToken::serialize().
How you can see, Symfony take your user's roles by invoking UserInterface::getRoles() before serialization. And serialize User and Roles separately.
You have to implement \Serializable interface in User and Role entities.
Example:
/**
* Acme\Bundle\UserBundle\Entity\User
*
* #ORM\Table(name="`user`")
* #ORM\Entity(repositoryClass="Acme\Bundle\UserBundle\Entity\UserRepository")
*/
class User implements AdvancedUserInterface, EquatableInterface, \Serializable
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $username
*
* #ORM\Column(type="string", length=30, unique=true)
*/
private $username;
/**
* #var string $email
*
* #ORM\Column(type="string", length=100, unique=true)
*/
private $email;
/**
* #var string $salt
*
* #ORM\Column(type="string", length=40)
*/
private $salt;
/**
* #var string $password
*
* #ORM\Column(type="string", length=128)
*/
private $password;
/**
* #var boolean $isActive
*
* #ORM\Column(type="boolean")
*/
private $isActive;
/**
* User's roles. (Owning Side)
*
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="Role", inversedBy="users")
*/
private $roles;
// .....
/**
* #see \Serializable::serialize()
*/
public function serialize()
{
/*
* ! Don't serialize $roles field !
*/
return \serialize(array(
$this->id,
$this->username,
$this->email,
$this->salt,
$this->password,
$this->isActive
));
}
/**
* #see \Serializable::unserialize()
*/
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->email,
$this->salt,
$this->password,
$this->isActive
) = \unserialize($serialized);
}
}
/**
* Acme\Bundle\UserBundle\Entity\Role
*
* #ORM\Table(name="role")
* #ORM\Entity
*
*/
class Role implements RoleInterface, \Serializable
{
/**
* #var integer $id
*
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $role
*
* #ORM\Column(name="role", type="string", length=20, unique=true)
*/
private $role;
/**
* Users in group (Inverse Side)
*
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="User", mappedBy="roles")
*/
private $users;
// .....
/**
* #see \Serializable::serialize()
*/
public function serialize()
{
/*
* ! Don't serialize $users field !
*/
return \serialize(array(
$this->id,
$this->role
));
}
/**
* #see \Serializable::unserialize()
*/
public function unserialize($serialized)
{
list(
$this->id,
$this->role
) = \unserialize($serialized);
}
}
And all will be serialized/unserialized correctly.
See discus at https://github.com/symfony/symfony/issues/3691
See also:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/cookbook/entities-in-session.html#serializing-entity-into-the-session

Resources