Intro

Often there are times when we want to assert JSON data in a json column of a PostgreSQL database.

This is common for services or repositories that store data in JSON format and we want to check if the data was properly stored with the correct format.


The Problem

Usually when asserting a field in the database you follow the following format:

$this->assertDatabaseHas('table', [
    'field' => 'value',
]);

Where you pass the table name and an array of fields and values to assert, that you wish to find in the database.

But how do we assert a JSON field?

Perhaps you’ve tried something like this:

$this->assertDatabaseHas('table', [
    'json_field' => [
        'field' => 'value',
    ],
]);

This will not work. The reason is that the assertDatabaseHas method uses the where method of the query builder, which in turn uses the = operator to compare the values. Therefore, it will not work for JSON fields.

But knowing that a where is used in the query, and it builds that query using the = operator, we can try deducing something.

Ever wanted to fetch queries in runtime? Let’s check how assertDatabaseHas builds a query:

DB::enableQueryLog();

$this->assertDatabaseHas('table', [
    'field' => 'value',
]);

dd(DB::getQueryLog());

This will output the following:

array:1 [
  0 => array:3 [
    "query" => "select * from "table" where "field" = ?"
    "bindings" => array:1 [
      0 => "value"
    ]
    "time" => 0.5
  ]
]

We can see that the query is built with the = operator, and the value is passed as a binding.


The Solution

Laravel allows to perform JSON column type queries using the -> operator. As for example a query built like this:

DB::table('table')
    ->where('json_field->field', 'value')
    ->get();

This will build a query like this:

array:2 [
  "query" => "select * from "table" where "json_field"->>'field' = ?"
  "bindings" => array:1 [
    0 => "value"
  ]
]

Note that the ->> operator is used instead of the = operator. Laravel when comparing an equality with a JSON field, uses the ->> operator.

Take for example the following json stored inside a ‘client’ json field in a table:

{
    "name": "Chico",
    "email": "chicomedia@email.com",
    "number_of_clients": 10
}

Then, how can we assert this entry exists in the database? As simple as:

$this->assertDatabaseHas('table', [
    'client->name' => 'Chico',
    'client->email' => 'chicomedia@email.com',
    'client->number_of_clients' => 10,
]);

The query that is built, using the DB facade like before:

[
    "query" => "select * from "table" where "client"->>'name' = ? and "client"->>'email' = ? and "client"->>'number_of_clients' = ?"
    "bindings" => [
        0 => "Chico",
        1 => "chicomedia@email.com",
        2 => 10,
]

And that’s it! Now you can assert JSON data in your tests.


Bonus content

Did you know you can pass aliases in the assertDatabaseHas method?

$this->assertDatabaseHas('table as t', [
    't.client->name' => 'Chico',
    't.client->email' => 'chicomedia@email.com',
    't.client->number_of_clients' => 10,
]);

Just thought it was worth mentioning, maybe it can be useful in some cases. (I don’t know which ones, but it’s there if you need it).