Reputation: 1246
I have a big (simple) problem.
I have a user entity with a geolocation property as an manyToOne relation
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
/**
* User
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Location", cascade= {"persist"})
* @ORM\JoinColumn(name="location_id", referencedColumnName="id")
*/
private $geolocation;
And I have a location Entity like this:
/**
* Location
*
* @ORM\Table(name="location")
* @ORM\Entity(repositoryClass="AppBundle\Entity\Repository\LocationRepository")
*/
class Location
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @Assert\NotBlank()
* @ORM\Column(name="latitude", type="float", scale=12, precision=18)
*/
private $latitude;
/**
* @var string
*
* @Assert\NotBlank()
* @ORM\Column(name="longitude", type="float", scale=12, precision=18)
*/
private $longitude;
/**
* @var string
*
* @Assert\NotBlank()
* @ORM\Column(name="address", type="string", length=255)
*/
private $address;
The Problem is now, that I want to change (update) the location of my users. For that I have a FormType:
$builder->add('geolocation', 'jquerygeolocation', array();
The 'jquerygeolocation' FormType is a created FormType with the
data_class' => 'AppBundle\Entity\Location'
But when I want to change (update) the users location I have a big problem. I want to persist a new location if the location is even not in the database and I want to connect an existing location with the user. But instead doctrine changes only the values of the connected location.
For example:
before:
after:
As you can see, the id is the same. There was only an update. Nothing from the logic I guessed.
Can someone help me with this.
Thanks Michael.
Upvotes: 0
Views: 153
Reputation: 1246
Very good. This was exactly, what I guessed. But the solution was not as easy like this. In addition to your solution you have to prevent the update on the old location entity. Otherwise the old value is the same as the new location. To do this, I used a Lifecycle Callback in the entity:
Location.php
/**
* @ORM\PreUpdate
*/
public function preventUpdate(PreUpdateEventArgs $args)
{
$address = $args->getOldValue('address');
$latitude =$args->getOldValue('latitude');
$longitude = $args->getOldValue('longitude');
$locality = $args->getOldValue('locality');
$country = $args->getOldValue('country');
$locationArray = array(
'address' => $address,
'latitude' => $latitude,
'longitude' => $longitude,
'locality' => $locality,
'country' => $country
);
$this->update($locationArray);
}
with the following function inside the Location.php entity:
public function update($location)
{
$data = $location;
$this->address = $data['address'] ?: null;
$this->latitude = $data['latitude'] ?: null;
$this->longitude = $data['longitude'] ?: null;
$this->locality = $data['locality'] ?: null;
$this->country = $data['country'] ?: null;
}
The problem is now, that I never can be able to update a Location. Ok, I don´t now yet if I will need it. But I would be grateful if there is another solution without this negative aspect.
Upvotes: 0
Reputation: 4957
With these relationships you would typically provide a pick list of existing locations and a separate mechanism to create a new location. With a location entity form embedded within your user form any updates will always be applied to the location entity that is already associated with the user.
If you want this to work as described in the question you will need to write some custom code in your controller (or better still in a business logic service used by the controller) to handle it.
Assuming it is the address which uniquely identifies a location then you would need something like this (after handling the request in the controller so that you have a user instance containing the submitted data):
$em = $this->getDoctrine()->getManager();
$locationRepo = $em->getRepository('AppBundle:Location');
$location = $locationRepo->findOneByAddress($user->getGeolocation()->getAddress());
if (!$location)
{
$location = new Location();
$location->setLongitude($user->getGeolocation()->getLongitude());
$location->setLatitude($user->getGeolocation()->getLatitude());
$location->setAddress($user->getGeolocation()->getAddress());
$em->persist($location);
}
$user->setLocation($location);
$em->flush($user);
Upvotes: 1