I'm stuck with my relation.
Here's my entity :
class Orderproduct
/**
* #ORM\OneToMany(targetEntity="ProductBundle\Entity\Product", mappedBy="orderproduct", cascade={"persist"})
*/
private $product;
/**
* #ORM\OneToMany(targetEntity="ProductBundle\Entity\Machining", mappedBy="orderproduct", cascade={"persist"})
*/
private $machining;
And my two others entity :
class Product
/**
* #ORM\ManyToOne(targetEntity="ProductBundle\Entity\Orderproduct", inversedBy="product")
* #ORM\JoinColumn(name="orderproduct_id", referencedColumnName="id", nullable=false)
*/
private $orderproduct;
class Machining
/**
* #ORM\ManyToOne(targetEntity="ProductBundle\Entity\Orderproduct", inversedBy="machining")
* #ORM\JoinColumn(name="orderproduct_id", referencedColumnName="id", nullable=false)
*/
private $orderproduct;
And I've got this error :
SQLSTATE[23000]: Integrity constraint violation: 1048 Le champ 'orderproduct_id' ne peut être vide (null)
Here's my simple add function
public function addOrderproductAction(Request $request)
{
$orderproduct = new Orderproduct();
$formorderproduct = $this->createForm(OrderproductType::class, $orderproduct);
if($formorderproduct->handleRequest($request)->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($orderproduct);
$em->flush();
return $this->redirect($this->generateUrl('product_bundle_listorderproduct'));
}
return $this->render('ProductBundle:Default:neworderproduct.html.twig', array(
'formorderproduct' => $formorderproduct->createView(),
));
}
And i got this with a dump just before the flush :
Any idea ?
Thx for your help!
Edit : After put $product->getOrderproduct($this) and $machining->getOrderproduct($this).
Edit :
I'm changing my model but still have the same problem. So I have a relation between product and machining
Product
/**
* #ORM\OneToMany(targetEntity="ProductBundle\Entity\Machining", mappedBy="product", cascade={"persist"})
*/
private $machining;
Machining
/**
* #ORM\ManyToOne(targetEntity="ProductBundle\Entity\Product", inversedBy="machining")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
private $product;
This is Machining table in my Db : product_id is null.
I already try to modify setProduct in Machining but it still the same.
try this:
in Orderproduct' addMachining($machining) method:
$this->machining[] = $machining;
$machining->setOrderproduct($this);
return $this;
the same for the other entity(of course different method name and properties). If this does not work try to persist all entity separately.
So I found a solution :
In the buildForm simply add
'by_reference => false'
Now it's work !
Related
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');
I have three entities : Trophy | Competition | Season
One Competition is created for one trophy for one season (you can't have two competitions with same combination "season + trophy").
Competition as a ManyToOne relation with Trophy, and a ManyToOne relation with Season.
Trophy and Season have no direct relation.
I want to display two dropdowns on a page with the content of the second one being dependent from the value of the first :
First dropdown allow to select a trophy type (which is a property of Trophy entity), second dropdown must list seasons that are "still available" for trophy type selected (meaning by that "list all seasons for which there are no competition for this trophy type")
I've got almost all working (listener in the Formtype, ajax etc) I've created a specific function allWithoutThisCompetitionType() in SeasonRepository. Function is correctly called every-time user select a new value in dropdown BUT... I don't know anything about SQL nor dql, so I'm struggling to find the correct formulation for my query. I've tried with notin(), with "sub" or "nested" queries... I definitely don't know what I'm doing...
How can I do something like ? :
$qb = $em->getRepository(\App\Entity\Seasonmanager\Season::class)->createQueryBuilder('s')
->where('s.competitions.trophy != :trophy')
->setParameter('trophy', $trophy);
= Here are all the seasons for which no competition has been already created with this trophy
Thank you for your help.
Trophy entity :
/**
* #ORM\Entity(repositoryClass="App\Repository\Seasonmanager\TrophyRepository")
*/
class Trophy
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $uniqueid;
// other properties here...
//////////////////////////////////////////////////////////////////////////////////
//// LIAISONS VERS D'AUTRES ENTITY ////
/**
* #ORM\OneToMany(targetEntity="App\Entity\Seasonmanager\Competition", mappedBy="trophy", orphanRemoval=true)
*/
private $competitions;
Competition entity :
/**
* #ORM\Entity(repositoryClass="App\Repository\Seasonmanager\CompetitionRepository")
* #UniqueEntity(
* fields={"trophy","season"},
* errorPath="trophy",
* message="Une compétition existe déjà pour ce trophée et cette saison"
* )
*/
class Competition
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
// other properties here...
//////////////////////////////////////////////////////////////////////////////////
//// LIAISONS VERS D'AUTRES ENTITY ////
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Seasonmanager\Trophy", inversedBy="competitions")
* #ORM\JoinColumn(nullable=false)
*/
private $trophy;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Seasonmanager\Season", inversedBy="competitions")
* #ORM\JoinColumn(nullable=false)
*/
private $season;
Season entity :
/**
* #ORM\Entity(repositoryClass="App\Repository\Seasonmanager\SeasonRepository")
* #UniqueEntity("yearin")
*/
class Season
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="integer", length=4)
*/
private $yearout;
/**
* #ORM\Column(type="string", length=8)
*/
private $uniqueid;
// other properties here...
//////////////////////////////////////////////////////////////////////////////////
//// LIAISONS VERS D'AUTRES ENTITY ////
/**
* #ORM\OneToMany(targetEntity="App\Entity\Seasonmanager\Competition", mappedBy="season", orphanRemoval=true)
*/
private $competitions;
The SeasonRepository where I try to add my query :
namespace App\Repository\Seasonmanager;
use App\Entity\Seasonmanager\Season;
use App\Entity\Seasonmanager\Trophy;
use App\Entity\Seasonmanager\Competition;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
/**
* #method Season|null find($id, $lockMode = null, $lockVersion = null)
* #method Season|null findOneBy(array $criteria, array $orderBy = null)
* #method Season[] findAll()
* #method Season[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class SeasonRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Season::class);
}
public function allWithoutThisCompetitionType($type): array
{
$em = $this->getEntityManager();
$trophys = $em
->getRepository(Trophy::class)
->findBy(['uniqueid' => $type],['id'=>'DESC'])
;
$trophy = reset($trophys);
$qb = $em->getRepository(\App\Entity\Seasonmanager\Season::class)->createQueryBuilder('s')
->where('s.competitions.trophy', $trophy);
$query = $qb->getQuery();
$result = $query->getResult();
$donnees = $result;
return $donnees;
}
Here is the query, though, I'm not 100% sure it will match your need.
Let me know in comment if something is wrong, I will edit my answer.
public function allWithoutThisCompetitionType($trophy) {
// Split init from the rest of the query in case you need to use `$qb->expr()`
$qb=$this->createQueryBuilder("season");
$qb->leftJoin("season.competition", "competition") // Join competition
->join("competition.trophy", "trophy") // Join Trophy
->andWhere($qb->expr()->orX( // Or (either one of the following satements)
$qb->expr()->isNull("competition.id"),
$qb->expr()->notIn("trophy.uniqueid", ":trophy")))
->setParameter("trophy", $trophy);
return $qb->getQuery()->getResult();
}
I have a problem with Symfony3/doctrine2 and many-to-many with extra columns. I just start to develop with it
Yes, I see the post : Doctrine2: Best way to handle many-to-many with extra columns in reference table
But honestly, I don't understand why it's not working with my code...
I'm stuck since 2 days, looking on Google (he's not my friend), reading documentations... my brain doesn't understand...
Please, help me to understand why.
Problem
I have member and address entities. I would like to have all addresses by member OR all members by address. So, ManyToMany in Doctrine.
On my join Table, I need to have an extra column favorite_address
So, I need to have 3 entities : Member, Address, MemberAddress
Entity Member :
class Member implements AdvancedUserInterface
{
....
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Member\MemberAddress", mappedBy="member", cascade={"all"})
* #ORM\JoinTable(name="member_address",
* joinColumns={#ORM\JoinColumn(name="member_id", referencedColumnName="id")})
*/
private $addresses;
....
public function __construct(){
$this->addresses = new ArrayCollection();
}
/**
* Add an address
* #param Address $address
*/
public function addAddress(\AppBundle\Entity\Address\Address $address)
{
// Ici, on utilise l'ArrayCollection vraiment comme un tableau
$this->addresses[] = $address;
}
/**
* Remove an address
* #param Address $address
*/
public function removeAddress(\AppBundle\Entity\Address\Address $address)
{
// Ici on utilise une méthode de l'ArrayCollection, pour supprimer la catégorie en argument
$this->addresses->removeElement($address);
}
/**
* Get all addresses
* #return ArrayCollection
*/
public function getAddresses()
{
return $this->addresses;
}
Entity Address :
class Address
{
....
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Member\MemberAddress", mappedBy="address", cascade={"all"})
* #ORM\JoinTable(name="member_address",
* joinColumns={#ORM\JoinColumn(name="address_id", referencedColumnName="id")})
*/
private $members;
....
/**
* Add a member
* #param Member $member
*/
public function addMember(\AppBundle\Entity\Member\Member $member)
{
// Ici, on utilise l'ArrayCollection vraiment comme un tableau
$this->members[] = $member;
}
/**
* Remove a member
* #param Member $member
*/
public function removeMember(\AppBundle\Entity\Member\Member $member)
{
// Ici on utilise une méthode de l'ArrayCollection, pour supprimer la catégorie en argument
$this->members->removeElement($member);
}
/**
* Get all members
* #return ArrayCollection
*/
public function getMembers()
{
return $this->members;
}
And the last Entity : MemberAddressReference
class MemberAddress
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/** #ORM\ManyToOne(targetEntity="AppBundle\Entity\Member\Member", inversedBy="addresses")
* #ORM\JoinColumn(name="member_id", referencedColumnName="id")
*/
protected $member;
/** #ORM\ManyToOne(targetEntity="AppBundle\Entity\Address\Address", inversedBy="members")
* #ORM\JoinColumn(name="address_id", referencedColumnName="id")
*/
protected $address;
/** #ORM\Column(type="boolean") */
protected $isFavorite;
To finish, the controller
class MemberAddressController extends Controller
{
public function createAction(Request $request){
....
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$currentDate = new \DateTime("now");
$em = $this->getDoctrine()->getManager();
$address = new Address();
$memberAddress = new MemberAddress();
$address->setType($form['type']->getData());
$address->setCreated($currentDate);
$address->setModified($currentDate);
$memberAddress->setMember($member);
$memberAddress->setAddress($address);
$memberAddress->setFavorite(1);
$em->persist($member);
$em->persist($address);
$em->persist($memberAddress);
$member->addAddress($address);
$em->flush();
dump($member);
die();
}
So, what's wrong
I get this error :
Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "AppBundle\Entity\Member\Member#$addresses", got "AppBundle\Entity\Address\Address" instead.
Yup, type is not good, I understand, but why he's not good ?
public function addAddress(\AppBundle\Entity\Address\Address $address)
{
// Ici, on utilise l'ArrayCollection vraiment comme un tableau
$this->addresses[] = $address;
}
addAddress take Address object, no ? So why he's waiting an array ?
Please help me, I'm going crazy...
I don't understand the goal of your memberAdress entity. If you want to create a OneToMany bidirectional relationship, you don't need to create a third entity.
According to doctrine documentation :
/** #Entity */
class Product
{
// ...
/**
* One Product has Many Features.
* #OneToMany(targetEntity="Feature", mappedBy="product")
*/
private $features;
// ...
public function __construct() {
$this->features = new ArrayCollection();
}
}
/** #Entity */
class Feature
{
// ...
/**
* Many Features have One Product.
* #ManyToOne(targetEntity="Product", inversedBy="features")
* #JoinColumn(name="product_id", referencedColumnName="id")
*/
private $product;
// ...
}
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html
EDIT
If you want to use a third entity, you don't have a relationship between your adress and your member. They are just linked by the third party.
So you should do the mapping this way :
Member Entity :
class Member implements AdvancedUserInterface
{
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Member\MemberAddress", mappedBy="member", cascade={"all"})
*/
private $addresses;
Adress Entity
class Address
{
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Member\MemberAddress", mappedBy="address", cascade={"all"})
*/
private $members;
If you want all the adresses of a member you will need to create a custom repository to join your adress table with the two other tables.
How can I set the protected object user? After filling the form i have to add user object with current user data (for example like saving comments). I tried something like that:
if ($form->isValid()) {
$comment = $form->getData();
$comment->user = $this->contextSecurity->getToken()->getUser();
$this->model->save($comment);
}
And i've got this error
FatalErrorException: Error: Cannot access protected property AppBundle\Entity\Comment::$user in /home/AppBundle/Controller/CommentsController.php line 184
Here is my Comment entity:
class Comment
{
/**
* Id.
*
* #ORM\Id
* #ORM\Column(
* type="integer",
* nullable=false,
* options={
* "unsigned" = true
* }
* )
* #ORM\GeneratedValue(strategy="IDENTITY")
*
* #var integer $id
*/
private $id;
/**
* Content.
*
* #ORM\Column(
* name="content",
* type="string",
* length=250,
* nullable=false
* )
* #Assert\NotBlank(groups={"c-default"})
* #Assert\Length(min=3, max=250, groups={"c-default"})
*
* #var string $content
*/
private $content;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="comments")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
*/
protected $user;
I'm using Symfony2.3. Any help will be appreciated.
You can't modify protected properties from outside of the object. You need a public property or a setter for that.
class Comment
{
// ...
public function setUser(User $user)
{
$this->user = $user;
}
}
And in a controller you can write:
$comment->setUser($this->getUser());
This question is not related to Symfony2, at first you should read about php types, especially about objects. read here and then here
You should understand how Visibility works. After that you will understand that access to protected/private properties of the object is only available from the object itself, so you need to create public method
setUser($user) {
$this->user = $user;
}
I always use protected, If i want edit variable or take the value, I use the getter and setter:
public function setUser($user) {
$this->user = $user;
}
public function getUser(){
return $this->user;
}
Since upgrading to Symfony 2.7, I seem to keep getting 'circular reference has been detected' errors when attempting to serialize an array of contacts associated with a given group. They're setup in a many-to-many association (one group has many contacts; one contact has many group-associations).
Now, I followed the guide for using serialization groups as per the 2.7 upgrade post, but still seem to get the error. My controller code for this is currently as follows:
$group = $this->getDoctrine()
->getRepository('TwbGroupsBundle:ContactGroup')
->find($id);
$groupContacts = $group->getContacts();
$encoder = new JsonEncoder();
$normalizer = new ObjectNormalizer();
$serializer = new Serializer(array($normalizer), array($encoder));
$json = $serializer->serialize($groupContacts, 'json', array(
'groups' => array('public')
));
When running $serializer->serialize(), I get the CircularReferenceException after 1 circular reference. So far I have my Contact entity configured like so, with the #Groups annotations:
/**
* Contact
*
* #ORM\Table(name="tblContacts")
* #ORM\Entity(repositoryClass="Twb\Bundle\ContactsBundle\Entity\Repository\ContactRepository")
*/
class Contact implements ContactInterface
{
/**
* #var string
*
* #ORM\Column(name="ContactName", type="string", length=50, nullable=true)
* #Groups({"public"})
*/
private $contactname;
/**
* #var integer
*
* #ORM\Column(name="ContactID", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #Groups({"public"})
*/
private $contactid;
/**
*
* #var ArrayCollection
* #ORM\ManyToMany(targetEntity="Twb\Bundle\GroupsBundle\Entity\ContactGroup", inversedBy="contacts")
* #ORM\JoinTable(name="tblContactsGroupsAssignments",
* joinColumns={#ORM\JoinColumn(name="contactId", referencedColumnName="ContactID")},
* inverseJoinColumns={#ORM\JoinColumn(name="contactGroupId", referencedColumnName="id")}
* )
*/
protected $contactGroups;
// ...getters/setters and so on
}
And my ContactGroup entity:
/**
* ContactGroup
*
* #ORM\Table(name="tblContactsGroups")
* #ORM\Entity
*/
class ContactGroup
{
// ...
/**
*
* #var Contact
*
* #ORM\ManyToMany(targetEntity="Twb\Bundle\ContactsBundle\Entity\Contact", mappedBy="contactGroups")
*/
private $contacts;
// ...
}
Is there something I'm missing here to get around the circularity problem? Many thanks.
It looks like something wrong with config.
You have to enable serialization groups annotation:
# app/config/config.yml
framework:
# ...
serializer:
enable_annotations: true
And proper use statement has to be present in ContactGroup entity class
use Symfony\Component\Serializer\Annotation\Groups;
$normalizers->setCircularReferenceHandler(function ($object) {
return $object->getId();
});
Just add it after you make the instance of your objectNormalizer ;