diff --git a/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst b/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst new file mode 100644 index 000000000..4aad58919 --- /dev/null +++ b/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst @@ -0,0 +1,251 @@ +Advanced field value conversion using custom mapping types +========================================================== + +.. sectionauthor:: Jan Sorgalla + +When creating entities, you sometimes have the need to transform field values +before they are saved to the database. In Doctrine you can use Custom Mapping +Types to solve this (see: :ref:`reference-basic-mapping-custom-mapping-types`). + +There are several ways to achieve this: converting the value inside the Type +class, converting the value on the database-level or a combination of both. + +This article describes the third way by implementing the MySQL specific column +type `Point `_. + +The ``Point`` type is part of the `Spatial extension `_ +of MySQL and enables you to store a single location in a coordinate space by +using x and y coordinates. You can use the Point type to store a +longitude/latitude pair to represent a geographic location. + +The entity +---------- + +We create a simple entity whith a field ``$point`` which holds a value object +``Point`` representing the latitude and longitude of the position. + +The entity class: + +.. code-block:: php + + point = $point; + } + + /** + * @return \Geo\ValueObject\Point + */ + public function getPoint() + { + return $this->point; + } + + /** + * @param string $address + */ + public function setAddress($address) + { + $this->address = $address; + } + + /** + * @return string + */ + public function getAddress() + { + return $this->address; + } + } + +We use the custom type ``point`` in the ``@Column`` docblock annotation of the +``$point`` field. We will create this custom mapping type in the next chapter. + +The point class: + +.. code-block:: php + + latitude = $latitude; + $this->longitude = $longitude; + } + + /** + * @return float + */ + public function getLatitude() + { + return $this->latitude; + } + + /** + * @return float + */ + public function getLongitude() + { + return $this->longitude; + } + } + +The mapping type +---------------- + +Now we're going to create the ``point`` type and implement all required methods. + +.. code-block:: php + + getLongitude(), $value->getLatitude()); + } + + return $value; + } + + public function convertToPHPValueSQL($sqlExpr, AbstractPlatform $platform) + { + return sprintf('AsText(%s)', $sqlExpr); + } + + public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) + { + return sprintf('PointFromText(%s)', $sqlExpr); + } + } + +We do a 2-step conversion here. In the first step, we convert the ``Point`` +object into a string representation before saving to the database (in the +``convertToDatabaseValue`` method) and back into an object after fetching the +value from the database (in the ``convertToPHPValue`` method). + +The format of the string representation format is called `Well-known text (WKT) +`_. The advantage of this format +is, that it is both human readable and parsable by MySQL. + +Internally, MySQL stores geometry values in a binary format that is not +identical to the WKT format. So, we need to let MySQL transform the WKT +representation into its internal format. + +This is where the ``convertToPHPValueSQL`` and ``convertToDatabaseValueSQL`` +methods come into play. + +This methods wrap a sql expression (the WKT representation of the Point) into +MySQL functions `PointFromText `_ +and `AsText `_ +which convert WKT strings to and from the internal format of MySQL. + +.. note:: + + When using DQL queries, the ``convertToPHPValueSQL`` and + ``convertToDatabaseValueSQL`` methods only apply to identification variables + and path expressions in SELECT clauses. Expressions in WHERE clauses are + **not** wrapped! + + If you want to use Point values in WHERE clauses, you have to implement a + :doc:`user defined function ` for + ``PointFromText``. + +Example usage +------------- + +.. code-block:: php + + getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('point', 'point'); + + // Store a Location object + use Geo\Entity\Location; + use Geo\ValueObject\Point; + + $location = new Location(); + + $location->setAddress('1600 Amphitheatre Parkway, Mountain View, CA'); + $location->setPoint(new Point(37.4220761, -122.0845187)); + + $em->persist($location); + $em->flush(); + $em->clear(); + + // Fetch the Location object + $query = $em->createQuery("SELECT l FROM Geo\Entity\Location WHERE l.address = '1600 Amphitheatre Parkway, Mountain View, CA'"); + $location = $query->getSingleResult(); + + /* @var Geo\ValueObject\Point */ + $point = $location->getPoint(); diff --git a/en/index.rst b/en/index.rst index ef2638705..b4816c73c 100644 --- a/en/index.rst +++ b/en/index.rst @@ -66,4 +66,5 @@ Cookbook cookbook/strategy-cookbook-introduction cookbook/validation-of-entities cookbook/working-with-datetime - cookbook/mysql-enums \ No newline at end of file + cookbook/mysql-enums + cookbook/advanced-field-value-conversion-using-custom-mapping-types \ No newline at end of file diff --git a/en/reference/basic-mapping.rst b/en/reference/basic-mapping.rst index 0e1ace490..90970de2e 100644 --- a/en/reference/basic-mapping.rst +++ b/en/reference/basic-mapping.rst @@ -300,6 +300,8 @@ list: - ``scale``: (optional, default 0) The scale for a decimal (exact numeric) column. (Applies only if a decimal column is used.) +.. _reference-basic-mapping-custom-mapping-types: + Custom Mapping Types --------------------