{"id":1679,"date":"2025-02-27T15:17:02","date_gmt":"2025-02-27T15:17:02","guid":{"rendered":"https:\/\/editjournal.redakt.eu\/faxmodem\/?p=1679"},"modified":"2025-02-27T15:17:35","modified_gmt":"2025-02-27T15:17:35","slug":"read-write-geospatial-point","status":"publish","type":"post","link":"https:\/\/editjournal.redakt.eu\/faxmodem\/blog\/development\/cakephp\/read-write-geospatial-point\/","title":{"rendered":"Reading and writing geospatial in CakePHP 5.1+"},"content":{"rendered":"<p>Limited support for SQL geospatial types was <a href=\"https:\/\/github.com\/cakephp\/cakephp\/pull\/17733\">added<\/a> in CakePHP 5.1.0. To quote <a href=\"https:\/\/book.cakephp.org\/5\/en\/orm\/database-basics.html#geospatial-types\">the doc<\/a>:<\/p>\n<blockquote>\n<p>The <code>geometry<\/code>, <code>point<\/code>, <code>linestring<\/code>, and <code>polygon<\/code> types are also known as the \u201cgeospatial types\u201d. CakePHP offers limited support for geospatial columns. Currently they can be defined in migrations, read in schema reflection, and have values set as text.<\/p>\n<\/blockquote>\n<h3>Creating a POINT column<\/h3>\n<p>Using <code>point<\/code> as an example, your database table <code>MyTable<\/code> needs to have a column <code>position<\/code> with type <code>POINT<\/code>. You will then have to have some additional processing for reading and writing while the built-in support is limited.<\/p>\n<p>Here's how you doublecheck if CakePHP understands your column type correctly:<\/p>\n<pre><code>debug($myTable-&gt;getSchema()-&gt;getColumn(&#039;position&#039;));<\/code><\/pre>\n<pre><code>[\n  &#039;type&#039; =&gt; &#039;point&#039;,\n  &#039;length&#039; =&gt; null,\n  &#039;null&#039; =&gt; true,\n  &#039;default&#039; =&gt; null,\n  &#039;comment&#039; =&gt; &#039;&#039;,\n  &#039;precision&#039; =&gt; null,\n  &#039;srid&#039; =&gt; null,\n]<\/code><\/pre>\n<p>If you just created the column, you may have to do <code>bin\/cake cache clear_all<\/code>.<\/p>\n<h3>Reading POINT<\/h3>\n<p>As per <a href=\"https:\/\/stackoverflow.com\/questions\/52742813\/how-to-unpack-mysql-multipoint-geometry-data-in-php\">https:\/\/stackoverflow.com\/questions\/52742813\/how-to-unpack-mysql-multipoint-geometry-data-in-php<\/a>: <\/p>\n<pre><code>$position = unpack(&#039;x\/x\/x\/x\/corder\/Ltype\/dx\/dy&#039;, $entity-&gt;position);\ndebug($position);<\/code><\/pre>\n<pre><code>[\n    &#039;order&#039; =&gt; (int) 1,\n    &#039;type&#039; =&gt; (int) 1,\n    &#039;x&#039; =&gt; (float) 12.34,\n    &#039;y&#039; =&gt; (float) -56.78,\n]<\/code><\/pre>\n<p>You may want to do this post-processing in a <a href=\"https:\/\/book.cakephp.org\/5\/en\/orm\/entities.html#creating-virtual-fields\">Virtual Field<\/a> on your Entity. This way the geospatial data is already parsed whenever you need it.<\/p>\n<h3>Writing POINT<\/h3>\n<p>To save our POINT, we need to pass it through the database function <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.4\/en\/gis-wkt-functions.html\"><code>ST_GeomFromText()<\/code><\/a>. Because CakePHP doesn't come with all possible database functions built in, we're going to use a query expression with <a href=\"https:\/\/book.cakephp.org\/5\/en\/orm\/query-builder.html#custom-functions\">a custom function<\/a> which is a magic wrapper.<\/p>\n<pre><code>\/\/ Take the $entity we have and patch it with request data\n$entity = $myTable-&gt;patchEntity($entity, $this-&gt;getRequest()-&gt;getData());\n\/\/ Specifically set the position column using query expression\n$entity-&gt;position = $myTable-&gt;query()-&gt;func()-&gt;ST_GeomFromText([\n    &quot;&#039;POINT(0.123 -20.00)&#039;&quot; =&gt; &#039;literal&#039;, \/\/ single quotes are important\n]);\n\/\/ Save the data\n$result = $myTable-&gt;save($entity);<\/code><\/pre>\n<ul>\n<li><a href=\"https:\/\/github.com\/cakephp\/cakephp\/pull\/17733\">https:\/\/github.com\/cakephp\/cakephp\/pull\/17733<\/a><\/li>\n<li><a href=\"https:\/\/book.cakephp.org\/5\/en\/orm\/database-basics.html#geospatial-types\">https:\/\/book.cakephp.org\/5\/en\/orm\/database-basics.html#geospatial-types<\/a><\/li>\n<li><a href=\"https:\/\/book.cakephp.org\/5\/en\/orm\/query-builder.html#custom-functions\">https:\/\/book.cakephp.org\/5\/en\/orm\/query-builder.html#custom-functions<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>CakePHP 5.1.0 introduced support for geospatial types, but for now it&#8217;s limited. Here&#8217;s how you can read and write these values easily.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1047],"tags":[715,720],"class_list":["post-1679","post","type-post","status-publish","format-standard","hentry","category-cakephp","tag-cakephp","tag-mysql","entry"],"_links":{"self":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts\/1679","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/comments?post=1679"}],"version-history":[{"count":2,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts\/1679\/revisions"}],"predecessor-version":[{"id":1681,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts\/1679\/revisions\/1681"}],"wp:attachment":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/media?parent=1679"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/categories?post=1679"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/tags?post=1679"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}