I am trying to add a distance calculated inside a Model::get() method. The distance should be marked as "distance", while now it returns nothing (no added select).
Here is my Eloquent Query :
`
Candidate::whereHas('positions', function($query) use ($data) {
$query->where('positions.id', $data['position']);
})
->whereHas('user', function($query) use ($data) {
$query->where('status', '=', 1);
if($data['lat'] !== null and $data['lng'] !== null){
$query->whereHas('address', function($query) use ($data) {
$sqlDistance = DB::raw('( 111.045 * acos( cos( radians(' . $data['lat'] . ') ) * cos( radians( addresses.latitude ) )
* cos( radians( addresses.longitude ) - radians(' . $data['lng'] . ') )
+ sin( radians(' . $data['lat'] . ') ) * sin( radians( addresses.latitude ) ) ) )');
$query->selectRaw("{$sqlDistance} AS distance");
});
}
})->with('user.address','user','user.media')
->get();
`
Here is what I get :
Any help would be appreciated. Thanks
I am trying to find the business distance from user's current location.
$request->latitude & $request->longitude is user's current location which will be pass in request and I have already stored business's location in database column named latitude & longitude. selectSub() is used to add column in data records. It will contain two arguments first it will be function or column name and the second one will be a new column name.
Business::with(['user','category'])
->select('*')
->selectSub('(111.111 *
DEGREES(ACOS(LEAST(1.0, COS(RADIANS(latitude))
* COS(RADIANS('.$request->latitude.'))
* COS(RADIANS(longitude - '.$request->longitude.'))
+ SIN(RADIANS(latitude))
* SIN(RADIANS('.$request->latitude.'))))))','distance_in_km')
->orderBy('distance_in_km','ASC')
->take($limit)
->skip($offset)->get();
Here is my working code in laravel
The Solution is a mix between Eloquent and Query Builder :
`
Candidate::select('candidates.*')
->whereHas('positions', function ($query) use ($data) {
$query->where('positions.id', $data['position']);
})
->whereHas('user', function ($query) use ($data) {
$query->where('status', '=', 1);
$query->whereHas('address', function ($query) use ($data) {
if($data['country'] !== null){
$query->where('country', '=', $data['country']);
}
});
})
->leftJoin('users', 'users.id', 'candidates.user_id')
->with('user.address', 'user', 'user.media','reviews')
->leftJoin('addresses', 'addresses.id', 'users.address_id')
->addSelect(DB::raw("{$sqlDistance} AS distance") )
->get();
`
Related
how to convert below sql query in laravel
SELECT `models`.* FROM (
(SELECT * FROM `models` WHERE `models`.`fk_car_model_id` = 3 LIMIT 5)
UNION ALL
(SELECT * FROM `models` WHERE `models`.`fk_car_model_id` = 2 LIMIT 3)
UNION ALL
(SELECT * FROM `models` WHERE `models`.`fk_car_model_id` = 1 LIMIT 2)
) AS `models`
// if you want to get all the fields, you can use the following:
$fields = '*';
// if you want to select only some fields, use this:
// $fields = [
// 'id',
// 'fk_car_model_id',
// ];
$data = Model::select($fields)
->where('fk_car_model_id', 3)->limit(5)
->unionAll(
Model::select($fields)
->where('fk_car_model_id', 2)->limit(3)
)
->unionAll(
Model::select($fields)
->where('fk_car_model_id', 1)->limit(2)
)
->get()
->toArray();
return $data;
Or you can use something like this:
$query = DB::table('models')
->select('models.*')
->where('models.fk_car_model_id', '=', 3)
->limit(5)
->unionAll(
DB::table('models')
->select('models.*')
->where('models.fk_car_model_id', '=', 2)
->limit(3)
)
->unionAll(
DB::table('models')
->select('models.*')
->where('models.fk_car_model_id', '=', 1)
->limit(2)
)
->get();
Please check it, this might help you. This might not b exact as you want but you can get some idea about its working and change as per you requirements.
Model::where(function ($query) {
$query->where('fk_car_model_id', '=', 3)
->limit(5)
})->orWhere(function ($query) {
$query->where('fk_car_model_id', '=', 2)
->limit(3)
})->orWhere(function ($query) {
$query->where('fk_car_model_id', '=', 1)
->limit(2)
})->get();
See more for Documentation for reference Logical Grouping
I am new to Laravel and I am trying to search for product using nearby shops
Here is my code
Shop::selectRaw(" id ,
( 6371 * acos( cos( radians(?) ) *
cos( radians( lat ) )
* cos( radians( lng ) - radians(?)
) + sin( radians(?) ) *
sin( radians( lat ) ) )
) AS distance", [$latitude, $longitude, $latitude])
->having("distance", "<", $radius)
->orderBy("distance",'asc')->products()->where('name', 'LIKE', '%' . $keyword . '%')->get();
But I am getting this error message:
"message": "Call to undefined method
Illuminate\Database\Eloquent\Builder::products()"
Note: The Relationship between shop and product is working in my other functions
I am really confused, why are you using products() after orderBy and all the other clauses ?
If you are trying to get products from a shop, you do not have to do products(), you have to use Product::...., but if you want to get shops related to products you have to use whereHas('products', ....), read more about it here.
Your code should be like this:
$shops = Shop::selectRaw(
"id, (6371 * acos(
cos(radians(?)) * cos(radians(lat)) * cos(radians(lng) - radians(?)) + sin(radians(?)) * sin(radians(lat))
)) AS distance",
[$latitude, $longitude, $latitude])
->having("distance", "<", $radius)
->orderBy("distance",'asc')
->whereHas('products', function ($query) use ($keyword) {
return $query->where('name', 'LIKE', '%' . $keyword . '%');
})
->with('products')
->get();
That code will give you shops, you can then do $shops->products.
But if you want directly to get the products you have to use the Product model:
$products = Product::where('name', 'LIKE', '%' . $keyword . '%')
->whereHas('shops', function ($query) use ($latitude, $longitude, $latitude, $radius) {
return $query->selectRaw(
"id, (6371 * acos(
cos(radians(?)) * cos(radians(lat)) * cos(radians(lng) - radians(?)) + sin(radians(?)) * sin(radians(lat))
)) AS distance",
[$latitude, $longitude, $latitude]
)
->having("distance", "<", $radius)
->orderBy("distance",'asc');
})
->get();
I'm trying to return a list of Users of type teacher who have an associated teacher profile, their associated class and location who are within 15 miles of a postcode.
It seems to return every user of every type where obj values are null if there is no record for that model and user,its adds the distance to the location correctly but isn't filtering by distance, don't know why.
But what I want is only users with a teacher profile (teacher model) and a location within 15 miles.
The function in my model
public function searchLocationsByDistance($query, $lat, $lng, $max_distance = 15){
$query->getQuery()->locations = [];
return $query->select('*')
->selectRaw("( 3959 * acos( cos( radians($lat) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians($lng) ) + sin( radians($lat) ) * sin(radians(lat)) ) ) AS distance")
->having('distance', '<=', $max_distance)
->orderBy('distance');
}
The Function in my controller
public function search(Request $request){
$input = $request->all();
//Just google api search this populates fine.
$location = $this->geolocatePostcode( $input['postal_code'] );
$instructors=\App\User::with(['teacher', 'teacher.class','teacher.location' => function ($query)
use($location) {
$locations = new Location;
$locations->searchLocationsByDistance($query,$location->lat,$location->lng);
}])
//->where('type', '==', 'instructor')
->get();
// var_dump($instructors);
return response()->json($instructors->toArray());
}
Can anyone advise what is wrong with my queries or guide me in the right direction.
Are your variables correctly replaced within your raw query? There are several ways to do it;
->selectRaw("...query some '$lat' more query...")
or
->selectRaw("...query some {$lat} more query...")
You could replace variables in another way as well;
->selectRaw("...query some :lat more query...", ['lat' => $lat])
The User query you've written is going to return everything because there is no filter on that query. I think using the Laravel method whereHas() will help you.
Give this a try, but you'll need to tweak as necessary as I'm guessing at a lot, but this should give you an idea to help:
$instructors=\App\User::whereHas(['teacher', function($query) use($location){
$query->with(['class', 'location' => function($q) use($location){
$locations = new Location;
$locations->searchLocationsByDistance($q,$location->lat,$location->lng);
}])->where('type', '==', 'instructor');
}])
->get();
Also - newing up the Location inside the query may not work as expected and seems complex. You may wish to pull the filtered list of teachers first, using just $query->with(['class', 'location']); on that inner query, and then use the search locations method on that collection. Test both ways to see what is most efficient / works best.
I have 2 models, on for Listing and one for State. The relationship on Listing is:
return $this->belongsTo('App\State');
I have my own method on the Listing model to return items based on distance:
public static function getClosest($lat, $lng, $distance)
{
$results = Listing::select('id', 'name', 'address', 'suburb',
'postcode', 'phone', 'url')
->selectRaw('( 6371 * acos( cos( radians(?) ) *
cos( radians( ST_X(position) ) )
* cos( radians( ST_Y(position) ) - radians(?)
) + sin( radians(?) ) *
sin( radians( ST_X(position) ) ) )
) AS distance', [$lat, $lng, $lat])
->havingRaw("distance < ?", [$distance])
->orderBy('distance', 'asc')
->get();
return collect($results);
}
In my ListingController I want to return the name of the state for each item returned by my results.
If I get the results as:
$listings = Listing::all();
It works, but if I use my custom method it doesn't:
$listings = Listing::getClosest($request['lat'], $request['lng'],1000);
The way I'm trying to get the state is with:
foreach ($listings as $item) {
return $item->state->short_name;
Why does it work with I get all but not when I get the results with my custom method?
Because you loss the foreign_key state_id. and your selectRaw cover your select.
Remember you need to select your foreign_key(state_id), so that laravel can find its relationship.
Try to do it like this:
public static function getClosest($lat, $lng, $distance)
{
return Listing::select(\DB::raw('( 6371 * acos( cos( radians(?) ) *
cos( radians( ST_X(position) ) )
* cos( radians( ST_Y(position) ) - radians(?)
) + sin( radians(?) ) *
sin( radians( ST_X(position) ) ) )
) AS distance', [$lat, $lng, $lat]),
'id', 'name', 'address', 'suburb', 'postcode', 'phone', 'url','name', 'state_id')
->havingRaw("distance < ?", [$distance])
->orderBy('distance', 'asc')
->get();
}
Below is a function I have on a model which brings back a Service with the data from three relationships. The scope closeTo contains data relating to how far a location is from a service which I would also like returned, but it currently only uses it to figure out distances.
return Service::with(['relationship1', 'relationship2', 'locations'])
->whereHas('locations', function ($query) use ($latitude, $longitude, $radius){
$query->closeTo($latitude, $longitude, $radius);
})
->get();
the $query->closeTo uses the function below, and I want to send the distance data back with the return from the first function.
public function scopeCloseTo($query, $latitude, $longitude, $radius = 25)
{
$haversine = "(3959 * acos(cos(radians($latitude))
* cos(radians(latitude))
* cos(radians(longitude)
- radians($longitude))
+ sin(radians($latitude))
* sin(radians(latitude))))";
return $query
->select(['id', 'ccg', 'ccg_code']) //pick the columns you want here.
->selectRaw("{$haversine} AS distance")
->whereRaw("{$haversine} < ?", [$radius]);
}
Whats the best wat to achieve this?
Just add the closeTo statement to your eager loading as a Closure instead of a string:
return Service::with(['relationship1', 'relationship2',
'locations' => function ($query) use ($latitude, $longitude, $radius){
$query->closeTo($latitude, $longitude, $radius);
}])->whereHas('locations', function ($query) use ($latitude, $longitude, $radius){
$query->closeTo($latitude, $longitude, $radius);
})
->get();