How can I set the protected object user? After filling the form i have to add user object with current user data (for example like saving comments). I tried something like that:
if ($form->isValid()) {
$comment = $form->getData();
$comment->user = $this->contextSecurity->getToken()->getUser();
$this->model->save($comment);
}
And i've got this error
FatalErrorException: Error: Cannot access protected property AppBundle\Entity\Comment::$user in /home/AppBundle/Controller/CommentsController.php line 184
Here is my Comment entity:
class Comment
{
/**
* Id.
*
* #ORM\Id
* #ORM\Column(
* type="integer",
* nullable=false,
* options={
* "unsigned" = true
* }
* )
* #ORM\GeneratedValue(strategy="IDENTITY")
*
* #var integer $id
*/
private $id;
/**
* Content.
*
* #ORM\Column(
* name="content",
* type="string",
* length=250,
* nullable=false
* )
* #Assert\NotBlank(groups={"c-default"})
* #Assert\Length(min=3, max=250, groups={"c-default"})
*
* #var string $content
*/
private $content;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="comments")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
*/
protected $user;
I'm using Symfony2.3. Any help will be appreciated.
You can't modify protected properties from outside of the object. You need a public property or a setter for that.
class Comment
{
// ...
public function setUser(User $user)
{
$this->user = $user;
}
}
And in a controller you can write:
$comment->setUser($this->getUser());
This question is not related to Symfony2, at first you should read about php types, especially about objects. read here and then here
You should understand how Visibility works. After that you will understand that access to protected/private properties of the object is only available from the object itself, so you need to create public method
setUser($user) {
$this->user = $user;
}
I always use protected, If i want edit variable or take the value, I use the getter and setter:
public function setUser($user) {
$this->user = $user;
}
public function getUser(){
return $this->user;
}
Related
Im learning doctrine2. Problem is: I have just updated my entity class. Old version of entity consisted of $id, $name and $username fields. After this update below, I run command doctrine:generate:entities Acme, doctrine:update:schema and etc., but result is still old table with only 3 fields. It looks like old meta-data is saved somewhere. Can someone provide me with information what Im doing wrong ? And why I get old database table instead of new one ? And even how to solve my problem ?
namespace Acme\DemoBundle\Entity;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="user")
* #ORM\Entity
*/
class User implements UserInterface, EquatableInterface
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=50)
*/
private $username;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=50)
*/
private $password;
/**
* #var string
*
* #ORM\Column(name="roles", type="string", length=50)
*/
private $roles;
/**
* #var array
*
* #ORM\Column(name="apikey", type="array")
*/
private $apiKey;
/**
* #var string
*
* #ORM\Column(name="salt", type="string", length=10)
*/
private $salt;
function __construct($apiKey, $id, $password ,$roles , $salt, $username)
{
$this->apiKey = $apiKey;
$this->id = $id;
$this->password = $password;
$this->roles = $roles;
$this->salt = $salt;
$this->username = $username;
}
/**
* The equality comparison should neither be done by referential equality
* nor by comparing identities (i.e. getId() === getId()).
*
* However, you do not need to compare every attribute, but only those that
* are relevant for assessing whether re-authentication is required.
*
* Also implementation should consider that $user instance may implement
* the extended user interface `AdvancedUserInterface`.
*
* #param UserInterface $user
*
* #return bool
*/
public function isEqualTo(UserInterface $user)
{
if (!$user instanceof User) {
return false;
}
if ($this->password !== $user->getPassword()) {
return false;
}
if ($this->salt !== $user->getSalt()) {
return false;
}
if ($this->username !== $user->getUsername()) {
return false;
}
return true;
}
/**
* Returns the roles granted to the user.
*
* <code>
* public function getRoles()
* {
* return array('ROLE_USER');
* }
* </code>
*
* Alternatively, the roles might be stored on a ``roles`` property,
* and populated in any number of different ways when the user object
* is created.
*
* #return Role[] The user roles
*/
public function getRoles()
{
return $this->roles;
}
/**
* Returns the password used to authenticate the user.
*
* This should be the encoded password. On authentication, a plain-text
* password will be salted, encoded, and then compared to this value.
*
* #return string The password
*/
public function getPassword()
{
return $this->password;
}
/**
* Returns the salt that was originally used to encode the password.
*
* This can return null if the password was not encoded using a salt.
*
* #return string|null The salt
*/
public function getSalt()
{
return $this->salt;
}
/**
* Returns the username used to authenticate the user.
*
* #return string The username
*/
public function getUsername()
{
return $this->username;
}
/**
* #return string
*/
public function getApiKey()
{
return $this->apiKey;
}
/**
* #param string $apiKey
*/
public function setApiKey($apiKey)
{
$this->apiKey = $apiKey;
}
/**
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #param int $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* Removes sensitive data from the user.
*
* This is important if, at any given point, sensitive information like
* the plain-text password is stored on this object.
*/
public function eraseCredentials()
{
// TODO: Implement eraseCredentials() method.
}
}
If the old metadata is saved You have to clear cache
doctrine:cache:clear-metadata Clears all metadata cache for an entity manager
doctrine:cache:clear-query Clears all query cache for an entity manager
doctrine:cache:clear-result Clears result cache for an entity manager
I think you did not executed the update command correctly.
You'll need to force the changes:
php app/console doctrine:schema:update --force
Or dump the SQL and execute it manually:
php app/console doctrine:schema:update --dump-sql
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.
I would like to save my users search on my website. Thaht's why i have a Class User and i would like to create Search Class.
I have done that :
class Search
{
public function __construct()
{
$this->searched_date = new \Datetime();
}
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
private $id;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="test\UserBundle\Entity\User")
*/
private $user;
/**
* #ORM\Column(name="termsearch", type="string", length=255, nullable="true")
*/
private $termsearch;
/**
* #ORM\Column(name="goodtitle", type="string", length=255, nullable="true")
*/
private $goodtitle;
/**
* #ORM\Column(name="searched_date", type="datetime")
*/
private $searched_date;
/**
* Set termsearch
*
* #param text $termsearch
*/
public function setTermsearch($termsearch)
{
$this->termsearch = $termsearch;
}
/**
* Get termsearch
*
* #return text
*/
public function getTermsearch()
{
return $this->termsearch;
}
/**
* Set searched_date
*
* #param datetime $searchedDate
*/
public function setSearchedDate($searchedDate)
{
$this->searched_date = $searchedDate;
}
/**
* Get searched_date
*
* #return datetime
*/
public function getSearchedDate()
{
return $this->searched_date;
}
/**
* Set user
*
* #param test\UserBundle\Entity\User $user
*/
public function setUser(\test\UserBundle\Entity\User $user)
{
$this->user = $user;
}
/**
* Get user
*
* #return test\UserBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
/**
* Set goodtitle
*
* #param text $goodtitle
*/
public function setGoodtitle($goodtitle)
{
$this->goodtitle = $goodtitle;
}
/**
* Get goodtitle
*
* #return text
*/
public function getGoodtitle()
{
return $this->goodtitle;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
And i would like to insert like that :
$em = $this->getDoctrine()->getEntityManager();
$user = $em->getRepository('TestUserBundle:User')->find($currentuser->getID());
$search = new Search();
$search->setUser($user);
$search->setTermsearch($termsearch);
$search->setGoodtitle($goodtitle);
$em->persist($search);
$em->flush();
Unfortunately i have this error :
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens (500 Internal Server Error)
And in the stack we can found that :
INSERT INTO s_search (user_id, termsearch, goodtitle, searched_date) VALUES (?, ?, ?, ?) ({"1":"c2c","2":"C2C Down The Road","3":{"date":"2012-10-31 00:18:47","timezone_type":3,"timezone":"Europe\/Paris"}})
I don't know how i can create this class Search...
Thank for for you help !
Remove #ORM\Id from the $user as #ManyToOne mapping reference does not require a type. See Doctrine's Annotation Reference for details. Doctrine takes care of the correct column type to hold the reference to another entity.
Make also sure that your User query really returns a valid $user. If it's possible that Search does not have $user, use #JoinColumn annotation to claim the column nullable. See another SO question Doctrine 2 can't use nullable=false in manyToOne relation?
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
I have a problem while json_encodeing a Entity.
public function jsonvoteAction($id) {
$em = $this->getDoctrine()->getEntityManager();
$entity = $em->getRepository('KorumAGBundle:AGVote')->findOneById($id);
$response = new Response(json_encode($entity, 200));
$response->headers->set('Content-Type',' application/json');
return $response;
}
This code returns me a the users entity
{"users":{"__isInitialized__":false,"id":null,"nickname":null,"pwd":null,"email":null,"firstname":null,"lastname":null,"poste":null,"addr1":null,"addr2":null,"pc":null,"country":null,"phone":null,"province":null,"acess":null,"site":null,"crew":null,"utilisateur":null}}
And when I var dymp my $entity, it returns both my AGVote and USers entity.
Here is my AGVote Entity
<?php
namespace Korum\AGBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Korum\AGBundle\Entity\AGVote
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class AGVote
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*
*/
private $id;
/**
* #ORM\Column(type="text")
*/
private $question;
/**
* #ORM\Column(type="smallint")
*/
private $actif;
/**
* #ORM\ManyToOne(targetEntity="\Korum\KBundle\Entity\Users", cascade={"all"})
*/
public $users;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set question
* Nb : Only AG admin can set a question
* #param text $question
*/
public function setQuestion($question)
{
$this->question = $question;
}
/**
* Get question
*
* #return text
*/
public function getquestion()
{
return $this->question;
}
/**
* Set actif
*
* #param smallint $actif
*/
public function setActif($actif)
{
$this->actif = $actif;
}
/**
* Get actif
*
* #return smallint
*/
public function getActif()
{
return $this->actif;
}
/**
* Set Users
*
* #param Korum\KBundle\Entity\Province $Users
*/
public function setUsers(\Korum\KBundle\Entity\Users $users)
{
$this->users = $users;
}
/**
* Get Users
*
* #return Korum\KBundle\Entity\Users
*/
public function getUsers()
{
return $this->users;
}
}
Does anyone have an idea of what happened ?
I tried to install the JSMSerializerBundle but event with Metadata library at version 1.1.
When I want to clear my cache, it failed with error :
See :
JMSSerializerBundle Installation : Catchable Fatal Error: Argument 1 passed to JMSSerializerBundle\Twig\SerializerExtension::__construct()
By default, json_encode only uses public properties.
So it serialized the only public property of AGVote: $users. The content of $users was an instance of User; which public fields were serialized.
You could work around these by adding a toArray() method to your entities, and then doing json_encode($entity->toArray()), but i highly recommend you to have a look and use the JMSSerializedBundle.