I keep getting this error with Doctrine:
PHP Catchable fatal error: Object of class User could not be converted to string in vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php on line 1337
In my system users can have many permissions in a One to Many relationship. I have set up a User
and Permission
entity. They look like this (I removed some annotations, getters and setters to reduce clutter):
class User {
/**
* @ORM\Column(name="user_id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
public function getId()
{
return $this->id;
}
/**
* @ORM\OneToMany(targetEntity="Permission", mappedBy="user", cascade={"persist"})
*/
protected $permissions;
public function getPermissions()
{
return $this->permissions;
}
}
class Permission {
/**
* @ORM\Column(name="user_id", type="integer")
* @ORM\ManyToOne(targetEntity="User", inversedBy="permissions")
*/
protected $user;
public function getUser()
{
return $this->user;
}
public function setUser( $user )
{
$this->user = $user;
return $this;
}
}
The problem occurs when I add a new Permission
to a User
:
$permission = new Permission();
$user->getPermissions()->add( $permission );
$em->persist( $user );
$em->flush();
This is the last bit of my stack trace:
PHP 11. Doctrine\ORM\UnitOfWork->persist() vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:565
PHP 12. Doctrine\ORM\UnitOfWork->doPersist() vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1555
PHP 13. Doctrine\ORM\UnitOfWork->cascadePersist() vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1615
PHP 14. Doctrine\ORM\UnitOfWork->doPersist() vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:2169
PHP 15. Doctrine\ORM\UnitOfWork->persistNew() vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1597
PHP 16. Doctrine\ORM\UnitOfWork->scheduleForInsert() doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:836
PHP 17. Doctrine\ORM\UnitOfWork->addToIdentityMap() vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1157
PHP 18. implode() vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1337
Any insight would be greatly appreciated.
Your solution gave me a clue of what is happening.
Even though you have the entities and the anotations, Doctrine is not being able to understand the relation between entities. When doctrine understands the relation between entities, it knows what methods to call (ie User::getId()) but otherwise, it tries to transform whatever you are sending to a scalar value that it can use to query the database. Thats why it is calling the __toString function of the User, and thats why if you return the id in toString, everything works from here.
This is ok, but its a patch, and probably you dont want to keep it if we can find a better solution, since it could be harder to maintain as your application grows.
What i can see, is that in Permissions you have:
/**
* @ORM\Column(name="user_id", type="integer")
* @ORM\ManyToOne(targetEntity="User", inversedBy="permissions")
*/
protected $user;
You should remove the @ORM\Column(type="integer"
)
About the join columns, it is not mandatory, but you have to be sure that the defauts, are what you want. As we can read here
Before we introduce all the association mappings in detail, you should
note that the @JoinColumn and @JoinTable definitions are usually
optional and have sensible default values. The defaults for a join
column in a one-to-one/many-to-one association is as follows:
name: "<fieldname>_id"
referencedColumnName: "id"
so, they will be the same as an explicit:
/**
* @ORM\ManyToOne(targetEntity="User", inversedBy="permissions", cascade={"persist"})
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="user_id", referencedColumnName="id")
* })
*/
protected $user;
So it is supposed to look for a column user_id
in the Permissions
table, and join it with the id
column of the User
table. We suppose that this is ok.
If this is true, then in your User, the id shouldnt be user_id, but id:
/**
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
Or if the column name is actually user_id, then the User class is ok, but you have to change the join column to @ORM\JoinColumn(name="user_id", referencedColumnName="user_id")
That much i can say. I cannot try it know, but i will be glad if you can give it a second.
OK. I've got it working.
I haven't fully worked out the reason yet but when I add the following to my User
entity it works:
class User {
public function __toString()
{
return strval( $this->getId() );
}
}
If I find out more I will post here.
I think there's a problem with the mapping of user property in permission entity. Try this one:
/**
* @ORM\ManyToOne(targetEntity="User", inversedBy="permissions")
* @JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
are you initializing the collection in your OneToMany side?
and also, the methods to add and remove from the collection?
class User {
/**
* @ORM\OneToMany(targetEntity="Permission", mappedBy="user", cascade={"persist"})
*/
protected $permissions;
public function getPermissions()
{
return $this->permissions;
}
public function __construct()
{
$this->permissions = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addPermissions (Permission $permissions)
{
$this->permissions[] = $permissions;
return $this;
}
public function removePermissions(Permission $permissions)
{
$this->permissions->removeElement($permissions);
}
//...