Symfony 5 :Relation ManyToMany do not save - php

in my project i create 2 entities :Projects & category.They are a ManyToMany relation MySql shema
im using Easy-admin bundle trying to Manage my database but the problem is when i try to add a category to a project it will not save, it work when i try to add a project to a category but i don't know why it fails in the other way . thanks for help :)
Category
<?php
namespace App\Entity;
use App\Repository\CategoryRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=CategoryRepository::class)
* #ORM\Table(name="category")
*/
class Category
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $title;
/**
* #ORM\Column(type="text")
*/
private $body;
/**
* #ORM\Column(type="string", length=255)
*/
private $image;
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
/**
* #ORM\ManyToMany(targetEntity=Projects::class, inversedBy="category",cascade={"persist"})
*
*/
private $projects;
public function __construct()
{
$this->projects = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getBody(): ?string
{
return $this->body;
}
public function setBody(string $body): self
{
$this->body = $body;
return $this;
}
public function getImage(): ?string
{
return $this->image;
}
public function setImage(string $image): self
{
$this->image = $image;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
/**
* #return Collection|projects[]
*/
public function getProjects(): Collection
{
return $this->projects;
}
public function addProjects(projects $project): self
{
if (!$this->projects->contains($project)) {
$this->projects[] = $project;
$project->addCategory($this);
}
return $this;
}
public function removeProjects(projects $project): self
{
if ($this->projects->contains($project)) {
$this->projects->removeElement($project);
$project->removeCategory($this);
}
return $this;
}
public function __toString()
{
return $this->title;
}
}
projects
<?php
namespace App\Entity;
use App\Repository\ProjectsRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=ProjectsRepository::class)
* #ORM\Table(name="projects")
*/
class Projects
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $title;
/**
*
* #ORM\Column(type="text")
*/
private $body;
/**
* #ORM\Column(type="string", length=255)
*/
private $image;
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
/**
* #ORM\ManyToMany(targetEntity=Category::class, mappedBy="projects",cascade={"persist"})
*/
private $category;
public function __construct()
{
$this->category = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getBody(): ?string
{
return $this->body;
}
public function setBody(string $body): self
{
$this->body = $body;
return $this;
}
public function getImage(): ?string
{
return $this->image;
}
public function setImage(string $image): self
{
$this->image = $image;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
/**
* #return Collection|category
*/
public function getCategory(): Collection
{
return $this->category;
}
public function addCategory(category $category): self
{
if (!$this->category->contains($category)) {
$this->category[] = $category;
$category->addProjects($this);
}
return $this;
}
public function removeCategory(Category $category): self
{
if ($this->category->contains($category)) {
$this->category->removeElement($category);
$category->removeProjects($this);
}
return $this;
}
public function __toString()
{
return $this->title;
}
}

I suspect, that the adders and removers are never called. In the case of Category::getProjects you return the collection, which, when changed, will lead to updates in the database, since it's the owning side of the relation.
On the other side however, you got Projects::getCategory, which also returns the collection, which probably is modified, but the changes are not propagated to the database at all, since it's the inverse side.
To fix this, I presume you have to fix your pluralization:
Category's property should be called $projects, the getter should be called getProjects and imho return $this->projects->toArray(); (the implementation details should be hidden), the adder should be named addProject (note the missing s) and the remover removeProject. Symfony will handle the different pluralizations and understand which methods to call.
the class should be called Project (no plural s) and similarly the getters, adders and removers have their names getCategories, addCategory and removeCategory. again, in the getter return $this->categories->toArray(). providing the collection might lead to the bundle editing the collection directly, especially if the adders and removers are named weirdly.
After these changes (adapting the #ManyToMany annotations too of course), it should work (tm).

it worked the mapping was false it should be like this in
category
#ORM\ManyToMany(targetEntity=Projects::class, mappedBy="categories")
and like this in projects
#ORM\ManyToMany(targetEntity=Category::class, inversedBy="projects")
then i droped the db and update the schema ... plus the fix of the pluralization
thnx a lot <3 <3

Related

DQL Queries not functionning (Doctrine2; Symfony5.0)

I'm having trouble with DQL queries on a project I'm working on. What the DQL is supposed to do is find in the DB all the datas that are related to a productLine variable; that a user would type and search for into a form.
My issue is when I run a test with a product Line; I get an "Error 500" thrown; and I know the error origins from the Repository file; since the controller works fine (I've added some var_dumps in the controller after the query's line to check and I'm not reaching it when testing with the faulty function, whereas with another working function I can reach it).
I know that the data typed in by the user is collected (the product Line entered by the user), there seems to be an issue with DQL processing the query that I can't seem to find an answer for anywhere.
Without further ado; here's the code.
Working fine query
public function findByLotNumber($lotNumber){
return $this->createQueryBuilder('u')
->distinct()
->where('u.lotNumber LIKE :lotNumberS')
->setParameter('lotNumberS', $lotNumber . '%')
->getQuery()
->getResult();
}
faulty query
public function findByCriteriaIdentification($criteriaIdentification){
$givenLotNumber = $_POST['lotNumber'];
if(empty($_POST['lotNumber'])){
$givenLotNumber = '%';
}
$givenProductLine = $_POST['productLine'];
if(empty($_POST['productLine'])){
$givenProductLine = '%';
}
$varTest = $this->createQueryBuilder('u')
->distinct()
->where('u.lotNumber LIKE :lotNumberS')
->orWhere('u.productLine LIKE :productLineS')
->setParameter('lotNumberS', $givenLotNumber)
->setParameter('productLineS', $givenProductLine);
$queryTest = $varTest->getQuery();
return $queryTest->execute();
}
Does anyone have an insight regarding what I'm coding wrong ?
PS: this being a corporate project I don't have the possibility of upgrading to Symfo 6 or Doctrine3 at the moment.
EDIT: Here's the class of the entity i'm querying:
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\WafRepository")
*/
class Waf extends Sil
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
* #ORM\OneToOne(targetEntity="App\Entity\Sil")
*/
private $id;
/**
* #ORM\Column(type="integer")
*/
private $rankId;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $size;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $thickness;
/*public function getId(): ?int
{
return $this->id;
}*/
public function getRankId(): ?int
{
return $this->rankId;
}
public function setRankId(int $rankId): self
{
$this->rankId = $rankId;
return $this;
}
public function getSize(): ?int
{
return $this->size;
}
public function setSize(int $size): self
{
$this->size = $size;
return $this;
}
public function getThickness(): ?int
{
return $this->thickness;
}
public function setThickness(?int $thickness): self
{
$this->thickness = $thickness;
return $this;
}
}
In case its useful, I'm adding as well the class "sili" that waf is an extent from:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\SiliconRepository")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="silType", type="string")
*/
class Sil
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=20)
*/
private $lotNumber;
/**
* #ORM\Column(type="string", length=80, nullable=true)
*/
private $productLine;
/**
* #ORM\Column(type="string", length=20, nullable=true)
*/
private $productCode;
/**
* #ORM\Column(type="string", length=20, nullable=true)
*/
private $productLineCode;
/**
* #ORM\Column(type="string", length=20, nullable=true)
*/
private $sourceLot;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $division;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
//private $comment;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $testStatus;
/**
* #ORM\Column(type="integer", nullable=true)
*/
//private $lastUpdate;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $maturity;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Operations", mappedBy="silicon")
*/
private $operations;
/**
* #ORM\OneToMany(targetEntity="App\Entity\State", mappedBy="silicon")
*/
private $states;
/**
* #ORM\Column(type="string", length=20, nullable=true)
*/
private $fatherLotNumber;
public function __construct()
{
$this->operations = new ArrayCollection();
$this->states = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getLotNumber(): ?string
{
return $this->lotNumber;
}
public function setLotNumber(string $lotNumber): self
{
$this->lotNumber = $lotNumber;
return $this;
}
public function getProductLine(): ?string
{
return $this->productLine;
}
public function setProductLine(?string $productLine): self
{
$this->productLine = $productLine;
return $this;
}
public function getProductCode(): ?string
{
return $this->productCode;
}
public function setProductCode(?string $productCode): self
{
$this->productCode = $productCode;
return $this;
}
public function getProductLineCode(): ?string
{
return $this->productLineCode;
}
public function setProductLineCode(?string $productLineCode): self
{
$this->productLineCode = $productLineCode;
return $this;
}
public function getSourceLot(): ?string
{
return $this->sourceLot;
}
public function setSourceLot(?string $sourceLot): self
{
$this->sourceLot = $sourceLot;
return $this;
}
public function getDivision(): ?int
{
return $this->division;
}
public function setDivision(?int $division): self
{
$this->division = $division;
return $this;
}
/*public function getComment(): ?string
{
return $this->comment;
}
public function setComment(?string $comment): self
{
$this->comment = $comment;
return $this;
}*/
public function getTestStatus(): ?string
{
return $this->testStatus;
}
public function setTestStatus(?string $testStatus): self
{
$this->testStatus = $testStatus;
return $this;
}
/*public function getLastUpdate(): ?int
{
return $this->lastUpdate;
}
public function setLastUpdate(int $lastUpdate): self
{
$this->lastUpdate = $lastUpdate;
return $this;
}*/
public function getMaturity(): ?int
{
return $this->maturity;
}
public function setMaturity(?int $maturity): self
{
$this->maturity = $maturity;
return $this;
}
/**
* #return Collection|Operations[]
*/
public function getOperations(): Collection
{
return $this->operations;
}
public function addOperation(Operations $operation): self
{
while (!($this)->operations->contains($operation)) {
$this->operations[] = $operation;
$operation->setSilicon($this);
}
return $this;
}
public function removeOperation(Operations $operation): self
{
if ($this->operations->contains($operation)) {
$this->operations->removeElement($operation);
//$this->reformatTab($this->operations);
// set the owning side to null (unless already changed)
if ($operation->getSilicon() === $this) {
$operation->setSilicon(null);
}
}
return $this;
}
/**
* #return Collection|State[]
*/
public function getStates(): Collection
{
return $this->states;
}
public function addState(State $state): self
{
if (!$this->states->contains($state)) {
$this->states[] = $state;
$state->setSilicon($this);
}
return $this;
}
public function removeState(State $state): self
{
if ($this->states->contains($state)) {
$this->states->removeElement($state);
// set the owning side to null (unless already changed)
if ($state->getSilicon() === $this) {
$state->setSilicon(null);
}
}
return $this;
}
public function getFatherLotNumber(): ?string
{
return $this->fatherLotNumber;
}
public function setFatherLotNumber(?string $fatherLotNumber): self
{
$this->fatherLotNumber = $fatherLotNumber;
return $this;
}
}
Thanks in advance !

I try to create a custom querybuilder with Symfony

I have a OneToMany relationship on these 2 entities:
Article
ArticleLikes
<?php
namespace App\Entity;
use App\Repository\ArticleRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=ArticleRepository::class)
*/
class Article
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $title;
/**
* #ORM\Column(type="text")
*/
private $content;
/**
* #ORM\Column(type="datetime_immutable")
*/
private $createdAt;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="articles")
* #ORM\JoinColumn(nullable=false)
*/
private $relation;
/**
* #ORM\OneToMany(targetEntity=ArticleLikes::class, mappedBy="article")
*/
private $articleLikes;
public function __construct()
{
$this->articleLikes = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getContent(): ?string
{
return $this->content;
}
public function setContent(string $content): self
{
$this->content = $content;
return $this;
}
public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeImmutable $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getRelation(): ?User
{
return $this->relation;
}
public function setRelation(?User $relation): self
{
$this->relation = $relation;
return $this;
}
/**
* #return Collection|ArticleLikes[]
*/
public function getArticleLikes(): Collection
{
return $this->articleLikes;
}
public function addArticleLike(ArticleLikes $articleLike): self
{
if (!$this->articleLikes->contains($articleLike)) {
$this->articleLikes[] = $articleLike;
$articleLike->setArticle($this);
}
return $this;
}
public function removeArticleLike(ArticleLikes $articleLike): self
{
if ($this->articleLikes->removeElement($articleLike)) {
// set the owning side to null (unless already changed)
if ($articleLike->getArticle() === $this) {
$articleLike->setArticle(null);
}
}
return $this;
}
/**
* #param User $user
* #return bool
*/
public function isLikedByUser(User $user): bool
{
foreach ($this->getArticleLikes() as $like)
{
if ($like->getUser() === $user) {
return true;
}
}
return false;
}
}
<?php
namespace App\Entity;
use App\Repository\ArticleLikesRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=ArticleLikesRepository::class)
*/
class ArticleLikes
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=Article::class, inversedBy="articleLikes")
* #ORM\JoinColumn(nullable=false)
*/
private $article;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="articleLikes")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\Column(type="datetime_immutable")
*/
private $createdAt;
public function getId(): ?int
{
return $this->id;
}
public function getArticle(): ?Article
{
return $this->article;
}
public function setArticle(?Article $article): self
{
$this->article = $article;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeImmutable $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
}
The Article entity can contain a collection of ArticleLikes.
In the ArticleLikes entity, each like is related to an Article (articleId) as well as to a user field (userId).
To retrieve the number of likes for each article, I have a method in the Article entity:
/ **
* #return Collection | ArticleLikes []
* /
public function getArticleLikes (): Collection
{
return $ this-> articleLikes;
}
I am lost because I would like to create a query that allows you to retrieve the last 10 articles the most liked (with the most of ArticleLikes). I don't know if I need to do a left join or something else. How can I do this query?
I want to retrieve the 10 articles that have the most ArticleLikes.
Image:
The solution was to put:
->orderBy('count(al)', 'DESC')
Query Builder that gets the 10 most liked articles:
/**
* #return array
* get the 10 most liked articles
*/
public function findArticlesMostLiked(): array
{
return $this->createQueryBuilder('a')
->select( 'a')
->join('a.articleLikes', 'al')
->groupBy('al.article')
->orderBy('count(al)', 'DESC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}

ManyToMany relationship only lets add from one side Symfony

I'm a totally new Symfony user - I'm trying to make a ManyToMany relationship with User and Entity Group, however i can edit those only from one side(Group atm). Struggling with this for two days.. Would really appreciate some help. Here is my code..
Group Entity:
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=160)
*/
private $name;
/**
* #ORM\ManyToMany(targetEntity=Vartotojas::class, inversedBy="groups")
*/
private $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* #return Collection|Vartotojas[]
*/
public function getUsers(): Collection
{
return $this->users;
}
public function addUser(Vartotojas $user): self
{
if (!$this->users->contains($user)) {
$this->users[] = $user;
$user->addGroup($this);
}
return $this;
}
User Entity:
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=64)
*/
private $name;
/**
* #ORM\ManyToMany(targetEntity=Grupe::class, inversedBy="users")
*/
private $groups;
public function __construct()
{
$this->groups = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* #return Collection|Grupe[]
*/
public function getGroups(): Collection
{
return $this->groups;
}
public function addGroup(Grupe $group): self
{
if (!$this->group->contains($group)) {
$this->groups[] = $group;
$group->addUser($this);
}
return $this;
}
I'm using EasyAdmin to manage those, and my Group/User crud's are the same like this:
namespace App\Controller\Admin;
use App\Entity\Grupe;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
class GrupeCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return Grupe::class;
}
public function configureFields(string $pageName): iterable
{
return [
TextField::new('name'),
AssociationField::new('users'),
];
}
}
if you want to edit a user inside the Group admin, I think what you are looking for is the CollectionField, this works with a Symfony form created by you.

