sonata-adminでの座標を使用した作業の実装

こんにちは、%habrauser%!



最近、さまざまなmysql幾何関数を使用する可能性を備えたデータベースにGPSデータを保存するタスクが発生しました。 座標は、sonata-adminから管理する必要があります。 これに由来するものは、カットの下で読むことができます。



私が心配しなければならなかった最初のことは、教義にふさわしい拡張を見つけることでした。 しばらく時間をかけてグーグルで調べた結果、Doctrine2 -mysql-spatialライブラリを見つけました(フォークでpgsqlを含むバージョンを見つけることができます)。 そのすべてが優れていますが、ST_関数(mysql 5.6以降)のサポートはありません。 ためらうことなく、彼はフォークを作り、必要な機能を追加し、作曲家用のパッケージを作成しました。 ライブラリのインストールと設定にこだわるつもりはありません。すべてが正常であり、 インストールファイルに含まれています。



2番目はソナタのチューニングです。 魔法の断片を使用したいくつかの操作により、対応するエンティティのデータの出力と保存を実現することができました。

エンティティクラスでは、座標、ゲッター、セッターの事前/事後処理用に1つの偽フィールドが必要です。 adminクラスで、作成メソッドと更新メソッドをオーバーロードします。 それでは始めましょう。



座標は、ポリゴンタイプのフィールドに格納されます。 エンティティのサンプルの説明は次のようになります。

コードを表示
#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
      
      







生成後、結果のエンティティクラスを開き、そこに追加します。

コードを表示
 //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; }
      
      







次に、管理クラスを開きます。このクラスは、ソナタ内の目的のエンティティの結論を担当します。 configureFormFieldsメソッド(FormMapper $ formMapper)に移動して、座標の操作を担当するフィールドを追加します。

コードを表示
  $formMapper .... ->add('__area','sonata_type_native_collection',[ 'options'=>['label'=>'(GpsX;GpsY)'], 'allow_add'=>true, 'allow_delete'=>true, 'label' => ' ' ]) ....
      
      







また、基本クラスのメソッドをオーバーロードします。

コードを表示
 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]; } }
      
      









結果は次のようになります。

写真を見せて




リポジトリクラスでは、次のクエリを使用できます。

コードを表示
  $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();
      
      







ご清聴ありがとうございました。 テキストとコードはscootyとともに準備されます



All Articles