Jeffz
Jeffz

Reputation: 2105

Laravel: searching related data

I have models: Student, Tutor, Country.

Main model is Student with code:

public function studentTutors()
{
    return $this->morphedByMany(Tutor::class, 'studentable')
                ->with('tutorAddresses');
}

Then relations.

Tutor:

public function tutorAddresses()
{
   return $this->hasMany(TutorAddress::class, 'tutor_id', 'id')
               ->with('tutorCountry');
}   

TutorAddress:

public function tutorCountry()
{
   return $this->hasOne(Country::class, 'country_id', 'country_id')
               ->where('user_lang', 'en');
}

How do I use it:

$paginator = $student->studentFavouriteTutors()
    ->getQuery()  //for paginate
    ->where(function ($query) use ($searchPhraze) {
        if (strlen(trim($searchPhraze))) {
            return $query
                ->where('username', 'like', '%' . $searchPhraze . '%')
                ->orWhere('firstname', 'like', '%' . $searchPhraze . '%')
                ->orWhere('lastname', 'like', '%' . $searchPhraze . '%');
        }
    })
    ->paginate($pages, $columns, $pageName, $page);

Question:

I am searching in tutors table (Tutor) for user/first/last names.

Is there are way to search for country name from countries table (Country: tutorCountry)? Lets say, table has 'name' column with country names.

If yes, how should $paginator code look like, to get data from countries table?

Same question goes for relation tutorAddresses. Lets say, table has 'city' column with city names.

Is this possible?

Now, I do not use relations for search, and just do joins.

BTW: I tried hasManyThrough relation, but it does not seem to pass data from that 'through' table, so this is not going to work for me. Also, my 'through' relations go a bit too deep for it (unless I do not understand something as far as this relation is concerned).


EDIT:

Answer by jedrzej.kurylo is perfect!

I just want to add, for all these, who look for a way to search within relation of a relation, like in my case:

studentTutors / tutorAddresses / tutorCountry

... where within model Student, I also want to look for country name inside of Country model, that is deeper in chain of relations and is not directly related to Tutor, but to TutorAddress, which is related to Tutor.

It is just a question of nesting queries:

$searchPhraze = 'France';
$res = $student->studentFavouriteTutors()

//first relation level
->whereHas('tutorAddresses', function($query) use ($searchPhraze) {

    //deeper relation
    $query->whereHas('tutorCountry', function($query) use ($searchPhraze) {
        $query->where('country', 'like', '%' . $searchPhraze . '%');
    });

})->get();

Or you can even combine of searches of parent and child relations:

$searchPhraze = 'Hodkiewiczville';
$res = $student->studentFavouriteTutors()

//first relation level
->whereHas('tutorAddresses', function($query) use ($searchPhraze) {
    $query
        //first level relation search
        ->where('city', 'like', '%' . $searchPhraze . '%')

        //deeper relation
        ->orWhereHas('tutorCountry', function($query) use ($searchPhraze) {
        $query->where('country_label', 'like', '%' . $searchPhraze . '%'));
    });

})->get();

Above code found related datasets as expected.

Thou, I am not sure as to benchmark of this.

Upvotes: 3

Views: 127

Answers (1)

jedrzej.kurylo
jedrzej.kurylo

Reputation: 40899

You can search data in related tables using whereHas() function, e.g.:

$student->studentFavouriteTutors()
  ->whereHas('tutorAddresses', function($query) use ($searchPhraze) {
    $query->where('city', 'like', '%' . $searchPhraze . '%');
  })->get();

This will get you all student's favourite tutors that have an address where city column contains given phrase.

Upvotes: 1

Related Questions