Doctrine: Object of class User could not be conver

2020-07-11 06:05发布

问题:

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.

回答1:

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.



回答2:

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.



回答3:

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;


回答4:

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);
      }

       //...