Search with Relationship - Laravel 5.3 - laravel

Im making a search functionality where users can type in a name. This is the query im doing:
public function api(Request $request)
{
$query = request()->get('query');
$results = Listing::searchAndCache($query);
return $results;
}
Which hits this:
public static function searchAndCache($query)
{
return self::where('name','like','%'.$query.'%')->where('live',true)->paginate(15);
}
I also need to search 'location' along with this query, the problem is its in a diffrent table. The models name is Location. My question is how would I attach the model Location to this query?
So ideally, I would want to do this for the query:
public static function searchAndCache($query)
{
return self::where('name','like','%'.$query.'%')->where('region','like','%'.$query.'%')->where('live',true)->paginate(15);
}

For search you can use searchable trait. so you can do this :
Listing::where('live',true)->search($query)->paginate(15);
if there is a relation between Listing And Location, you can use Laravel realations to do something like this
Listing::where('live',true)
->search($query)
->location
->search($query2)
->paginate(15);

Related

How to retrieve multiple relations with multiple tables in laravel eloquent

I'm using Laravel 5.8 to build a babysitting site. I have 4 tables with different relationships as below:
please see this image
The relationships are:
Babysitter->hasMany(session)
Sessions->hasOne(Review)
Sessions->hasOne(Kids)
Sessions->hasOne(Babysitter)
Sessions->hasOne(Parent)
I want to achieve 2 things:
First one
I want to show this result when listing all babysitters. I'm showing this information for each babysitter:
plsease see this image
See here what I couldn't achieve
plsease see this image
This is my code
Sitters::where('Status', 'active')->where('Verified', 1)->get();
Second one
Also, I've tried to show kids name with parent review as shown here:
plsease see this image
This is what i'm using
Sessions::select('Reviews.*', 'Sessions.Parent_id')->join('Reviews', 'Reviews.Session_id', '=', 'Sessions.id')->with('owner')->where('Trainer_id', session('user')->Id)->where('Status', '=', 'complete')->with('owner')->orderBy('Sessions.id', 'DESC')->get();
Here is Session.php Model
public function owner(){
return $this->belongsTo('App\Models\Parents', 'Parent_id');
}
As discussed change the relations:
Babysitter->hasMany(sesstion)
Sessions->hasOne(Review)
Sessions->belongsTo(Kids)
Sessions->belongsTo(Babysitter)
Sessions->belongsTo(Parent)
First one
in Babysitter.php declare the following attributes
class Babysitter extends Model
{
public function reviews()
{
$this->hasManyThrough(Review::class, Session::class);
}
public function getAverageReviewAttribute()
{
return $this->reviews()->avg('Rating');
}
}
Then you just need to call it on the model instance.
$babysitter = Babysitter::first();
return $babysitter->average_review;
Second one
Just use the relation
$babysitter = BabySitter::with(['sessions' => public function ($session) {
$session->with(['review','parent','kids']);
})->where('trainer_id', '=', session('user')->Id) //did not understand this condition
->first();
This assumes you have parent, kids and review relation declared on Session::class. (change the names if needed)
After a few days of searching & testing, this is what worked for me:
Inside (Sitters) Model, put this relation
public function sessions()
{
return $this->hasMany(Sessions::class, 'sitter_id')
->withCount('reviews')
->withCount(['reviews as review_avg' => function($query){
$query->select(DB::raw('AVG(Rating)'));
}]);
}
Also, inside (Sessions) Model, put this relation
public function reviews()
{
return $this->hasOne(Reviews::class, 'Session_id');
}
Now you query like this
return $sitters = Sitters::with('sessions')->get();
I hope this can help someone :)

Laravel Nova Metrics Partition belongsToMany relationship

I have the following data model:
(Publication) <-[belongsToMany]-> (Subscriber)
I want to create a Nova Partition Metric to display the number of Subscribers for each Publication.
The calculate method of my Partition class looks like this:
public function calculate(Request $request)
{
return $this->count($request, Subscriber::with('publications'), 'publication.id');
}
But I am getting an "unknown column" error. Anyone know how to make this work?
You could do something like this:
public function calculate(Request $request)
{
$subscribers = Subscriber::withCount('publications')->get();
return $this->result(
$subscribers->flatMap(function ($subscriber) {
return [
$subscriber->name => $subscriber->publications_count
];
})->toArray()
);
}
The count helper only allows to group by a column on model's table. It also don't allow to join tables.
If you want a more complex query, with a join and a group by column in another table, you can build your own array of results and return it with the results helper.
You can see the results helper docs here: https://nova.laravel.com/docs/1.0/metrics/defining-metrics.html#customizing-partition-colors
You should create your array (you can use eloquent or query builder here) inside the calculate function, then return that array with the results helper.
Hope this helps!
You can make the groupBy on publication_foreign_key in Subscriber table and edit the publication_foreign_key to publication_name using ->label() method
Like this
public function calculate(Request $request)
{
return $this->count($request, Subscriber::class, 'publication_id')
->label(function($publicationId)
{
switch($publicationId)
{
case publication_foreign_key_1 : return publication_name_1;
break;
case publication_foreign_key_2 : return publication_name_2;
break;
default: return 'Others';
}
});
}

Export Relationship data using Laravel Excel

