0

so I'm working on a symfony project in which I'm using class inheritence: I have three main entities: ActeursLocaux-> parent class, Entreprise-> child class and Association-> child too.

I have the following problem: when I create an Entreprise, I only ask for a name and categories. Then, the user is redirected to a "show" page in which he can choose to update the entity to add things like: number of employees, website etc.. But my edit route returns the following error: Uncaught PHP Exception Doctrine\DBAL\Exception\InvalidFieldNameException: "An exception occurred while executing a query: SQLSTATE[42S22]: Column not found: 1054 Unknown column 't0.id' in 'where clause'" I tried a lot of things (usual symfony form, ajax) and can't find a solution

Here is the code for the first two entities:

#ACTEURSLOCAUX

<?php

namespace App\Entity;

use App\Repository\ActeursLocauxRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;

#[ORM\Entity(repositoryClass: ActeursLocauxRepository::class)]
#[ORM\InheritenceType("JOINED")]
#[ORM\DiscriminatorColumn(name:'type', type:'string')]
#[ORM\DiscriminatorMap([
    'association' => Association::class,
    'entreprise' => Entreprise::class
])]

abstract class ActeursLocaux
{
    #[ORM\Id]
    #[ORM\GeneratedValue(strategy:"AUTO")]
    #[ORM\Column (name:'id', type:'integer')]
    protected ?int $id = null;

    #[ORM\Column(name:'raison_sociale', type:'string', length: 255)]
    protected ?string $raison_sociale = null;

    #[ORM\Column(length: 255)]
    #[Gedmo\Slug(fields: ['raison_sociale'])]
    protected ?string $slug = null;

    #[ORM\Column(length: 255, nullable: true)]
    protected ?string $site_internet = null;

    #[ORM\Column(length: 255, nullable: true)]
    protected ?string $coordonnees_mail = null;

    #[ORM\Column(length: 255, nullable: true)]
    protected ?string $coordonnees_numero = null;

    #[Gedmo\Timestampable(on: 'create')]
    #[ORM\Column(nullable: true)]
    protected ?\DateTimeImmutable $created_at = null;

    #[Gedmo\Timestampable(on: 'update')]
    #[ORM\Column(type: 'datetime_immutable', nullable: true)]
    protected ?\DateTimeImmutable $updated_at = null;

    #[ORM\OneToMany(mappedBy: 'acteursLocaux', targetEntity: ActeursLienExterne::class, orphanRemoval: true)]
    protected Collection $lien_externe;

    #[ORM\OneToMany(mappedBy: 'acteursLocaux', targetEntity: ActeursLocauxAdresse::class)]
    protected Collection $adresse;

    #[ORM\OneToMany(mappedBy: 'acteursLocaux', targetEntity: Horaires::class)]
    protected Collection $horaires_ouverture;

    #[ORM\OneToMany(mappedBy: 'target', targetEntity: Association::class)]
    protected Collection $associations;

    #[ORM\OneToMany(mappedBy: 'target', targetEntity: Entreprise::class)]
    protected Collection $entreprises;

    #[ORM\Column(length: 255, nullable: true)]
    protected ?string $logo = null;


    public function __construct()
    {
        $this->lien_externe = new ArrayCollection();
        $this->adresse = new ArrayCollection();
        $this->horaires_ouverture = new ArrayCollection();
        $this->associations = new ArrayCollection();
        $this->entreprises = new ArrayCollection();
    }

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

    public function getRaisonSociale(): ?string
    {
        return $this->raison_sociale;
    }

    public function setRaisonSociale(string $raison_sociale): self
    {
        $this->raison_sociale = $raison_sociale;

        return $this;
    }

    public function getSiteInternet(): ?string
    {
        return $this->site_internet;
    }

    public function setSiteInternet(?string $site_internet): self
    {
        $this->site_internet = $site_internet;

        return $this;
    }

    
  [...]

    /**
     * @return Collection<int, Association>
     */
    public function getAssociations(): Collection
    {
        return $this->associations;
    }

    public function addAssociation(Association $association): self
    {
        if (!$this->associations->contains($association)) {
            $this->associations->add($association);
            $association->setTarget($this);
        }

        return $this;
    }

