📜 ⬆️ ⬇️

We introduce work with coordinates in sonata-admin

Good day,% habrauser%!

Recently, there was a task to store in the GPS database with the further possibility of using various geometric functions of mysql. Coordinates should be managed from sonata-admin. What came out of this can be read under the cut.

The first thing you had to do was to find a suitable extension for the doctrine. After spending some time on googling, I found the doctrine2-mysql-spatial library (you can find versions with pgsql included in the forks). Everything is good in it, but there is no support for ST_ functions (mysql 5.6 or higher). Without hesitation, I made a fork , added the necessary functions, and built a package for the compositor. I will not dwell on the installation and configuration of the library, everything is banal and is in the installation file.
')
The second is the setting of the sonata. By some manipulations with the use of magic share, it was possible to realize the output and storage of data for the corresponding entity.
In the entity class we need one fake field for pre / post coordinate processing, a getter and a setter. In adminclass class, overload the create and update methods. So, let's begin.

The coordinates will be stored in the field type polygon. An example description of an entity is as follows:
show code
#Bundle\Resources\config\doctrine\Location.orm.yml Location: type: entity table: Location repositoryClass: LocationRepository id: id: type: integer nullable: false generator: strategy: AUTO fields: title: type: string length: 500 nullable: false area: type: polygon nullable: false 


After generation, open the resulting entity class and add there:
show code
 //Bundle\Entity\Location.php private $__area; const COORDS_SEPARATOR = '; '; const POLYGON_SEPARATOR = '[]'; public function get_Area($raw = false) { if ($raw) { return $this->__area; } $result = array(); if (is_null($this->getArea())) { return $result; } $rings = $this->getArea()->getRings(); $count_rings = count($rings) -1; foreach ($rings as $key => $line) { foreach ($line->getPoints() as $point) { $result[] = $point->getX() . self::COORDS_SEPARATOR . $point->getY(); } if($count_rings != $key) { $result[] = Task::POLYGON_SEPARATOR; } } return $result; } public function set_Area($__area) { $this->__area = $__area; return $this; } 


Next, open the admin class, which is responsible for the output of the desired entity in the sonata. We go to the configureFormFields method (FormMapper $ formMapper) and add our field, which will be responsible for working with coordinates:
show code
  $formMapper .... ->add('__area','sonata_type_native_collection',[ 'options'=>['label'=>'(GpsX;GpsY)'], 'allow_add'=>true, 'allow_delete'=>true, 'label' => ' ' ]) .... 


We also overload the methods of the base class:
show code
 public function update($object) { $object = $this->prepareTask($object); return parent::update($object); } public function create($object) { $object = $this->prepareTask($object); return parent::create($object); } protected function prepareTask($object) { $res = array(); if (count($object->get_Area(true))) { $flb = $this->getConfigurationPool()->getContainer()->get('session')->getFlashBag(); $i = 0; foreach ($object->get_Area(true) as $point) { if((string) $point === Task::POLYGON_SEPARATOR) { if(count($res[$i]) > 2) { $this->fillLastPoint($res[$i]); } $i++; continue; } if (!preg_match('/[\d]+[.]{0,1}[\d]{0,}' . preg_quote(Task::COORDS_SEPARATOR) . '[\d]+[.]{0,1}[\d]{0,}/', (string) $point)) { $flb->add( 'danger', '    ' . (string) $point . '.       ( .)  ";"' ); return $object; } list($x, $y) = explode(Task::COORDS_SEPARATOR, $point); $res[$i][] = new Point($x, $y); } foreach ($res as $key => $ring) { if(count($ring) < 3) { $flb->add( 'danger', "  №" . $key + 1 . "   , ..      3 ,    " . count($ring) . '.' ); unset($res[$key]); continue; } } if(count($res)) { end($res); $key = key($res); $this->fillLastPoint($res[$key]); $object->setArea(new Polygon($res)); } } return $object; } private function fillLastPoint(&$arr) { if ($arr[0]->getX() !== end($arr)->getX() || $arr[0]->getY() !== end($arr)->getY()) { $arr[] = $arr[0]; } } 



The result looks like this:
show picture


In the repository class, you can use the following query:
show code
  $query = $this->getEntityManager() ->createQuery( 'SELECT t FROM Bundle:Location t where ST_CONTAINS(t.area, Point(:x,:y)) = 1' ) ->setParameter('x', $x) ->setParameter('y', $y) ; $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'CrEOF\Spatial\ORM\Query\GeometryWalker'); $result = $query->getResult(); 


Thank you all for your attention. Text and code prepared in conjunction with scooty habraiser

Source: https://habr.com/ru/post/240493/


All Articles