calin.ionut's avatar

query builder search a json column

Hello!

In the product table a have a column Colors where i saved the product colors as a json (the colors id`s)

Example of data saved in a column Colors:

[{"id":"2"},{"id":"4"},{"id":"5"},{"id":"6"}]

The query i want to make:

$products = Product::query();
if(request()->has('color')){
    $color = ProductColor::where('Slug', request('color'))->first();
    $products = $products->where('Colors->"$.id"', $color->ColorID);
}

but does not work (it doesn`t display any product)

I also tried with the JSON_EXTRACT and not working.

$products = $products->where(DB::raw('JSON_EXTRACT(`Colors`, "$.id")'), '=', $color->ColorID);

Wow can i find a value in a json column?

0 likes
17 replies
Saikishore's avatar

Are you sure , in your database column names are camel case?

What is 'Colors->"$.id"' ?

Tray2's avatar

Save yourself a lot of headache and use a one to many relationship on your colors.

Nezar's avatar

You can try multiple ways, one could be $products->where('colors->id', $color->ColorID) -> make sure you use the right cases, like Saikishore suggested. There is also whereJsonContains which you would use like this: $products->whereJsonContains('colors', $color->ColorID)

calin.ionut's avatar

@nezar i have tried both and not working:

 $products = $products->where('Colors->id', 2);
$products = $products->whereJsonContains('Colors', 2);
calin.ionut's avatar

Could be because the Colors colum is varchar??? But the data inside is json

mstrauss's avatar

What about json_decode'ing the Colors column before comparing? Like:

class Products extends Model
{
    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'Colors' => 'array',
    ];
}

From the docs: https://laravel.com/docs/5.8/eloquent-mutators#array-and-json-casting

The array cast type is particularly useful when working with columns that are stored as serialized JSON. For example, if your database has a JSON or TEXT field type that contains serialized JSON, adding the array cast to that attribute will automatically deserialize the attribute to a PHP array when you access it on your Eloquent model:

Then you should have more success with your query as the json data will have automatically been transformed into a regular PHP array:

// notice the **whereIn**

$products = Product::query();
if(request()->has('color')){
    $color = ProductColor::where('Slug', request('color'))->first();
    $products = $products->whereIn('Colors', $color->ColorID);
}
calin.ionut's avatar

@mstrauss i have tried casting the Colors in the model Products and

$products = $products->whereIn('Colors', [2, 3]);

but nothing.

mstrauss's avatar

Can you please show us your Colors model? Also if you die and dump $color = ProductColor::where('Slug', request('color'))->first(); what does that look like?

calin.ionut's avatar

@mstrauss sure

class ProductColor extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */

    protected $table = 'products_colors';
    protected $primaryKey = 'ColorID';
}
calin.ionut's avatar

this seems to work:

$products = $products->whereRaw('JSON_CONTAINS(Colors, \'{"id": "2"}\')');

if using "2" not 2 will work!

1 like
mstrauss's avatar

Sorry, I meant to ask for the Product model, not the ProductColor.

calin.ionut's avatar

@mstrauss the product model

class Product extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */

    protected $table = 'products';
    protected $primaryKey = 'ProductID';

    protected $casts = [
        'Colors' => 'array',
    ];
}
mstrauss's avatar

Seems like you got the answer, good job! Now you just have to transform the $color variable request yielded from the user input to match the regex in the query. Good luck with the project! Just beware that this particular query may be a bit unwieldy to maintain.

staudenmeir's avatar

You can use whereJsonContains() with an array:

$products = $products->whereJsonContains('Colors', ['id' => '2']);
2 likes
ga46's avatar

@staudenmeir suppose a have two key like

{"set_by_admin": 1, "accessable_categories": "139"}

i want to search to set_by_admin not equal 1 how we do it?

Sinnbeck's avatar

@ga46 please make a new thread instead of reopening a 3 year old thread :)

Please or to participate in this conversation.