I'm using Laravel Excel to export data. I want to get all the reminders attached to a vet, which are accessed over a hasManyThrough relationship.
I have tried the following code
RemindersExport.php
public function collection()
{
$vets = Vet::where('is_active', 1)->get();
foreach($vets as $vet){
$reminders = $vet->reminders();
}
return $reminders;
}
Controller
public function reminders()
{
return Excel::download(new RemindersExport, 'reminders30days.xlsx');
}
I get the following message...
Method Illuminate\Database\Query\Builder::all does not exist.
The problem is that you're using the collection method but you should use the query method and laravel-excel excecutes the query for you so you don't have to use the get method at the end of your query, first you sould use the FromQuery concern, after that you should replace your collection method with the query method, write your query and DON'T add the get method at the end of your query
use Maatwebsite\Excel\Concerns\FromQuery;
class VetExport implements FromQuery{
public function query()
{
$vets = Vet::where('is_active', 1);
foreach($vets as $vet){
$reminders = $vet->reminders();
}
return $reminders;
}
}
I hope this helps

Laravel oneToMany accessor usage in eloquent and datatables

On my User model I have the following:
public function isOnline()
{
return $this->hasMany('App\Accounting', 'userid')->select('rtype')->latest('ts');
}
The accounting table has activity records and I'd like this to return the latest value for field 'rtype' for a userid when used.
In my controller I am doing the following:
$builder = App\User::query()
->select(...fields I want...)
->with('isOnline')
->ofType($realm);
return $datatables->eloquent($builder)
->addColumn('info', function ($user) {
return $user->isOnline;
}
})
However I don't get the value of 'rtype' for the users in the table and no errors.
It looks like you're not defining your relationship correctly. Your isOnline method creates a HasMany relation but runs the select method and then the latest method on it, which will end up returning a Builder object.
The correct approach is to only return the HasMany object from your method and it will be treated as a relation.
public function accounts()
{
return $this->hasMany('App\Accounting', 'userid');
}
Then if you want an isOnline helper method in your App\User class you can add one like this:
public function isOnline()
{
// This gives you a collection of \App\Accounting objects
$usersAccounts = $this->accounts;
// Do something with the user's accounts, e.g. grab the last "account"
$lastAccount = $usersAccounts->last();
if ($lastAccount) {
// If we found an account, return the rtype column
return $lastAccount->rtype;
}
// Return something else
return false;
}
Then in your controller you can eager load the relationship:
$users = User::with('accounts')->get(['field_one', 'field_two]);
Then you can do whatever you want with each App\User object, such as calling the isOnline method.
Edit
After some further digging, it seems to be the select on your relationship that is causing the problem. I did a similar thing in one of my own projects and found that no results were returned for my relation. Adding latest seemed to work alright though.
So you should remove the select part at very least in your relation definition. When you only want to retrieve certain fields when eager loading your relation you should be able to specify them when using with like this:
// Should bring back Accounting instances ONLY with rtype field present
User::with('accounts:rtype');
This is the case for Laravel 5.5 at least, I am not sure about previous versions. See here for more information, under the heading labelled Eager Loading Specific Columns
Thanks Jonathon
USER MODEL
public function accounting()
{
return $this->hasMany('App\Accounting', 'userid', 'userid');
}
public function isOnline()
{
$rtype = $this->accounting()
->latest('ts')
->limit(1)
->pluck('rtype')
->first();
if ($rtype == 'Alive') {
return true;
}
return false;
}
CONTROLLER
$builder = App\User::with('accounting:rtype')->ofType($filterRealm);
return $datatables->eloquent($builder)
->addColumn('info', function (App\User $user) {
/*
THIS HAS BEEN SUCCINCTLY TRIMMED TO BE AS RELEVANT AS POSSIBLE.
ARRAY IS USED AS OTHER VALUES ARE ADDED, JUST NOT SHOWN HERE
*/
$info[];
if ($user->isOnline()) {
$info[] = 'Online';
} else {
$info[] = 'Offline';
}
return implode(' ', $info);
})->make();

Accessing more remote model in Laravel 5.4 -updated

I have three models
Photo
-id
-path_full
Person
-id
-name
Face
-id
-person_id //was people_id
-photo_id
I am trying to access the person name from the Photo model.
Face Model:
public function photo()
{
return $this->belongsTo(Photo::class);
}
public function person()
{
return $this->belongsTo(Person::class);
}
Photo Model:
public function faces()
{
return $this->hasMany(Face::class);
}
Person Model:
public function faces()
{
return $this->hasMany(Face::class);
}
In my controller I load the Photos like this:
$photos = Photo::with('faces')->paginate();
In my blade template I want to access the name of the face in the photo.
I got this far.
This is in a foreach hence singular $photo:
{{implode($photo->faces->pluck('people_id')->toArray(),', ')}}
How can I get the name of the person instead?
Solution
I needed this in my view and note my change to the db to person_id so eloquent could do it's magic.
//Controller:
$photos = Photo::with('faces.person')->paginate();
//View:
#foreach($photo->faces as $face)
{{$face->person['name']}}
#endforeach
You can eager load the person data all the time you call faces on the Photo's model:
// Photo.php
public function faces()
{
return $this->hasMany(Face::class)->with('person');
}
Or in your query, you can do this to eager load only at that time:
$photos = Photo::with('faces', 'faces.person')->paginate();
Now you can access like this:
$photos->first()->faces->first()->person->name; // will print the name of the person of the first face of the first photo...
I hope this answer can be helpful.
Try to get the person of the face by changing your query like so:
Photo::with(['faces' => function($query) {$query->with('person')}])->paginate();
I am not quite sure about the syntax, but this is how you can nest relations in an eloquent model. A shorter way to write this might be: Photo::with('faces.person')->paginate();
Some more information is provided here.

Resources