I'm trying to get the "demands" of a user.
User can have some demands and a demand have only one user (OneToMany)
This is my User entity (Utilisateur in french) :
class Utilisateur extends AbstractEntity implements UserInterface, PasswordAuthenticatedUserInterface
{
/**
* #ORM\Id
* #ORM\Column(type="ulid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class=UlidGenerator::class)
*/
private Ulid $id;
/**
* #ORM\OneToMany(targetEntity=DemandeTransport::class, mappedBy="utilisateur", orphanRemoval=true)
*/
private Collection $demandeTransports;
And my demands entity :
class DemandeTransport extends AbstractEntity
{
/**
* #ORM\Id
* #ORM\Column(type="ulid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class=UlidGenerator::class)
*/
private Ulid $id;
/**
* #ORM\ManyToOne(targetEntity=Utilisateur::class, inversedBy="demandeTransports")
* #ORM\JoinColumn(nullable=false)
*/
private Utilisateur $utilisateur;
My controller receiving the request :
/**
* #throws Exception
*/
#[Route('/liste_propositions_transporteur', name: 'liste_propositions_transporteur', methods: ['GET'])]
public function listePropositionsTransporteur(Request $request): Response
{
return match ($request->getMethod()) {
'GET' => new Response(json_encode(['success' => true, 'data' => $this->propositionsTransportService->handleListePropositionsByUser($this->getUser())])),
default => new Response(404),
};
}
The service handling the request and retreiving the demands :
/**
* #param UserInterface $user
* #return array
*/
public function handleListePropositionsByUser(UserInterface $user) : array
{
$propositions = [];
foreach ($this->propositionTransportRepository->findPropositionsByUtilisateur($user) as $propositionTransport) {
$propositions[] = DemandeTransportHelper::serializePropositionDemande($propositionTransport);
}
return $propositions;
}
And the DQL :
/**
* #param UserInterface $user
* #return mixed
*/
public function findPropositionsByUtilisateur(UserInterface $user) : mixed
{
$q = $this->createQueryBuilder('p')
->where('p.utilisateur = :utilisateur')
->setParameters([
'utilisateur' => $user
])
->orderBy('p.dateCreation', 'DESC');
return $q->getQuery()->getResult();
}
So :
When i'm doing $utilisateur->getDemandesTransports() : it works by showing me all the demands.
Well, but when I'm trying to get them by DQL (cause I want them orderd by), it returns me 0 results...
Solved by setting the parameter type :
->setParameter('utilisateur', $utilisateur->getId(), 'ulid')
I'm using ULID (UUID like) on IDs.
https://symfony.com/doc/current/components/uid.html#working-with-ulids
With annotations
You can order your data by specifying sorting in your $demandeTransports property annotations.
/**
* #ORM\OneToMany(targetEntity=DemandeTransport::class, mappedBy="utilisateur", orphanRemoval=true)
* #ORM\OrderBy({"dateCreation" = "DESC"})
*/
private Collection $demandeTransports;
So when you call $utilisateur->getDemandesTransports() you will get ordered data.
With DQL
Also if you still want to use DQL then you should change your query to this as you need to join the Utilisateur entity then you can use the desired properties
$q = $this->createQueryBuilder('p')
->join('p.utilisateur', 'u')
->where('u.utilisateur = :utilisateur')
->setParameters([
'utilisateur' => $user
])
->orderBy('u.dateCreation', 'DESC');
Related
I have two entities (User and Product) with a OneToMany relationship. I want to retrieve all Product entities related to a specific User filtering them by a field called "finished". I will post the relevant information of those entities:
User Entity:
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
*/
class User implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Product", mappedBy="user")
*/
private $products;
....
Product Entity:
/**
* #ORM\Entity(repositoryClass="App\Repository\Productepository")
*/
class Product
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="products")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\Column(type="boolean")
*/
private $finished;
....
I am using the following code to filter my products, but is there any other more efficient way to do it?
$user = $this->getUser();
$products = $user->getProducts();
$filtered = [];
foreach ($products as $product){
if(!$product->getFinished()){
$filtered[] = $product;
}
}
Thanks for your help
One of the solutions would be to use a database query like the following :
- in the controller, you can do
<?php
namespace App\Controller\ProductController;
use App\Repository\ProductRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
class ProductController extends AbstractController
{
/**
* #Route("/products", methods={"GET"}, name="app_get_products")
* #IsGranted("IS_AUTHENTICATED_REMEMBERED")
*/
public function listProduct(ProductRepository $repository)
{
$user = $this->getUser();
$products = $repository->findBy([
'user' => $user,
'finished' => false
]);
return $this->render('Product/list.html.twig', [
'products' => $products,
]);
}
}
Notice the request that is made : this is an attempt at a solution.
I need to remove the association between two objects with the following relation
Devis Entity :
class Devis
{
/**
* #var \stdClass
*
* #ORM\ManyToOne(targetEntity="DevisBundle\Entity\Client", inversedBy="devis", cascade={"persist"})
* #ORM\JoinColumn(nullable=false, nullable=true)
*/
private $client;
/**
* Set client
*
* #param \DevisBundle\Entity\Client $client
* #return Devis
*/
public function setClient(\DevisBundle\Entity\Client $client)
{
$this->client = $client;
return $this;
}
Client entity:
class Client
{
/**
* #var \stdClass
*
* #ORM\OneToMany(targetEntity="DevisBundle\Entity\Devis", mappedBy="client", cascade={"remove"})
*/
private $devis;
I tried to folllow the doc from doctrine : http://docs.doctrine-project.org/en/latest/reference/working-with-associations.html#removing-associations
$devis = $em->getRepository('DevisBundle:Devis')->findOneById($devisId);
$client = $em->getRepository('DevisBundle:Client')->findOneById($devis->getClient());
$client->getDevis()->removeElement($devis);
$devis->setClient(null);
//$em->persist($devis);
$em->flush();
dump($devis);
But I have this error :
Catchable Fatal Error: Argument 1 passed to DevisBundle\Entity\Devis::setClient() must be an instance of DevisBundle\Entity\Client, null given, called in....
Change your set Method like this :
public function setClient(\DevisBundle\Entity\Client $client = null)
{
$this->client = $client;
return $this;
}
This is also how it would be generated with the command doctrine:generate:entities.
I'm learning SF2 - really impressed with the job done, faced my first real issue I can't solve myself.
I have two entities: Post and Tag. Shortened code below:
class Tag
{
/**
* #ORM\ManyToMany(targetEntity="Post", mappedBy="tags", cascade={"persist"})
*/
private $posts;
public function __construct()
{
$this->posts = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* #param \My\AppBundle\Entity\Snippet $posts
* #return Tag
*/
public function addSnippet(\My\AppBundle\Entity\Post $posts)
{
$this->posts[] = $posts;
return $this;
}
/**
* #param \My\AppBundle\Entity\Snippet $snippets
*/
public function removeSnippet(\My\AppBundle\Entity\Post $posts)
{
$this->posts->removeElement($posts);
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getSnippets()
{
return $this->posts;
}
}
class Post
{
/**
* #ORM\ManyToMany(targetEntity="Tag", inversedBy="posts", cascade={"persist"})
* #ORM\JoinTable(name="posts_tags",
* joinColumns={#ORM\JoinColumn(name="post_id", referencedColumnName="id", unique=true, onDelete="cascade")},
* inverseJoinColumns={#ORM\JoinColumn(name="tag_id", referencedColumnName="id", unique=true, onDelete="cascade")}
* )
*/
private $tags;
public function __construct()
{
$this->tags = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* #param \My\AppBundle\Entity\Tag $tags
* #return Snippet
*/
public function addTag(\My\AppBundle\Entity\Tag $tags)
{
$this->tags[] = $tags;
return $this;
}
/**
* #param \My\AppBundle\Entity\Tag $tags
*/
public function removeTag(\My\AppBundle\Entity\Tag $tags)
{
$this->tags->removeElement($tags);
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getTags()
{
return $this->tags;
}
}
As you can see I have M:M relation between two entities.
I have also a form to add Post with embedded Tag collection:
$builder
->add('title')
->add('tags', 'collection', array(
'type' => new \My\AppBundle\Form\TagType(),
'allow_add' => true,
'by_reference' => false,
'prototype' => true
))
;
TagType form class:
$builder->add('name');
Everything works as expected. Except one thing: if there's a Tag object with the following name, I'm getting SQLSTATE[23000]: Integrity constraint violation MySQL error which is obvious. If I apply unique validation constraint I can add a tag to post (if it already exists in database).
It's obvious I need to check if following tag does exist in database and add it only if does not, but... how to do it Symfony way?
Any suggestions appreciated!
You can use UniqueEntity to handle this. I can't see your annotations on your tags class, or your declaration of 'name' but if you add something like the below it should give you a unique validation constraint based on name with an optional message to throw back.
/**
* #ORM\Entity
* #UniqueEntity(fields="name", message="This tag name already exists")
*/
class Tag...
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=255, unique=true)
*/
protected $name;
I'm trying to perform a ManyToMany self referencing association in my Symfony 2.1 project by following the Doctrine docs: http://docs.doctrine-project.org/en/latest/reference/association-mapping.html#many-to-many-self-referencing
My use-case is that I'm working on a CMS and I'm adding the ability to have related items of content. For example: I could have a sidebar on a website which would say that this piece of content X is related to Y and Z. Similarly on pages where content Y appears it says that it is related to content item X.
In my tests using this to add a new relation between content items fails because it reaches PHP's maximum nesting level of 100 because it is running toArray() on the current content item and then again on the related content item and so on and so on.
I've seen many similar questions on SO about Many-to-Many Self referential Doctrine associations but none with enough complete code to be able to see how others have managed this. Can anybody help?
My Content entity:
/**
* #ORM\MappedSuperclass
* #ORM\Table(name="content")
* #ORM\Entity(repositoryClass="CMS\Bundle\Common\ContentBundle\Entity\ContentRepository")
* #ORM\InheritanceType("JOINED")
*/
abstract class content implements ContentInterface
{
/**
* #var int $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $title
*
* #ORM\Column(name="title", type="string", length=255)
* #Assert\NotBlank()
*/
private $title;
// Other class properties
/**
* #var array
*
* #ORM\ManyToMany(targetEntity="Content", cascade={"persist"})
* #ORM\JoinTable(name="content_relation",
* joinColumns={#ORM\JoinColumn(name="relation_id", referencedColumnName="id")},
* inverseJoinColumns={
* #ORM\JoinColumn(name="related_content_id", referencedColumnName="id")
* })
**/
private $related;
public function __construct()
{
$this->related = new ArrayCollection();
}
// Other getters & setters for class properties
/**
* #return array
*/
public function getRelated()
{
return $this->related;
}
/**
* #param Content $relation
*/
public function addRelation(Content $relation)
{
$this->related->add($relation);
$this->related->add($this);
}
/**
* #return array
*/
public function toArray()
{
$related = array();
foreach($this->getRelated() as $relatedItem) {
$related[] = $relatedItem->toArray();
}
return array(
'type' => static::getType(),
'id' => $this->id,
'title' => $this->title,
....
'related' => $related
);
}
In my RelationsController for managing the related content data I use it like this:
/**
* Creates a new relation to a content item
*
* #Route("{_locale}/content/{id}/related", name="relation_add")
* #Method("POST")
*/
public function addAction(Request $request, $id)
{
// Validation and error checking
// $entity is loaded by the repository manager doing a find on the passed $id
$entity->addRelation($relation);
$em = $this->getEntityManager();
$em->persist($entity);
$em->persist($relation);
$em->flush();
$response = $relation->toArray();
return new JsonResponse($response, 201);
}
The fix for this was to use the JMSSerializerBundle to encode the entity to JSON instead of using a toArray method and change the addRelation function to:
/**
* #param Content $relation
*/
public function addRelation(Content $relation)
{
$this->related[] = $relation;
if (! $relation->getRelated()->contains($this)) {
$relation->addRelation($this);
}
}
I am stuck and frustrated with the bellow error message:
Catchable Fatal Error: Argument 1 passed to Medicine\UserBundle\Entity\User
::setUsertype() must be an instance of Medicine\UserBundle\Entity\Usertype,
instance of Doctrine\Common\Collections\ArrayCollection given, called in
/opt/lampp/htdocs/drugs/vendor/symfony/src/Symfony/Component/Form/Util
/PropertyPath.php on line 347 and defined in /opt/lampp/htdocs/drugs/src/
Medicine/UserBundle/Entity/User.php line 224
What I think this error is due to use of manytoone field in my entity, I even tried with keeping onetomany in another entity.
I have a user entity and a usertype entity, the usertype_id is a manytoone field in user table. here is the code for both the entities:-
User
namespace Medicine\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="Medicine\UserBundle\Repository\UserRepository")
* #ORM\Table(name="user")
* #ORM\HasLifecycleCallbacks()
*/
class User
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\HasLifecycleCallbacks()
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $username;
/**
* #ORM\ManyToOne(targetEntity="Usertype", inversedBy="users")
* #ORM\JoinColumn(name="usertype_id", referencedColumnName="id")
*/
protected $usertype;
/**
* #ORM\Column(type="string")
*/
protected $image;
/**
* Set usertype
*
* #param Medicine\UserBundle\Entity\Usertype $usertype
*/
public function setUsertype(\Medicine\UserBundle\Entity\Usertype $usertype)
{
$this->usertype = $usertype;
}
/**
* Get usertype
*
* #return Medicine\UserBundle\Entity\Usertype
*/
public function getUsertype()
{
return $this->usertype;
}
}
I am just showing the concerned code, i have all the getter and setter methods for the above code.
UserType
namespace Medicine\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="Medicine\UserBundle\Repository\UsertypeRepository")
* #ORM\Table(name="usertype")
* #ORM\HasLifecycleCallbacks()
*/
class Usertype
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\HasLifecycleCallbacks()
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $name;
/**
* #ORM\OneToMany(targetEntity="User", mappedBy="usertype")
*/
protected $users;
public function __construct()
{
$this->users = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add users
*
* #param Medicine\UserBundle\Entity\User $users
*/
public function addUser(\Medicine\UserBundle\Entity\User $users)
{
$this->users[] = $users;
}
/**
* Get users
*
* #return Doctrine\Common\Collections\Collection
*/
public function getUsers()
{
return $this->users;
}
}
Controller
This Executes when a user wants to login. He will fill in the username password and a UserType:
public function indexAction()
{
$entity = new User();
$form = $this->createForm(new LoginForm(), $entity);
$request = $this->getRequest();
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
if ($form->isValid()) {
echo "<pre>"; print_r($entity->getUsertype()); exit;
$em = $this->getDoctrine()
->getEntityManager();
$em->persist($entity);
$userrepository = $em->getRepository('MedicineUserBundle:User');
echo "<pre>"; print_r($entity->getUsertype()); exit;
$all = $userrepository->findOneBy(array('login' => $entity->getLogin(), 'password' => $entity->getPassword()));
if($all)
{
return $this->redirect($this->generateUrl('MedicineUserBundle_login'));
}
}
}
return $this->render('MedicineUserBundle:User:loginpage.html.twig',array(
'form' => $form->createView()
));
}
Login Form
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('login', 'text', array('label' => 'Username',))
->add('password','password')
->add('usertype', 'entity', array('class' => 'MedicineUserBundle:Usertype', 'property'=>'name', 'multiple' => true, ))
;
}
The 'multiple' => true in combination with your entity association definition is causing this problem.
You should find that if you change multiple to false (and as such can only select one UserType for your User), things work properly.
If you want multiple UserTypes for one User, you have a Many-to-Many association - one user can have many UserTypes, and one UserType can have many Users. See Doctrine's ManyToMany association type to implement this. Documentation Here.
Hope this helps.