Symfony: $images must be an instance of Doctrine\Common\Collections\ArrayCollection, Doctrine\ORM\PersistentCollection used

I currently have the following error with my entity code, and I don't really understand why I am having this problem.
I only have it in the page of my create dashboard, while I don't call the images but only the heritages, and in the form everything works fine.
I have tried changing Collection to ArrayCollection but nothing has changed.
<?php
namespace App\Entity;
use App\Repository\PatrimoineRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity(repositoryClass=PatrimoineRepository::class)
*/
class Patrimoine
{
/**
* #ORM\Id
* #ORM\Column(type="string", unique=true)
* #ORM\GeneratedValue("UUID")
*/
private ?string $id = null;
/**
* #ORM\Column(type="string", length=255)
*/
private ?string $name;
/**
* #ORM\Column(type="string", length=255)
*/
private ?string $description;
/**
* #ORM\Column(type="json")
* #Assert\Collection(
* fields={
* "lat" = {
* #Assert\NotBlank,
* #Assert\Regex("/^(-?(?:1[0-7]|[1-9])?\d(?:\.\d{1,18})?|180(?:\.0{1,18})?)$/")
* },
* "lng" = {
* #Assert\NotBlank,
* #Assert\Regex("/^(-?[1-8]?\d(?:\.\d{1,18})?|90(?:\.0{1,18})?)$/")
* },
* },
* missingFieldsMessage="Le champs {{ field }} est manquant"
* )
*/
private array $localisation = [];
/**
* #ORM\Column(type="datetime_immutable")
*/
private ?\DateTimeImmutable $created_at;
/**
* #ORM\Column(type="string", length=255)
*/
private ?string $statut;
/**
* #ORM\ManyToOne(targetEntity=CategoryPatrimoine::class, inversedBy="patrimoines")
* #ORM\JoinColumn(nullable=false)
*/
private ?CategoryPatrimoine $category;
/**
* #ORM\Column(type="string", length=255)
*/
private ?string $visibility;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="patrimoines")
* #ORM\JoinColumn(nullable=false)
*/
private ?User $author;
/**
* #ORM\OneToMany(targetEntity=Image::class, mappedBy="patrimoine", orphanRemoval=true)
*/
private ArrayCollection $images;
public function __construct()
{
$this->setCreatedAt(new \DateTimeImmutable());
$this->images = new ArrayCollection();
}
public function getId(): ?string
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
public function getLocalisation(): ?array
{
return $this->localisation;
}
public function setLocalisation(array $localisation): self
{
$this->localisation = $localisation;
return $this;
}
public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->created_at;
}
public function setCreatedAt(\DateTimeImmutable $created_at): self
{
$this->created_at = $created_at;
return $this;
}
public function getStatut(): ?string
{
return $this->statut;
}
public function setStatut(string $statut): self
{
$this->statut = $statut;
return $this;
}
public function getCategory(): ?CategoryPatrimoine
{
return $this->category;
}
public function setCategory(?CategoryPatrimoine $category): self
{
$this->category = $category;
return $this;
}
public function getVisibility(): ?string
{
return $this->visibility;
}
public function setVisibility(string $visibility): self
{
$this->visibility = $visibility;
return $this;
}
public function getAuthor(): ?User
{
return $this->author;
}
public function setAuthor(?User $author): self
{
$this->author = $author;
return $this;
}
/**
* #return Collection|Image[]
*/
public function getImages(): Collection
{
return $this->images;
}
public function addImage(Image $image): self
{
if (!$this->images->contains($image)) {
$this->images[] = $image;
$image->setPatrimoine($this);
}
return $this;
}
public function removeImage(Image $image): self
{
// set the owning side to null (unless already changed)
if ($this->images->removeElement($image) && $image->getPatrimoine() === $this) {
$image->setPatrimoine(null);
}
return $this;
}
}
I faced a similar problem too. You must declare the property as Collection (interface), not as implementation ArrayCollection because Doctrine ORM uses PersistentCollection when picks up data from database but you declare the property as ArrayCollection, and type mismatch happens.
class SomeEntity {
private Collection $items;
// all other code
}
I just realized that the typing of $ images was wrong, it was "ArrayCollection" whereas it must be "Collection"

