HasCoordinates   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 95
Duplicated Lines 0 %

Test Coverage

Coverage 53.33%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 29
dl 0
loc 95
ccs 16
cts 30
cp 0.5333
rs 10
c 1
b 0
f 0
wmc 12

11 Methods

Rating   Name   Duplication   Size   Complexity  
A getLongitude() 0 3 1
A scopeNearest() 0 20 1
A scopeNearestInMiles() 0 3 1
A distanceColName() 0 3 1
A latitudeColName() 0 3 1
A scopeNearestInKilometers() 0 3 1
A getDistanceAttribute() 0 7 2
A getDistance() 0 3 1
A scopeOrderByNearest() 0 3 1
A longitudeColName() 0 3 1
A getLatitude() 0 3 1
1
<?php
2
3
namespace UKTowns\Models;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Support\Facades\DB;
7
8
trait HasCoordinates
9
{
10
    /**
11
     * Table column name for latitude.
12
     */
13 1
    public function latitudeColName(): string
14
    {
15 1
        return $this->latitudeColumnName ?? 'latitude';
16
    }
17
18
    /**
19
     * Table column name for longitude.
20
     */
21 1
    public function longitudeColName(): string
22
    {
23 1
        return $this->longitudeColumnName ?? 'longitude';
24
    }
25
26
    /**
27
     * Filed name for distance. This field will be created only id use "nearest" scope.
28
     */
29 1
    public function distanceColName(): string
30
    {
31 1
        return $this->distanceColumnName ?? 'distance';
32
    }
33
34
    /**
35
     * Haversine formula (from Google solution example).
36
     * By default use radius in kilometers.
37
     */
38 1
    public function scopeNearest(Builder $query, float $lat, float $lng, float $radius, ?int $coef = null)
39
    {
40 1
        $coef = $coef ?? config('uk-towns.const.kilometers');
41
42 1
        $latColName        = $this->latitudeColName();
43 1
        $lngColName        = $this->longitudeColName();
44 1
        $distanceFieldName = $this->distanceColName();
45
46 1
        return $query->select([
47
            '*',
48 1
            DB::raw("
49
               ( {$coef} *
50
               acos(cos(radians({$lat})) *
51
               cos(radians({$latColName})) *
52
               cos(radians({$lngColName}) -
53
               radians({$lng})) +
54
               sin(radians({$lat})) *
55
               sin(radians({$latColName})))
56
            ) AS {$distanceFieldName} "),
57 1
        ])->having($distanceFieldName, '<=', $radius);
58
    }
59
60 1
    public function scopeNearestInKilometers(Builder $query, float $lat, float $lng, float $radius)
61
    {
62 1
        return $this->scopeNearest($query, $lat, $lng, $radius, config('uk-towns.const.kilometers'));
63
    }
64
65
    public function scopeNearestInMiles(Builder $query, float $lat, float $lng, float $radius)
66
    {
67
        return $this->scopeNearest($query, $lat, $lng, $radius, config('uk-towns.const.miles'));
68
    }
69
70
    /**
71
     * Order query results by distance.
72
     */
73
    public function scopeOrderByNearest(Builder $query, string $direction = 'asc')
74
    {
75
        return $query->orderBy($this->distanceColName(), $direction);
76
    }
77
78
    /**
79
     * Field has value only is use "nearest" scope.
80
     */
81
    public function getDistanceAttribute(): float
82
    {
83
        if (array_key_exists($this->distanceColName(), $this->attributes)) {
84
            return (float) $this->attributes[$this->distanceColName()];
85
        }
86
87
        return 0;
88
    }
89
90
    public function getLatitude(): ?float
91
    {
92
        return $this->{$this->latitudeColName()};
93
    }
94
95
    public function getLongitude(): ?float
96
    {
97
        return $this->{$this->longitudeColName()};
98
    }
99
100
    public function getDistance(): float
101
    {
102
        return $this->distance;
103
    }
104
}
105