Cómo resolver el error de Symfony 5: Object of class Proxies\__CG__\App\Entity could not be converted to string

Cómo resolver el error de Symfony 5: Object of class Proxies\__CG__\App\Entity could not be converted to string

Symfony es increíble, eso es seguro. Incluye una gran cantidad de herramientas que puedes usar para mejorar los tiempos de desarrollo de tu proyecto, automatizando cosas muy importantes como la generación de formularios a partir de tablas que ya existen, por lo que no necesitas escribirlas como entidades de doctrina desde cero. El uso de las herramientas mencionadas puede conducir, por supuesto, a que aparezcan algunas excepciones en su proyecto si no comprende completamente lo que hicieron los comandos de Symfony.

En el caso de "Objeto de clase Proxies\__CG__\App\Entity no se pudo convertir a cadena", es muy probable que generaste las entidades de doctrina desde una base de datos automáticamente, y por lo tanto, las entidades no tienen el método mágico __toString. En este artículo, te explicaré cómo evitar fácilmente que aparezca esta excepción.

Contexto

Para replicar este error al igual que el uso que está teniendo en este momento, usaremos un ejemplo muy básico. Tenemos 2 tablas con una relación ManyToOne:

Many To One Relationship SQL

Cuando el usuario registra una nueva persona en la base de datos, es necesario proporcionar el estado en el que vive el usuario, bastante simple. Esta información, convertida en entidades de doctrina, se ve respectivamente así, para la entidad Person.php:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Person
 *
 * @ORM\Table(name="person")
 * @ORM\Entity
 */
class Person
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="bigint", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string|null
     *
     * @ORM\Column(name="first_name", type="string", length=255, nullable=true, options={"default"="NULL"})
     */
    private $firstName;

    /**
     * @var string|null
     *
     * @ORM\Column(name="last_name", type="string", length=255, nullable=true, options={"default"="NULL"})
     */
    private $lastName;

    /**
     * @var \States
     *
     * @ORM\ManyToOne(targetEntity="States")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="states_id", referencedColumnName="id")
     * })
     */
    private $state;

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

    public function getFirstName(): ?string
    {
        return $this->firstName;
    }

    public function setLastName(?string $firstName): self
    {
        $this->firstName = $firstName;

        return $this;
    }

    public function getLastName(): ?string
    {
        return $this->lastName;
    }

    public function setLastName(?string $lastName): self
    {
        $this->lastName = $lastName;

        return $this;
    }

    public function getState(): ?State
    {
        return $this->state;
    }

    public function setState(?State $state): self
    {
        $this->state = $state;

        return $this;
    }
}

Y la entidad State.php:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * State
 *
 * @ORM\Table(name="state")
 * @ORM\Entity
 */
class State
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="bigint", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255, nullable=false)
     */
    private $name;

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

    public function getName(): ?string
    {
        return $this->nombre;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }
}

En nuestro caso, se crearon automáticamente usando doctrine:mapping:import (usando ingeniería inversa). Luego, creamos el CRUD a través de generate:doctrine:crud. Esto generaría las vistas donde el usuario debería poder ver todos los elementos en la base de datos y el formulario para crear y editar las entidades Persona:

<?php

namespace App\Controller;

use App\Entity\Person;
use App\Form\PersonType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

/**
 * @Route("/person")
 */
class PersonController extends AbstractController
{
    /**
     * @Route("/", name="person_index", methods={"GET"})
     */
    public function index(): Response
    {
        // .. //
    }

    /**
     * @Route("/new", name="person_new", methods={"GET","POST"})
     */
    public function new(Request $request): Response
    {
        $persona = new person();
        $form = $this->createForm(PersonType::class, $persona);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $entityManager = $this->getDoctrine()->getManager();
            $entityManager->persist($persona);
            $entityManager->flush();

            return $this->redirectToRoute('person_index');
        }

        return $this->render('person/new.html.twig', [
            'persona' => $persona,
            'form' => $form->createView(),
        ]);
    }

    /**
     * @Route("/{id}", name="person_show", methods={"GET"})
     */
    public function show(person $persona): Response
    {
        // .. //
    }

    /**
     * @Route("/{id}/edit", name="person_edit", methods={"GET","POST"})
     */
    public function edit(Request $request, person $persona): Response
    {
        // .. //
    }

    /**
     * @Route("/{id}", name="person_delete", methods={"DELETE"})
     */
    public function delete(Request $request, person $persona): Response
    {
        // .. //
    }
}

Ahora, si el usuario visita la ruta editada o nueva, aparecerá la excepción.

Causa y solucion

La única razón por la que aparece este error es porque no hay un método mágico __toString en la entidad State:

<?php

class MyEntity
{
    public function __toString() {
        return $this->somePropertyOrPlainString;
    }
}

Aunque estemos en forma de Persona. Básicamente, porque cuando se procesa el formulario, Symfony no sabe qué debería mostrarse en el campo de selección que causa el error, por lo que en este caso, como el select (state_id) debería permitir al usuario seleccionar uno de los estados registrados en el base de datos, cuando las entidades en la lista se imprimen como una cadena, debemos devolver la propiedad de nombre de la entidad que devuelve su valor en el método mágico:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * State
 *
 * @ORM\Table(name="state")
 * @ORM\Entity
 */
class State
{
    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255, nullable=false)
     */
    private $nombre;

    // Registra el método mágico para imprimir el nombre del estado, por ejemplo, California
    public function __toString() {
        return $this->name;
    }
}

Permitiendo que el formulario representa una lista de los estados sin ninguna excepción:

Symfony 5 Cast Entity as String

Que te diviertas ❤️!

Esto podria interesarte

Conviertete en un programador más sociable