Ordering Tag entites by related Articles count

I want to fetch tags with their related articles. However the goal is to order them by count of related articles. How to achieve that using QueryBuilder and repositiry classes? I'm new to symfony and I've tried fetching tags by DQL but it was not fetching whole entities.
Tags and Articles are in #ManyToMany relation:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\ArticleRepository")
*/
class Article
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $title;
/**
* #ORM\Column(type="string", length=255)
*/
private $shortDescription;
/**
* #ORM\Column(type="text")
*/
private $content;
/**
* #ORM\Column(type="float")
*/
private $price;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Category", inversedBy="articles")
*/
private $category;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Tag", inversedBy="articles")
*/
private $tags;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Author", inversedBy="articles")
*/
private $authors;
public function __construct()
{
$this->tags = new ArrayCollection();
$this->authors = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getShortDescription(): ?string
{
return $this->shortDescription;
}
public function setShortDescription(string $shortDescription): self
{
$this->shortDescription = $shortDescription;
return $this;
}
public function getContent(): ?string
{
return $this->content;
}
public function setContent(string $content): self
{
$this->content = $content;
return $this;
}
public function getPrice(): ?float
{
return $this->price;
}
public function setPrice(float $price): self
{
$this->price = $price;
return $this;
}
public function getCategory(): ?Category
{
return $this->category;
}
public function setCategory(?Category $category): self
{
$this->category = $category;
return $this;
}
/**
* #return Collection|Tag[]
*/
public function getTags(): Collection
{
return $this->tags;
}
public function addTag(Tag $tag): self
{
if (!$this->tags->contains($tag)) {
$this->tags[] = $tag;
}
return $this;
}
public function removeTag(Tag $tag): self
{
if ($this->tags->contains($tag)) {
$this->tags->removeElement($tag);
}
return $this;
}
/**
* #return Collection|Author[]
*/
public function getAuthors(): Collection
{
return $this->authors;
}
public function addAuthor(Author $author): self
{
if (!$this->authors->contains($author)) {
$this->authors[] = $author;
}
return $this;
}
public function removeAuthor(Author $author): self
{
if ($this->authors->contains($author)) {
$this->authors->removeElement($author);
}
return $this;
}
}
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\TagRepository")
*/
class Tag
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Article", mappedBy="tags")
*/
private $articles;
public function __construct()
{
$this->articles = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* #return Collection|Article[]
*/
public function getArticles(): Collection
{
return $this->articles;
}
public function addArticle(Article $article): self
{
if (!$this->articles->contains($article)) {
$this->articles[] = $article;
$article->addTag($this);
}
return $this;
}
public function removeArticle(Article $article): self
{
if ($this->articles->contains($article)) {
$this->articles->removeElement($article);
$article->removeTag($this);
}
return $this;
}
}
My last method I've used in TagRepository:
public function findTagsByArticlesCount()
{
return $this->createQueryBuilder('tag')
->leftJoin('tag.articles', 'article')
->orderBy('count(article.id)', 'DESC')
->getQuery()
->execute();
}
I vaguely recall a similar case I have 2 years ago, which I was able to resolve by having HIDDEN column:
public function findTagsByArticlesCount()
{
return $this->createQueryBuilder('tag')
->leftJoin('tag.articles', 'article')
->addSelect('COUNT(article.id) as HIDDEN cnt')
->orderBy('cnt', 'DESC')
->getQuery()
->execute();
}
However, I am currently not at my workstation to test this. Can you try it?
Hope it helps...
There is missing groupBy while counting tags in articles:
$this->createQueryBuilder('tag')
->leftJoin('tag.articles', 'article')
->addSelect('COUNT(tag.id) AS tagcount')
->groupBy('tag.id')
->orderBy('tagcount', 'DESC')
->getQuery()
->execute();
Note: while joining Articles you will receive number of tags * number of articles records that you need to group by tag to count number of tags in related articles.
Note 2: I would prefer to use innerJoin instead of leftJoin not choose tags without related articles.

Categories