    public function removeAssociation(Association $association): self
    {
        if ($this->associations->removeElement($association)) {
            // set the owning side to null (unless already changed)
            if ($association->getTarget() === $this) {
                $association->setTarget(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection<int, Entreprise>
     */
    public function getEntreprises(): Collection
    {
        return $this->entreprises;
    }

    public function addEntreprise(Entreprise $entreprise): self
    {
        if (!$this->entreprises->contains($entreprise)) {
            $this->entreprises->add($entreprise);
            $entreprise->setTarget($this);
        }

        return $this;
    }

    public function removeEntreprise(Entreprise $entreprise): self
    {
        if ($this->entreprises->removeElement($entreprise)) {
            // set the owning side to null (unless already changed)
            if ($entreprise->getTarget() === $this) {
                $entreprise->setTarget(null);
            }
        }

        return $this;
    }
    
}

#ENTREPRISE

<?php

namespace App\Entity;

use App\Repository\EntrepriseRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: EntrepriseRepository::class)]
class Entreprise extends ActeursLocaux
{
    #[ORM\Column(length: 255, nullable: true)]
    private?string $code_naf = null;

    #[ORM\Column(nullable: true)]
    private?int $nb_salaries = null;

    #[ORM\Column(length: 255, nullable: true)]
    private?string $code_ape = null;

    #[ORM\Column(length: 255, nullable: true)]
    private?string $code_siret = null;

    #[ORM\Column(type: Types::TEXT, nullable: true)]
    private?string $presentation_entreprise = null;

    #[ORM\ManyToOne(targetEntity: ActeursLocaux::class, inversedBy: 'entreprises')]
    private $target;

    #[ORM\Column (type:'string',length: 255, nullable: true)]
    private ?string $reference = null;

    #[ORM\ManyToMany(targetEntity: CategoriesActivite::class, inversedBy: 'entreprises')]
    private Collection $categories_activite;

    public function __construct()
    {
        parent::__construct();
        $this->categories_activite = new ArrayCollection();
    }

    public function __toString()
    {
        return $this->raison_sociale;
    }

    public function getCodeNaf(): ?string
    {
        return $this->code_naf;
    }
    
   [...]
    public function getTarget(): ?ActeursLocaux
    {
        return $this->target;
    }

    public function setTarget(?ActeursLocaux $target): self
    {
        $this->target = $target;

        return $this;
    } 

    
}
#EntrepriseController:
<?php

namespace App\Controller\Backoffice;

use App\Entity\Entreprise;
use App\Repository\EntrepriseRepository;
use App\Form\CreateEntrepriseType;
use App\Form\Entreprise\ModifierInformationsEntrepriseType;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;


use Doctrine\ORM\EntityManagerInterface;

#[Route('/backoffice', name: 'app_backoffice_')]
class EntrepriseController extends AbstractController
{
    // CREATE - n'est plus utilisée, voir ActeursLocauxController
    #[Route('/entreprise-create', name: 'entreprise_create', methods:['GET', 'POST'])]
    public function create(Request $request, EntityManagerInterface $entityManager): Response
    {
    }

    // SHOW
    #[Route('/entreprise/{id}', name:'entreprise_show')]
    public function show($id, EntrepriseRepository $entrepriseRepo): Response
    {
        $toutesLesEntreprises = $entrepriseRepo->findAll();
        $entrepriseCherchee = $entrepriseRepo->find($id);
        return $this->render('backoffice/entreprise/show.html.twig',
        [
            'entrepriseCherchee' => $entrepriseCherchee
        ]);
    }

    //EDIT (the problem is here -- might be errors because I tried many different solutions)

    #[Route('/entreprise/{id}/edit', name:'entreprise_edit', methods: ['GET', 'POST'])]
    public function modifierEntreprise($id, Request $request, Entreprise $entreprise,  EntityManagerInterface $entityManager): Response
    {
        $form = $this->createForm(ModifierInformationsEntrepriseType::class, $entreprise);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid())
        {
            $entreprise = $form->getData();
            $entityManager->persist($entreprise);
            $entityManager->flush();


            $this->addFlash('success', 'L\'Entreprise a été modifiée avec succès');
            return $this->redirectToRoute('app_backoffice_entreprises_show' , [
                'id' => $entreprise->getId(),
            ]);
        }

        return $this->render('backoffice/entreprise/edit_informations.html.twig',
        [
            'form_edit' => $form->createView(),
        ]);
    }
}

#Twig TEMPLATES --- #show.html.twig: (don't look at the div indent, I deleted things to make the code readable so there might be mistakes)

{% extends 'backoffice/backoffice_base.html.twig' %}

{% block body %}
<div class="container">
    {# PREMIERE LIGNE #}
    <div class="row">
        {# PARTIE DE GAUCHE #}
        <div class="col-lg-8">
           
            {# INFORMATIONS DE L'ASSO #}
            <div class="card">
                <div class="card-header" id="headingOne">
                <h5 class="mb-0">
                    <button class="btn btn-link" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
                        Informations
                    </button>

//LINK IS HERE
                    <a href="{{ path('app_backoffice_entreprise_edit', {'id': entrepriseCherchee.getId()})}}" class="btn btn-sm btn-warning float-right"><i class="fas fa-edit"></i></a>
                </h5>
                </div>
            </div>
            </div>
        </div
    </div>
    {# FIN PREMIERE LIGNE #}
</div>
{% endblock %}

#edit_informations.html.twig:

{% extends 'backoffice/backoffice_base.html.twig' %}

{% block body %}

 <!-- Main content -->
<section class="content">
    <div class="container">

        {{ form_start(form_edit) }}
            {{ form_widget(form_edit) }}
            <button type="submit" class="btn btn-primary">Enregistrer</button>
            {{ form_end(form_edit) }}
        
    </div>
    
</section>            
{% endblock %}

I tried the usual form create with symfony and ajax but the problem seems to be that doctrine can't get the selected entity. You'll notice that for the "/show" page in my EntrepriseController, I had to do a find all and then select the searched entreprise in order for it to work:

// SHOW
    #[Route('/entreprise/{id}', name:'entreprise_show')]
    public function show($id, EntrepriseRepository $entrepriseRepo): Response
    {
        $toutesLesEntreprises = $entrepriseRepo->findAll();
        $entrepriseCherchee = $entrepriseRepo->find($id);
        return $this->render('backoffice/entreprise/show.html.twig',
        [
            'entrepriseCherchee' => $entrepriseCherchee
        ]);
    }

Thanks for your help!

Edit: 1- this is the error displayed as I click on the edit button: error displayed

Will B.
  • 17,883
  • 4
  • 67
  • 69
melione
  • 1
  • 2
  • What is the query that is being executed as displayed in the logs or the doctrine queries tab from the debugger? What is the table structure of the referenced table? `SHOW CREATE table_name`. Shouldn't `#[ORM\ManyToOne(targetEntity: ActeursLocaux::class, inversedBy: 'entreprises')]` be self-referencing as `#[ORM\ManyToOne(targetEntity: Entreprise::class, inversedBy: 'entreprises')]` to prevent an invalid (partially-loaded) entity state due to the abstraction and to match the mapping declaration of the inverse side? – Will B. May 15 '23 at 15:02
  • Please trim your code to make it easier to find your problem. Follow these guidelines to create a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – Community May 16 '23 at 04:31
  • @WillB. I added the error at the end of my post. For the mapping part, I followed this documentation at the Class Table Inheritence part: https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/inheritance-mapping.html Along with this tutorial that follows the documentation: https://www.youtube.com/watch?v=hNAMEWYCkAM So maybe I did something wrong but I'm not sure – melione May 16 '23 at 09:00
  • Based on the query shown, something in the mappings is wrong, since the query is missing the `t0` table association in the `FROM` or the nonexistent `JOIN` clauses of the query. Normally if `t1` is referenced in the query, a `JOIN` is being used. Does the error persist if you return an empty response that doesn't try to interact with the entity in Twig or the Form? – Will B. May 16 '23 at 18:24
  • Am also a bit confused why `$entrepriseRepo->findAll();` for `$entrepriseRepo->find($id); ` not working as intended would be considered an appropriate resolution, rather than indicate something is broken with the Entity mapping. – Will B. May 16 '23 at 21:46

0 Answers0