public function handle(CriteriaConverter $converter, SelectQuery $query, Criterion $criterion, array $languageSettings)
{
$fieldDefinitionIds = $this->getFieldDefinitionIds($criterion->target);
$subSelect = $query->subSelect();
/** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Value\MapLocationValue $location */
$location = $criterion->valueData;
/*
* Note: this formula is precise only for short distances.
* @todo if ABS function was available in Zeta Database component it should be possible to account for
* distances across the date line. Revisit when Doctrine DBAL is introduced.
*/
$longitudeCorrectionByLatitude = pow(cos(deg2rad($location->latitude)), 2);
$distanceExpression = $subSelect->expr->add($subSelect->expr->mul($subSelect->expr->sub($this->dbHandler->quoteColumn('latitude', 'ezgmaplocation'), $subSelect->bindValue($location->latitude)), $subSelect->expr->sub($this->dbHandler->quoteColumn('latitude', 'ezgmaplocation'), $subSelect->bindValue($location->latitude))), $subSelect->expr->mul($subSelect->expr->sub($this->dbHandler->quoteColumn('longitude', 'ezgmaplocation'), $subSelect->bindValue($location->longitude)), $subSelect->expr->sub($this->dbHandler->quoteColumn('longitude', 'ezgmaplocation'), $subSelect->bindValue($location->longitude)), $subSelect->bindValue($longitudeCorrectionByLatitude)));
switch ($criterion->operator) {
case Criterion\Operator::IN:
case Criterion\Operator::EQ:
case Criterion\Operator::GT:
case Criterion\Operator::GTE:
case Criterion\Operator::LT:
case Criterion\Operator::LTE:
$operatorFunction = $this->comparatorMap[$criterion->operator];
$distanceInDegrees = pow($this->kilometersToDegrees($criterion->value), 2);
$distanceFilter = $subSelect->expr->{$operatorFunction}($distanceExpression, $subSelect->expr->round($subSelect->bindValue($distanceInDegrees), 10));
break;
case Criterion\Operator::BETWEEN:
$distanceInDegrees1 = pow($this->kilometersToDegrees($criterion->value[0]), 2);
$distanceInDegrees2 = pow($this->kilometersToDegrees($criterion->value[1]), 2);
$distanceFilter = $subSelect->expr->between($distanceExpression, $subSelect->expr->round($subSelect->bindValue($distanceInDegrees1), 10), $subSelect->expr->round($subSelect->bindValue($distanceInDegrees2), 10));
break;
default:
throw new RuntimeException('Unknown operator.');
}
// Calculate bounding box if possible
// @todo consider covering operators EQ and IN as well
$boundingConstraints = array();
switch ($criterion->operator) {
case Criterion\Operator::LT:
case Criterion\Operator::LTE:
$distanceUpper = $criterion->value;
break;
case Criterion\Operator::BETWEEN:
$distanceUpper = $criterion->value[0] > $criterion->value[1] ? $criterion->value[0] : $criterion->value[1];
break;
}
if (isset($distanceUpper)) {
$boundingConstraints = $this->getBoundingConstraints($subSelect, $location, $distanceUpper);
}
$subSelect->select($this->dbHandler->quoteColumn('contentobject_id'))->from($this->dbHandler->quoteTable('ezcontentobject_attribute'))->innerJoin($this->dbHandler->quoteTable('ezgmaplocation'), $subSelect->expr->lAnd(array($subSelect->expr->eq($this->dbHandler->quoteColumn('contentobject_version', 'ezgmaplocation'), $this->dbHandler->quoteColumn('version', 'ezcontentobject_attribute')), $subSelect->expr->eq($this->dbHandler->quoteColumn('contentobject_attribute_id', 'ezgmaplocation'), $this->dbHandler->quoteColumn('id', 'ezcontentobject_attribute'))), $boundingConstraints))->where($subSelect->expr->lAnd($subSelect->expr->eq($this->dbHandler->quoteColumn('version', 'ezcontentobject_attribute'), $this->dbHandler->quoteColumn('current_version', 'ezcontentobject')), $subSelect->expr->in($this->dbHandler->quoteColumn('contentclassattribute_id', 'ezcontentobject_attribute'), $fieldDefinitionIds), $distanceFilter, $this->getFieldCondition($subSelect, $languageSettings)));
return $query->expr->in($this->dbHandler->quoteColumn('id', 'ezcontentobject'), $subSelect);
}