I've already search a lot befors asking, even the related topic Symfony2-Doctrine: ManyToMany relation is not saved to database
but still no answer.
I've got this two classes:
class Intervenant extends User
{
/**
* #ManyToMany(targetEntity="iMDEO\DISAASBundle\Entity\Domaine", inversedBy="intervenants", cascade={"persist","merge"})
*/
private $domaines;
/**
* Add domaines
*
* #param Domaine $domaines
*/
public function addDomaine(Domaine $domaines)
{
$this->domaines[] = $domaines;
}
/**
* Get domaines
*
* #return Doctrine\Common\Collections\Collection
*/
public function getDomaines()
{
return $this->domaines;
}
}
class Domaine
{
// ...
/**
* #ORM\ManyToMany(targetEntity="Intervenant", mappedBy="domaines", cascade={"persist","merge"})
*
*/
private $intervenants;
/**
* Add intervenants
*
* #param Intervenant $intervenants
*/
public function addIntervenant(Intervenant $intervenants)
{
$intervenants->addDomaine($this);
$this->intervenants[] = $intervenants;
}
/**
* Get intervenants
*
* #return Doctrine\Common\Collections\Collection
*/
public function getIntervenants()
{
return $this->intervenants;
}
}
When I save an Intervenant, everthing is OK.
But when i save the inverse side Domaine, the changes are not persisted.
Reading Symfony's doc and topics everywhere, I can't find any solution to get a bi-directionnal relation between my two entities.
Here's part of my DomaineController:
$em = $this->getDoctrine()->getEntityManager();
$entity = $em->getRepository('myBundle:Domaine')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Domaine entity.');
}
$editForm = $this->createForm(new DomaineType(), $entity);
$deleteForm = $this->createDeleteForm($id);
$request = $this->getRequest();
$editForm->bindRequest($request);
if ($editForm->isValid()) {
$em->persist($entity);
$em->flush();
return $this->indexAction();
}
// ...
My purpose is that when I create/edit an Intervenant, I can choose related Domaine.
And when I create/edit a Domaine, I link every Intervenants in it.
Could you please help me?
Related
I have a ThirdPartyEntity from a third party bundle that, using a ThirdPartyEntityTrait, I link to MyEntity in my project.
Now, as the ThirdPartyEntity is not set a ApiResource nor as an ApiSubresource and as I don't have any serializaton group set on MyEntity, when I get MyEntity from ApiPlatform, it returns me something like this:
{
"#id":"/api/my_entities/17",
"#type":"MyEntity",
"id":17,
"third_party_entity": {
"id":22,
"a_property":"some value"
}
}
BUT IF I PUT a changed value for a_property with this body:
{
"#id":"/api/my_entities/17",
"#type":"MyEntity",
"id":17,
"third_party_entity": {
"id":22,
"a_property":"some NEW value to update"
}
}
I get a new third_party_entity to be created and get this response:
{
"#id":"/api/my_entities/17",
"#type":"MyEntity",
"id":17,
"third_party_entity": {
"id":23,
"a_property":"some NEW value to update"
}
}
SO, HOW CAN I UPDATE third_party_entity instead of creating it each time?
HERE THERE ARE THE INVOLVED CLASSES AND TRAITS
/**
* #ORM\Table(name="app_my_entities")
* #ORM\Entity()
* #ApiResource()
*/
class MyEntity
{
// !!!!!!!!!!!!!!!!!!
// This is the trait I use to link MyEntity
// with the entity from the third-party bundle
// !!!!!!!!!!!!!!!!!!
use ThirdPartyEntityTrait;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
...
}
And this is the ThirdPartyEntityTrait:
trait ThirdPartyEntityTrait
{
/**
* #ORM\OneToOne(targetEntity="Namespace\To\Bundle\Entity\ThirdPartyEntity", cascade={"all"})
* #ORM\JoinColumn(name="thirdPartyEntity", referencedColumnName="id")
*/
private $thirdPartyEntity;
/**
* #param thirdPartyEntity $thirdPartyEntity
*
* #return ThirdPartyEntity
*/
public function setThirdPartyEntity(thirdPartyEntity $thirdPartyEntity): ThirdPartyEntity
{
$this->thirdPartyEntity = $thirdPartyEntity;
/** #var ThirdPartyEntity $this */
return $this;
}
/**
* #return thirdPartyEntity
*/
public function getThirdPartyEntity(): ?thirdPartyEntity
{
return $this->thirdPartyEntity;
}
/**
* #return thirdPartyEntity
*/
public function removeThirdPartyEntity(): ?thirdPartyEntity
{
$thirdPartyEntity = $this->getThirdPartyEntity();
$this->thirdPartyEntity = null;
return $thirdPartyEntity;
}
}
As you can see, nothing more a property to save the relation and some accessors methods.
This is, instead, the linked Entity:
/**
* #ORM\Entity()
* #ORM\Table(name="third_party_entities")
*/
class ThirdPartyEntity
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #ORM\Column(name="aProperty", type="string", nullable=true)
*/
private $aProperty;
public function getId()
{
return $this->id;
}
public function getAProperty()
{
return $this->aProperty;
}
public function setAProperty($aProperty)
{
$this->aProperty = $aProperty;
return $this;
}
}
This question is cross-posted also on GitHub.
The solution was pretty simple: use another config method!
Practically, it is possible to mix configuration types and so, it is possible to use the annotations along with the yaml configuration.
Given this, it is sufficient to create a new config file in config/api_platform/third_party_entity.yaml.
In it put the configuration required to map the entity from the third party bundle:
resources:
App\Entity\MyEntity:
properties:
remote:
subresource:
resourceClass: 'Third\Party\Bundle\TheBundle\Entity\ThirdPartyEntity'
Third\Party\Bundle\TheBundle\Entity\ThirdPartyEntity:
This way it is possible to configure as subresource the entity from the third party bundle to which we don't have access with annotations.
I'm newbie in symfony and I would like some advices for creating a query builder statement. Is pretty simple, the idea is to get the data from a third entity.
I have the entities:
/**
* Entity
* #ORM\Entity(repositoryClass="LoungepassBundle\Entity\LoungepassRepository")
* #ORM\Table(name="loungepass_loungepass")
*
*/
class Loungepass{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
// ...
/**
* #ORM\ManyToOne(targetEntity="\AppBundle\Entity\Agency", inversedBy="loungepasses")
* #ORM\JoinColumn(name="agency_id", referencedColumnName="iata8", nullable=false)
*/
private $agency;
//...
}
Agency Entity
class Agency {
/**
* #ORM\Id
* #ORM\Column(type="string", length=8, name="iata8")
*/
protected $id;
//...
/**
* #ORM\OneToMany(targetEntity="LoungepassBundle\Entity\Loungepass", mappedBy="agency")
*/
protected $loungepasses;
/**
* #var Market
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Market")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="sales_country", referencedColumnName="id")
* })
*/
private $market;
//...
}
I would like to do a query, like this:
public function queryBySlugInContext($slug, $user) {
$query = $this->createQueryBuilder("l")
->where('l.slug = :slug')
->setParameter('slug', $slug);
if(count($user->getAgencies()) > 0){
$query->andWhere(':agencyIds MEMBER OF l.agencies')
->setParameter('agencyIds',$user->getAgencies());
}
Able to access the information from the market attribute located in the agency entity.So basically, retrieve information from the Market Entity.
any suggestions?
Thank you in advance
I modified a bit your code but the mind is here
When you have to access to retrieve data from other tables just keep in mind to use differents mysql join accessible with doctrine
public function queryBySlugInContext($slug, $user) {
$query = $this->createQueryBuilder("l")
->where('l.slug = :slug')
->setParameter('slug', $slug);
if(0 !== count($user->getAgencies())) {
$query = $query->innerJoin('l.agency', 'a')
->andWhere('a.id IN(:userAgenciesIds)')
->setParameter('userAgenciesIds', array_map(function(Agency $agency) {
return $agency->getId();
}, $user->getAgencies());
}
You should take a time to read some content on internet like for example the doctrine query builder documentation
I need to implement an API with a PUT method and I would like to use the ParamConverter in my Controller to find an existing entity object, or if the entity object doesn't exist, to create a new one.
However the standard Symfony ParamConverter returns an exception if it doesn't find the entity object in the repository.
Do you have any ideas to do that in a nice and clean way ? Thx.
Here is an example of what I would like to do (I use FOS REST Bundle to handle the PUT request):
/**
* #param Request $request
* #return View
*
* #ParamConverter("video")
*
*/
public function putVideosAction(Request $request, Video $video)
{
try {
return $this->getHandlerVideos()->put($video, $request->request->all());
} catch (InvalidFormException $e) {
return $e->getForm();
}
}
Here's a solution. Please give me your thoughts on it.
In your controller, I would do that:
/**
* #param Request $request
* #return View
*
* #Rest\Put()
* #Rest\View()
*
* #ParamConverter("video", converter="app_get_or_create_entity_converter", options={"repository_method" = "findOneById"})
*/
public function putVideosAction(Request $request, Video $video)
{
try {
$video = $this->getHandlerVideos()->put($video, $request->request->all());
return $video;
} catch (InvalidFormException $e) {
return $e->getForm();
}
}
I would write a dynamic param converter that way:
class GetOrCreateEntityConverter implements \Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface
{
/**
* #var EntityManagerInterface
*/
protected $entityManager;
/**
* #var ManagerRegistry $registry Manager registry
*/
private $registry;
/**
* #param ManagerRegistry $registry
* #param EntityManagerInterface $entityManager
*/
public function __construct(ManagerRegistry $registry, EntityManagerInterface $entityManager)
{
$this->registry = $registry;
$this->entityManager = $entityManager;
}
public function supports(ParamConverter $configuration)
{
if ('app_get_or_create_entity_converter' !== $configuration->getConverter()) {
return false;
}
return true;
}
/**
* {#inheritdoc}
*
* Applies converting
*
* #throws \InvalidArgumentException When route attributes are missing
* #throws NotFoundHttpException When object not found
*/
public function apply(Request $request, ParamConverter $configuration)
{
$name = $configuration->getName();
$options = $configuration->getOptions();
$class = $configuration->getClass();
$repository = $this->entityManager->getRepository($class);
$repositoryMethod = $options['repository_method'];
if (!is_callable([$repository, $repositoryMethod])) {
throw new \BadMethodCallException($repositoryMethod . ' function does not exist.', 405);
}
$entity = $repository->$repositoryMethod($id);
if (null === $entity) {
$entity = new $class;
}
$request->attributes->set($name, $entity);
}
}
If you ask why I return a form in the catch, please go and see https://github.com/liuggio/symfony2-rest-api-the-best-2013-way/blob/master/src/Acme/BlogBundle/Controller/PageController.php
You'll have to create your own custom paramConverter.
First, here is what you want to write in your controller:
/**
* #ParamConverter("video", class = "MyBundle:Video", converter = "my_param_converter")
* #param Request $request
* #param Video $video
* #return \Symfony\Component\HttpFoundation\Response
*/
public function putVideosAction(Request $request, Video $video)
{
// your code..
}
Now let's write the my_param_converter!
use Doctrine\Common\Persistence\ManagerRegistry;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface;
// ...
class MyParamConverter implements ParamConverterInterface
{
private $registry;
/**
* #param ManagerRegistry $registry
*/
public function __construct(ManagerRegistry $registry = null)
{
$this->registry = $registry;
}
/**
* Check if object supported by our paramConverter
*
* #param ParamConverter $configuration
*/
public function supports(ParamConverter $configuration)
{
// In this case we can do nothing and just return
if (null === $this->registry || !count($this->registry->getManagers())) {
return false;
}
// Check if the class is set in configuration
if(null === $configuration->getClass()) {
return false;
}
// Get actual entity manager for class
$em = $this->registry->getManagerForClass($configuration->getClass());
// Check what you need to check...
return true;
}
public function apply(Request $request, ParamConverter $configuration)
{
$videoId = $request->attributes->get('video');
if(null === videoId) {
throw new \InvalidArgumentException('Route attribute is missing');
}
// Get actual entity manager for class
$em = $this->registry->getManagerForClass($configuration->getClass());
$repository = $em->getRepository($configuration->getClass());
// Try to find the video
$video = $$repository->findOneById($videoId);
if($video === null || !($video instanceof Video)) {
// Here you can create your new video object
}
// Map video to the route's parameter
$request->attributes->set($configuration->getName(), $video);
}
}
Once your new paramConverter wrote, declare it as a service:
services:
app.param_converter.my_param_converter:
class: YourBundle\Path\To\MyParamConverter
tags:
- { name: request.param_converter, converter: my_param_converter }
arguments:
- #?doctrine
Here you're done!
My answer is largely inspired by this article and hope is helpful.
I have entity and proper mapping with them. PFB
/**
* User
*
* #ORM\Entity
* #ORM\Table(name="user")
*/
class User{
/**
* #ORM\OneToOne(targetEntity="Language", mappedBy="languageCode2")
**/
private $languageObj;
function __construct(){
$this->languageObj = new ArrayCollection();
}
}
/**
* Language
*
* #ORM\Entity
* #ORM\Table(name="language")
*/
class Language
{
/**
* #ORM\OneToOne(targetEntity="User", inversedBy="languageObj")
* #ORM\JoinColumn(name="language_id", referencedColumnName="language_id")
*/
private $languageCode2;
public function __construct()
{
$this->languageCode2 = new ArrayCollection();
}
}
when I print it It gives below
[languageObj:User:private] => Common\User\Entity\Language Object
(
[languageId:Language:private] => 1
[languageCode:Language:private] => en
[languageName:Language:private] => English
[languageCode2:Language:private] => User Object
what issue I am facing is, i am not able to fetch languageCode from Language object
To do that I have written below in __construct() of User class
$this->lc = $this->languageObj->first();
AND
$this->lc = $this->languageObj->getCode();
getCode() is method in Language class, which return $this->languageCode variable
Here is my problem : I have the 3 entities Item, User and Link above (these classes also have the usual getters and setters).
class User {
//...
/*
* #ORM\OneToMany(targetEntity="Link", mappedBy="user", cascade={"persist", "remove"})
*
*/
protected $links;
//...
}
class Item {
//...
/*
* #ORM\OneToMany(targetEntity="Link", mappedBy="item", cascade={"persist", "remove"})
*
*/
protected $links;
//...
}
class Link {
/**
* #var datetime $time
*
* #ORM\Column(name="time", type="datetime")
*/
private $time;
/**
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Item", inversedBy="links")
* #ORM\JoinColumn(name="item_id", referencedColumnName="id")
*/
private $item;
/**
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="User", inversedBy="links")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
//...
}
I did not use a ManyToMany relationship because of the $time property in the Link class.
When I create a Link, I do it this way :
$link = getExistingLink($item, $user);
if (!$link) {
$link = new Link();
$link->setItem($item);
$link->setUser($user);
}
$link->setTime(new \DateTime());
$em = $this->getEntityManager();
$em->persist($link);
$em->flush();
The data is written in the database, however when I call $user->getLinks(), it returns NULL. I event tried to do this :
$user->addLink($link);
$em->persist($user);
$em->flush();
But the link won't be loaded the next time the $user will be loaded.
Any idea why the Link entities are not loaded ?
OK Problem Solved.
The annotations starts with /* instead of /** in both User and Item classes.
Just a silly mistake ...