DEV Community

Cover image for Advance Searching in Laravel for JSON Column type and using WHEN Method
Jitesh Dhamaniya
Jitesh Dhamaniya

Posted on

Advance Searching in Laravel for JSON Column type and using WHEN Method

As a Regular Programmer Advance Searching is something you encounter once in a while. I recently implemented it in Laravel. Which i am going to explain here.

Prerequisite - it’s assumed that you do have basic knowledge of Laravel Architecture and its functionality such as how model, controller and routing works.

Okay so we do have a biodata table as the name represents it saves the biodata of a user. And this is how its migration looks like

Migration

$table->id();
$table->enum('gender',['boy','girl']);
$table->string('name',255);
$table->json('biodata');
$table->timestamps();
Enter fullscreen mode Exit fullscreen mode

As you can see, we do have a table column biodata which is JSON type. This allows us to save JSON in our db. And we are going to use attribute casting to convert it to object automatically when fetching. So our Biodata model would look like this

Model

namespace App;

use Illuminate\Database\Eloquent\Model;

class Biodata extends Model
{
    protected $guarded = [];

    protected $casts = [
       'biodata' => object
    ];

    protected $perPage = 10;

}

Enter fullscreen mode Exit fullscreen mode

Now before going further let’s see what our Biodata object would look like

Factory

return [
            'gender' => $this->faker->randomElement($this->gender),
            'name'=> $this->faker->name(),
            'biodata' => [
                'personal'=>[
                    'qualification' => $this->faker->randomElement($this->qualification),
                    'height'=>$this->faker->randomElement($this->height),
                    'income'=>$this->faker->numberBetween(100000,1000000),
                    'occupation' => $this->faker->randomElement($this->jobs),
                ],
                'family'=>[
                    'father_gotra' => $this->faker->randomElement($this->gotra),
                    'mother_gotra' => $this->faker->randomElement($this->gotra),
                ],
                'contact'=>[
                    'state' => $this->faker->randomElement($this->indianStates),
                ]
            ],
        ];

Enter fullscreen mode Exit fullscreen mode

And on all these fields we are going to run a search.
Now It’s time to discuss what we want to get out of our search function. So here our all optional search parameters

  • Gender
    • Single Value
    • field type - ENUM
  • Qualification
    • Could be Multiple Values
    • field type - JSON Key
  • Min Height
    • Single Value
    • field type - JSON Key
  • Income
    • Single Value
    • field type - JSON Key
  • Occupation
    • Single Value
    • field type - JSON Key
  • Gotra
    • Multiple Value
    • field type - JSON Key

Now this might seem very straightforward to you, all you have to use is where statement and you should be okay BUT hold your horses and Let’s go over some obvious problems first

Searching in JSON field ?

So how are we going to search in JSON ? well apparently that's not hard with MYSQL and even better with Laravel, in nutshell this is how you search in a JSON field provided its key

return $query->where('biodata->personal->income', '>=', $minIncome);

Enter fullscreen mode Exit fullscreen mode

Check Biodata object above to understand.

Multiple Values

Now before jumping into code to write search function, the one question you might be asking is how you are supposed to send multiple values in a query string ? well glad you asked, we will simply add them with comma like this

http:://url.com/?qualification=MBA,BA&gender=boy 
Enter fullscreen mode Exit fullscreen mode

Now we can simply explode them using “,” and it will give us an array of values for that parameter.

How to add multiple queries to where statements ?

So this is a standard where statement from Laravel docs

DB::table('users')->where('votes', 100)->get();
Enter fullscreen mode Exit fullscreen mode

And we do have a better one too

$users = DB::table('users')->where([
    ['status', '=', '1'],
    ['subscribed', '<>', '1'],
])->get();
Enter fullscreen mode Exit fullscreen mode

Here you can add multiple arguments to where statement,
But We need to have some kind of validation before we actually jump to running any query, Because all parameters are optional, so user might not provide all or any input to add to query.

And hence we need to write extra code when searching with where and that’s where when comes to save us. This is what official documentation says about it

Sometimes you may want clauses to apply to a query only when something else is true. For instance you may only want to apply a where statement if a given input value is present on the incoming request. You may accomplish this using the when method:

And this is how you write it

$users = DB::table('users')
                ->when($role, function ($query, $role) {
                    return $query->where('role_id', $role);
                })
                ->get();
Enter fullscreen mode Exit fullscreen mode

But we have one more problem, how do we go about adding multiple values for the same parameter, well the second argument of when is a function and we can do anything in this function :) keep reading

So for checking multiple values for the same parameter we are going to use a loop in it and this is how it's going to look like.

->when($gotra, function ($query, $gotra) {
               foreach($gotra as $g){
                   $query->where('biodata->family->father_gotra', '<>' ,$g);
               }
               return $query;
           })
Enter fullscreen mode Exit fullscreen mode

So that’s it. we covered all the basics we going to need to write search function. and this is how it’s going to look like

public function search(){

       $gender = request('gender');
       $state = request('state');

       $minIncome = (int)request('minIncome');
       $minHeight = (int)request('minHeight');

       $qualifications = request('qualification') != '' ? explode(",", request('qualification') ) : false;

       $gotra = request('gotra') != '' ? explode(",", request('gotra') ) : false;

       $results = Biodata::
           when($gender, function ($query, $gender) {
               return $query->where('gender', $gender);
           })
           ->when($state, function ($query, $state) {
               return $query->where('biodata->contact->state', $state);
           })
           ->when($qualifications, function ($query, $minQualification) {

               foreach($qualifications as $qualification){
                   $query->where('biodata->personal->qualification', '=', $qualification);
               }
               return $query;

           })
           ->when($minIncome, function ($query, $minIncome) {
               return $query->where('biodata->personal->income', '>=', $minIncome);
           })
           ->when($minHeight, function ($query, $minHeight) {
               return $query->where('biodata->personal->height', '>=' , $minHeight);
           })
           ->when($gotra, function ($query, $gotra) {
               foreach($gotra as $g){
                   $query->where('biodata->family->father_gotra', '<>' ,$g);
               }
               return $query;
           })
           ->paginate(10);

          return response($results,200);
Enter fullscreen mode Exit fullscreen mode

I followed TDD approach while developing this so i created tests first, let me know if anyone of you want to check them out. Send me msg and i will share code.

So i will leave you guys with this today, let me know what you think.

Top comments (5)

Collapse
 
yellow1912 profile image
yellow1912

What does it use under the hood? I imagine you need a new version of mysql that does support searching within json objects. I remember this feature was not available on mysql several years ago (postgre was the one that had it)

Collapse
 
jiteshdhamaniya profile image
Jitesh Dhamaniya

MySQL supports the native JSON data type since version 5.7.

Collapse
 
flinlock profile image
Tyson Roehrkasse

Very helpful, thank you! I am very interested in seeing your tests for this.

Collapse
 
iserveradmi profile image
Rajan Sharma

amazing article, helps a lot when an intermedeate moving as an expert .

Collapse
 
srsohan150626 profile image
Md Sohanur Rahaman

Billion billion thanks for making such a great post. It's very helpful for me. Thanks again.