Symfony 2.8 Doctrine. Using composite primary key and ManyToMany associations - symfony

I have 2 entities - Platform and Product. In the Products table I have composite primary key by [Product ID + Platform ID]. One product can be present at many platforms so and one platform can contain many products, so the association is ManyToMany.
The Platform entity:
/**
* Platform
*
* #ORM\Table(name="Platforms")
*/
class Platform
{
/**
* #var int
*
* #ORM\Column(name="Platform_Id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="Platform_Name", type="string", length=64)
*/
private $name;
/**
* #ORM\ManyToMany(targetEntity="Product", mappedBy="platforms", cascade={"ALL"}, indexBy="numpp")
*/
protected $products;
public function __construct()
{
$this->products = new ArrayCollection();
}
public function addProducts($numpp)
{
$this->products[$numpp] = new Product($numpp, $this);
}
The Product entity:
/**
* Product
*
* #ORM\Table(name="Products")
*/
class Product
{
/**
* #var string
*
* #ORM\Column(name="Numpp", type="string", length=6)
* #ORM\Id
*/
private $numpp;
/**
* #var int
*
* #ORM\ManyToMany(targetEntity="Platform", inversedBy="products")
* #ORM\JoinColumn(name="Platform_Id", referencedColumnName="Platform_Id")
* #ORM\Id
*/
private $platforms;
public function __construct($numpp, Platform $platform)
{
$this->numpp = $numpp;
$this->platforms = new ArrayCollection();
$this->platforms[] = $platform;
}
In my controller when trying to create new Product entity...
$em = $this->getDoctrine()->getManager();
$platform = $em->getRepository("AGAAnalyticsBundle:Platform")->find(1);
$product = new Product('05062', $platform);
$em->persist($product);
$em->flush();
I get an error - Cannot insert the value NULL into column 'Platform_Id', table 'dbo.Products'
And other way using addProduct method...
$em = $this->getDoctrine()->getManager();
$platform = $em->getRepository("AGAAnalyticsBundle:Platform")->find(1);
$platform->addProduct('05062');
$em->flush();
I get an error - The column id must be mapped to a field in class AGA\AnalyticsBundle\Entity\Platform since it is referenced by a join column of another class.
Please help to understand where I am wrong and how I should build this relation between my entities correctly.

Related

I get friends and all other users when trying to load only friends of the current user

I am trying to load friends of a specific user after asking why I get null values of the friend of the user data it was responded that it was because of the lazy loading. So I was advised to add JOIN and I admit this was a miss from my side. But after adding the JOIN I get the data of the friend in the result and then I receive all the users from my users table for which I have not asked.
I have already tried removing the myuser from the SELECT, but this way I get the lazy loading problem again. I have tried LEFT JOIN (I admit it was dumb try from my side). But how can I correct this when there is no ON in the Doctrine Query Language.
My Entity(Friends):
class Friends
{
/**
* #ORM\Id()
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="myfriends")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
*/
private $friendsWithMe;
/**
* #ORM\Id()
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="friendof")
* #ORM\JoinColumn(name="friend_id", referencedColumnName="id", nullable=false)
*/
private $afriendof;
/**
* #var integer
*
* #ORM\Column(name="status", type="smallint")
*/
private $status;
/**
* #return User
*/
public function getFriendsWithMe()
{
return $this->friendsWithMe;
}
/**
* #param mixed $friendsWithMe
*/
public function setFriendsWithMe($friendsWithMe)
{
$this->friendsWithMe = $friendsWithMe;
}
/**
* #return User
*/
public function getAfriendof()
{
return $this->afriendof;
}
/**
* #param mixed $afriendof
*/
public function setAfriendof($afriendof)
{
$this->afriendof = $afriendof;
}
/**
* #return integer
*/
public function getStatus()
{
return $this->status;
}
/**
* #param integer $status
*/
public function setStatus($status)
{
$this->status = $status;
}
}
My Entity(User):
class User implements UserInterface
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
public function __construct()
{
$this->userPosts = new ArrayCollection();
$this->myfriends = new ArrayCollection();
$this->friendof = new ArrayCollection();
}
/**
* #var
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Friends", mappedBy="afriendof")
*/
private $friendof;
/**
* #var
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Friends", mappedBy="friendsWithMe")
*/
private $myfriends;
My Repository(FriendsRepository):
public function personalFriends($userId){
$em = $this->getEntityManager();
$result = $em->createQuery('SELECT friends, myuser FROM AppBundle\Entity\Friends friends
INNER JOIN AppBundle\Entity\User myuser WHERE friends.friendsWithMe = :userId AND friends.status = 1');
$result->setParameter('userId', $userId);
return $result->getResult();
}
The place where I call the repository:
public function indexAction(Request $request)
{
$user = $this->get('security.token_storage')->getToken()->getUser();
$userId = $user->getId();
$friends = $this->getDoctrine()->getRepository(Friends::class)->personalFriends($userId);
dump($friends);
exit();
Results that I get now:
https://pastebin.com/2M4SYTLb
Results that I expect:
https://pastebin.com/BxsC9QbE
Hope I understand your problem.
But from what I see you are asking for the data of the friends AND the data of the users when you are doing 'SELECT friends, myuser.
Try only selecting friends
Like this:
SELECT friend FROM AppBundle\Entity\Friends friend INNER JOIN AppBundle\Entity\User user WHERE friend.friendsWithMe = :userId AND friend.status = 1
Then you'll only have as a result an array of Friends.
If there is still a problem you can add fetch="EAGER" so it wont be "LAZY"
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="myfriends", fetch="EAGER")

doctrine composite key and one to many

I use Symfony 2.8. I have two table and in both the primary key is composed by 3 columns:
id, tipo_corso, comune
02, it, devi
01, en, capi
09, es, file
Obviously the two table have other different columns. I can't change the primary key by use only one or two columns. For one record in StranieriCRS table there are many record in EsoneroLingua table (OneToMany):
First entity:
class StranieriCRS
{
/**
* #ORM\Column(type="string")
* #ORM\Id
*/
private $id;
/**
* #ORM\Column(type="string")
* #ORM\Id
*/
private $tipo_corso;
/**
* #ORM\Column(type="string")
* #ORM\Id
*/
private $comune;
public function __construct($id, $tipo_corso, $comune)
{
$this->id = $id;
$this->tipo_corso = $tipo_corso;
$this->comune = $comune;
$this->esonerolingua = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* #ORM\OneToMany(targetEntity="EsoneroLingua", mappedBy="stranieriCRS", fetch="EAGER")
*/
private $esonerolingua;
/**
* Get esonerolingua
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getEsonerolingua()
{
return $this->esonerolingua;
}
Second entity:
class EsoneroLingua
{
/**
* #ORM\Column(type="string")
* #ORM\Id
*/
private $id;
/**
* #ORM\Column(type="string")
* #ORM\Id
*/
private $tipo_corso;
/**
* #ORM\Column(type="string")
* #ORM\Id
*/
private $comune;
public function __construct($id, $tipo_corso, $comune)
{
$this->id = $id;
$this->tipo_corso = $tipo_corso;
$this->comune = $comune;
}
/**
* #ORM\ManyToOne(targetEntity="StranieriCRS", inversedBy="esonerolingua")
* #ORM\JoinColumns(
* #ORM\JoinColumn(name="id", referencedColumnName="id"),
* #ORM\JoinColumn(name="tipo_corso", referencedColumnName="tipo_corso"),
* #ORM\JoinColumn(name="comune", referencedColumnName="comune"),
* )
*/
private $stranieriCRS;
The problem occur when I want get the StranieriCRS object because he give me as result only one result...seems like a OneToOne relation.
My Controller:
$sql = $entityManager->createQuery("
SELECT c
FROM AppBundle:EsoneroLingua c
WHERE c.id = '1546871' and c.tipo_corso = 'C' and c.comune = '7868'
");
$test = $sql->getResult();
In $test I was expect N record of EsoneroLingua with the same record StranieriCRS but I get only one EsoneroLingua with the correct StranieriCRS object. Seems work like OneToOne relation...why? Plus if I made dump($sql->getSql()); I obtain the raw sql...I try to use it directly in my db and he give me the right result. Is it a Doctrine bug?
To make a bidirectionnal One-To-Many, specify the JoinColumns only in the Many-To-One side.
So, in StranieriCRS, remove the following lines :
* #ORM\JoinColumns(
* #ORM\JoinColumn(name="id", referencedColumnName="id"),
* #ORM\JoinColumn(name="tipo_corso", referencedColumnName="tipo_corso"),
* #ORM\JoinColumn(name="comune", referencedColumnName="comune"),
* )
And just let Doctrine guess the columns with the inversedBy and mappedBy attributes.
For more information on the mappings, see this page.

Symfony 3 UniqueEntity validation on update

I am facing an issue with UniqueEntity validation.
I have a field "internal_asset_number" which should be unique and it's working fine on create. On update when i edit the existing current data with the same values, it shows "There is already an asset with that internal number!" but it shouldn't because it's the same entry.
The entity:
/**
* Asset
*
* #ORM\Table(schema="assets", name="asset", uniqueConstraints= {#ORM\UniqueConstraint(name="uk_asset_internal_asset_number_client_id", columns={"internal_asset_number", "client_id"})})
* #ORM\Entity(repositoryClass="Api\AssetBundle\Entity\AssetRepository")
* #UniqueEntity(fields={"internalAssetNumber"}, groups={"post", "put"}, message="There is already an asset with that internal number!")
*/
class Asset
{
/**
* #var guid
*
* #ORM\Column(name="id", type="string")
* #ORM\Id
* #ORM\GeneratedValue(strategy="UUID")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="client_id", type="string", length=255, nullable=false)
*/
private $clientId;
/**
* #var string
*
* #ORM\Column(name="internal_asset_number", type="string", length=255, nullable=true, unique=true)
*/
private $internalAssetNumber;
Update method:
public function putAssetAction(Request $request, $id)
{
$data = $this->deserializer('Api\AssetBundle\Entity\Asset', $request, 'put');
if ($data instanceof \Exception) {
return View::create(['error' => $data->getMessage()], 400);
}
$validator = $this->get('validator');
$errors = $validator->validate($data, null, 'put');
if (count($errors) > 0) {
$errorsResponse = [];
foreach ($errors as $error) {
$errorsResponse = $error->getMessage();
}
return View::create(array('error' => $errorsResponse), 400);
}
...
As #xabbuh commented, the problem is that the entity you persist after update is not retrieved through the entityManager so when you persist it the entity manager thinks it is a new entity.
Here is how to solve it:
$entityManager->merge($entity);
This will tell the entitymanager to merge your serialized entity with the managed one
Some more explanation on merge():
https://stackoverflow.com/a/15838232/5758328

Doctrine2 not update DateTime [duplicate]

This question already has answers here:
Doctrine2 ORM does not save changes to a DateTime field
(3 answers)
Closed 8 years ago.
I think I have found a bug which I can not find solution ..
I try to update the datetime field, but do not update it, don't gives me an error.
Move all other fields modifies them correctly, but the datetime field no.
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('MyOwnBundle:Events')->find($id);
$In = $entity->getDateIn();
$In->modify('+1 day');
$entity->setDateIn($In);
$em->flush();
I also tried to insert a DateTime() object directly but does not update at all!
$entity->setDateIn(new \DateTime());
Is there a solution to this problem?
I installed symfony 2.1 and doctrine 2.3.3
EDIT
Event entity:
/**
* Events
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="My\OwnBundle\Entity\EventsRepository")
*/
class Events
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=100)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="description", type="text")
*/
private $description;
/**
* #var \DateTime
*
* #ORM\Column(name="dateIn", type="datetime")
*/
private $dateIn;
/**
* #var \DateTime
*
* #ORM\Column(name="dateOut", type="datetime")
*/
private $dateOut;
....
/**
* Set dateIn
*
* #param \DateTime $dateIn
* #return Events
*/
public function setDateIn($dateIn)
{
$this->dateIn = $dateIn;
return $this;
}
/**
* Get dateIn
*
* #return \DateTime
*/
public function getDateIn()
{
return $this->dateIn;
}
/**
* Set dateOut
*
* #param \DateTime $dateOut
* #return Events
*/
public function setDateOut($dateOut)
{
$this->dateOut = $dateOut;
return $this;
}
/**
* Get dateOut
*
* #return \DateTime
*/
public function getDateOut()
{
return $this->dateOut;
}
....
The modify() method will not update the entity since Doctrine tracks DateTime objects by reference. You need to clone your existing DateTime object, giving it a new reference. Modify the new one and then set is as a new timestamp.
For more information, see the article in the Doctrine Documentation.
the entity is right, but you need to persist your entity with $em->persist($entity) and you don't need to set again the date because the datetime is passed by reference
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('MyOwnBundle:Events')->find($id);
$entity->getDateIn()->modify('+1 day');
$em->persist($entity);
$em->flush();

Symfony2 setting other rows to 0 after/before flush

Here's what I'm having trouble with.
I've a Table which contains a column called shown_on_homepage and only one row should be set to 1, the rest should all be set to 0. I'm trying to add a new row to the database and this one should be set to 1, setting the one that previously had a 1 to 0.
In MySQL I know this can be achieved by issuing an update before the insert:
UPDATE table_name SET shown_on_homepage = 0
Here's my Entity:
class FeaturedPerson {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="content", type="string", length=2500, nullable=false)
*/
private $content;
/**
* #var \DateTime
*
* #ORM\Column(name="date_updated", type="datetime")
*/
private $dateUpdated;
/**
* #var bool
*
* #ORM\Column(name="shown_on_homepage", type="boolean", nullable=false)
*/
private $isShownOnHomepage;
//...
public function getIsShownOnHomepage() {
return $this->isShownOnHomepage;
}
public function setIsShownOnHomepage($isShownOnHomepage) {
$this->isShownOnHomepage = $isShownOnHomepage;
return $this;
}
}
And for the Controller I've:
$featured = new FeaturedPerson();
$featured->setContent('Test content.');
$featured->setDateUpdated('01/02/2013.');
$featured->setIsShownOnHomepage(TRUE);
$em = $this->getDoctrine()->getManager();
$em->persist($featured);
$em->flush();
It does add the new row, but the one that had a shown_on_homepage set to 1 still has it. I've researched but I couldn't find a way to achieve this, I hope you can help me.
You could execute a query prior to your existing code in your controller:
$queryBuilder = $this->getDoctrine()->getRepository('YourBundleName:FeaturedPerson')->createQueryBuilder('qb');
$result = $queryBuilder->update('YourBundleName:FeaturedPerson', 'd')
->set('d.isShownOnHomepage', $queryBuilder->expr()->literal(0))
->where('d.isShownOnHomepage = :shown')
->setParameter('shown', 1)
->getQuery()
->execute();
Change 'YourBundleName' to your bundle name.

Resources