Dor Yan
Dor Yan

Reputation: 19

calculated/virtual field in symfony entity - how to do it?

I have first entity called "places" that maps places data like place name,latitude and longitude. Another entity called "users" stores the user data like username,user current latitude,user current longitude.

I want to add a virtual field for the "places" entity that calculate the distance between the geo points.

I'm using this function -

class Places{

/**
 * @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=255)
 * @Assert\NotBlank()
 * @Expose
 */
private $name;

/**
 * @var string
 * @ORM\Column(name="description", type="string", length=255, nullable=true)
 * @Expose
 */
private $description;

/**
 * @var float
 * @ORM\Column(name="lon", type="float", nullable=true)
 * @Expose
 */
private $lon;

/**
 * @var float
 * @ORM\Column(name="lat", type="float", nullable=true)
 * @Expose
 */
private $lat;

/**
 * @var integer
 * @ORM\ManyToOne(targetEntity="Countries")
 * @ORM\JoinColumn(name="country", referencedColumnName="id", onDelete="SET NULL")
 * @Assert\NotBlank()
 * @Expose
 */
private $country;

/**
 * @var string
 * @VirtualProperty
 * @Type("string")
 * @SerializedName("distance")
 * @return string
 */
public function distance(){

    $longitude1 = '34.7722';//$user->getLon();(?)
    $latitude1 = '32.0114';//$user->getLat();(?)
    $longitude2 = $this->getLon();
    $latitude2  = $this->getLat();

    $theta = $longitude1 - $longitude2;
    $miles = (sin(deg2rad($latitude1)) * sin(deg2rad($latitude2))) + (cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * cos(deg2rad($theta)));
    $miles = acos($miles);
    $miles = rad2deg($miles);
    $miles = $miles * 60 * 1.1515;
    $kilometers = $miles * 1.609344;
    //return round($kilometers);
    return $this->user->getLastName();
}
}

but as you can see the lon1/lat1 are hard coded because i can't get the user lan/lat from with in the places entity.

How do i do it?

Thanks.

Upvotes: 0

Views: 5253

Answers (3)

Harold
Harold

Reputation: 699

You can pass $place entity to your function:

public function distance($place)
{
      $longitude1 = $place->getLon();
      $latitude1 = $place->getLat();
      $longitude2 = $this->getLon();
      $latitude2  = $this->getLat();

      $theta = $longitude1 - $longitude2;
      $miles = (sin(deg2rad($latitude1)) * sin(deg2rad($latitude2))) + (cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * cos(deg2rad($theta)));
      $miles = acos($miles);
      $miles = rad2deg($miles);
      $miles = $miles * 60 * 1.1515;
      $kilometers = $miles * 1.609344;

      return round($kilometers);
}

Upvotes: 1

h3llr4iser
h3llr4iser

Reputation: 236

you do not have any relations between place and user, you cant do that inside place entity. Assuming that user is the logged user. Your logic has to be in a service and inject TokenStorage to get the user logged:

namespace Acme\Bundle\AcmeBundle\Manager;

use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Acme\Bundle\AcmeBundle\Entity\Location;


class LocationFinder{
/*
 * @var TokenStorageInterface
 */
private $tokenStorage;

public function __construct(TokenStorageInterface $tokenStorage)
{
    $this->tokenStorage = $tokenStorage;
}

public function getDistance(Location $location)
{
    $longitude1 =$this->tokenStorage->getToken()->getUser()->getLon();
    $latitude1 = $this->tokenStorage->getToken()->getUser()->getLat();

    $longitude2 = $this->getLon();
    $latitude2  = $this->getLat();

    $theta = $longitude1 - $longitude2;
    $miles = (sin(deg2rad($latitude1)) * sin(deg2rad($latitude2))) + (cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * cos(deg2rad($theta)));
    $miles = acos($miles);
    $miles = rad2deg($miles);
    $miles = $miles * 60 * 1.1515;
    $kilometers = $miles * 1.609344;
    return round($kilometers);

}

}

Then you have to create the service inside Resources\config\services.xml

<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
    <service id="acme.manager.location_finder" class="Acme\Bundle\AcmeBundle\Manager\LocationFinder">
        <argument type="service" id="security.token_storage" />
    </service>
</services>
</container>

now you can get the LocationService inside a controller and get the distance between place and the user logged

class AccountController extends Controller
{

/**
 * @Route("/place/{id}", name="place")
 * @Method("GET")
 */
public function placeAction($id)
{
    $em = $this->getDoctrine()->getEntityManager();
    $location = $em->getRepository('AcmeBundle:Location')->findOneBy([
        'id' => $id
    ]);
    $distance = $this->get('acme.manager.location_finder')->getDistance($location);
}

}

You have more information here: http://symfony.com/doc/current/book/service_container.html

Sf2 : using a service inside an entity

Upvotes: 0

Dor Yan
Dor Yan

Reputation: 19

I got it!! Managed to solve this problem using an event listener .

listener:

class SearchIndexer{

        public function __construct(TokenStorageInterface  $tokenStorage)
        {
            $this->tokenStorage = $tokenStorage;
        }

        public function postLoad (LifecycleEventArgs $args){
            $entity = $args->getEntity();
            if ($entity instanceof Places) {
                $user = $this->tokenStorage->getToken()->getUser();
                $entity->setUser_longitude($user->getLastGeoLocationLon());
                $entity->setUser_latitude($user->getLastGeoLocationLat());
            }
        }

    }

services:

services:
   doctrine.listener:
      class: ApplicationBundle\Listener\SearchIndexer
      arguments: ["@security.token_storage"]
      tags:
          - { name: doctrine.event_listener, event: postLoad }

places entity:

class Places
{
  private $user_longitude;

        public function setUser_longitude($user_longitude){
            $this->user_longitude = $user_longitude;
        }

        private $user_latitude;

        public function setUser_latitude($user_latitude){
            $this->user_latitude = $user_latitude;
        }

        /**
         * @var string
         * @VirtualProperty
         * @Type("string")
         * @SerializedName("distance")
         * @return string
         */
        public function distance(){

            $longitude2 = $this->getLon();
            $latitude2  = $this->getLat();

            $theta = $this->user_longitude - $longitude2;
            $miles = (sin(deg2rad($this->user_latitude)) * sin(deg2rad($latitude2))) + (cos(deg2rad($this->user_latitude)) * cos(deg2rad($latitude2)) * cos(deg2rad($theta)));
            $miles = acos($miles);
            $miles = rad2deg($miles);
            $miles = $miles * 60 * 1.1515;
            $kilometers = $miles * 1.609344;
            return round($kilometers);

        }
 //rest of the class
}

Upvotes: 1

Related Questions