Skip to main content

Supporting complex selects

Sometimes you might need to conditionally include some columns through some query scopes that might not be in the original database table. A common scenario is including distance for coordinate fields when a user supplies a latitude and longitude.

For these scenarios, you can override the modifyQuery method to call these scopes.

// IslandsController.php

protected function modifyQuery(\Spatie\QueryBuilder\QueryBuilder $query): \Spatie\QueryBuilder\QueryBuilder
{
if (request()->has('lat') && request()->has('lng')) {
$fields = $this->fields()->isNotEmpty() ? $this->fields()->get('_') : [];

if ($this->sorts()->contains('distance') || $this->sorts()->contains('-distance') || (is_array($fields) && in_array('distance', $fields))) {
$query->withDistance('islands.coordinates', new Point(request()->input('lat'), request()->input('lng'), Srid::WGS84));
}
}

return $query;
}

Notice the calls to the $this->sorts() and $this->fields() methods. These are helper methods to retrieve the sorts and fields of the current QueryBuilder request. The following helper methods are included in the Api controllers.

  • sorts()
  • fields()
  • includes()
  • filters()
  • appends()

You can use the modifyQuery method to conditionally eager load relations as well to prevent N + 1 queries.

protected function modifyQuery(\Spatie\QueryBuilder\QueryBuilder $query): \Spatie\QueryBuilder\QueryBuilder
{
$fields = $this->fields()->isNotEmpty() ? $this->fields()->get('_') : [];

if ((is_array($fields) && in_array('formatted_name', $fields)) || $this->appends()->contains('formatted_name')) {
$query->with('atoll');
}

return $query;
}

The $this->fields()->get('_') will return the fields requested for the main model.

For complex selects, use the getAllowedDynamicFields method to allow these fields to be included.

public function getAllowedDynamicFields(): array
{
$fields = [];

if (request()->has('lat') && request()->has('lng')) {
$fields[] = 'distance';
}

return $fields;
}

You may need to also dynamically set the allowed sorts as well.

public function getAllowedSorts(): array
{
$sorts = [
'id',
'created_at',
'updated_at',
'name',
'code',
'land_survey_code',
'order_column',
];

if (request()->has('lat') && request()->has('lng')) {
$sorts[] = 'distance';
}

return $sorts;
}