I have three models: Vehicle
, LiveData
, and Place
.
In my Vehicle
model, I currently have a relationship between Vehicle
and LiveData
that gets the latest LiveData
for a vehicle:
public function latestPosition(): HasOne
{
return $this->hasOne(LiveData::class)->latestOfMany();
}
Now in my LiveData
model, I want to establish a relationship between LiveData
and Place
, however there are no direct keys between the two, the relationship is between a range of latitude and longitude.
This is how the relationship would be called in pure sql:
LEFT JOIN `places`
ON `live_data`.`speed` = 0 AND `places`.`lat` BETWEEN (`live_data`.`lat` - 0.00007) AND (`live_data`.`lat` + 0.00007) AND `places`.`lng` BETWEEN (`live_data`.`lng` - 0.00007) AND (`live_data`.`lng` + 0.00007)
Because I want to be able to use this relationship with eloquent like so:
$vehicles = Vehicle::where('disabled', 0)
->with(['latestPosition.place', 'tags'])
->get();
Is this something that is even possible?
2
Answers
Yes and no.
Yes, you can establish eloquent relationships with fields that are not keys.
For example, for tables such as these
You could define a
hasOne
orbelongsTo
using email as instead of a key. Foreign keys are not a requirement. They just make it easier to keep data integrity and your rdbms knows how to optimize queries for them a bit.No, you cannot define a relationship that does that exact query in the background. Not unless you were to implement a custom
Relation
class. Laravel does not provide (to my knowledge) any relationship that resolves to aLEFT JOIN
clause that compares columns to values or aggregate values.You could define a query scope that encapsulates all that logic on the
LiveData
model and then call that.I use
$join->where
instead of$join->on
becauseon
is reserved for comparing columns whereaswhere
can be used to compare with values.Yes, it is possible to establish a relationship between the LiveData model and the Places model based on a range of latitude and longitude values. In Laravel’s Eloquent ORM, you can define a custom relationship using a closure in the model’s with method.
In your LiveData model, you can define the relationship with Places using the whereHas method along with a closure to specify the latitude and longitude range conditions. Here’s an example of how you can define the relationship:
In this example, the place relationship is defined as a hasOne relationship with Places model. It includes the conditions to check for speed being 0 and latitude and longitude falling within the specified range based on the lat and lng values of the LiveData model.
Now, in your query to retrieve vehicles, you can eager load the latestPosition.place relationship along with other relationships:
With this setup, each LiveData record associated with a vehicle will be eager loaded with its corresponding place based on the latitude and longitude range conditions specified in the relationship.
Make sure to adjust the column names and ranges according to your actual database structure and requirements.