how to search a array of objects with array property in laravel - php

i want query a table with a json column
the object stored in json column is like this:
[
{
"title":"first",
"ids":[79,583,584]
},
{
"title":"second",
"ids":[600,601,602]
},
{
"title":"third",
"ids":[605,606,624]
}
]
and for example i want to find a row where one of its ids property contain for example 79. something like this:
Model::query()->whereJsonContains('data', ['ids[*]' => 79])->first();
i,ve searched a lot and tried some syntaxes but nothing worked. is that possible to do? how?
my database is mysql

querying nested json formats might not be available as of now, you can try filtering the eloquent collection though.
you have to cast the json data column as array on your model first and then filter the collection as per your conditions.
$id = 8;
$filtered = Model::all()->filter(function($item, $key) use($id) {
$count = 0;
foreach($item->data as $data) {
if(in_array($id, $data['ids'])) {
$count += 1;
}
}
return $count > 0;
})->all();

Related

Query builder filtering rows that have atleast one value from array in JSON column

In Laravel 8, I'm trying to make local scope filtering of users with JSON array columns in the related table. The problem is that I don't know how to check if the JSON column contains at least one value from the array I declared at the beginning.
public function scopeViewable($query)
{
$user = Auth::user();
$locations = $user->getShopAccess()->pluck('id')->toArray();
if ($user->hasPermissionTo('users.index'))
{
return $query->whereHas('shopAccess', function (Builder $q) {
// return $q->whereJsonContains('location_ids', "");
// Here is the problem I want to check if location_ids contain at least one value
// from $locations
});
}
}
Example of values that are contained in column location_ids is
["1", "2", "5"] , integer values wrapped as string
$locations variable is an array of integer values.
If it is possible to check if the location_ids column is a subset of $locations, that would be great.
According to this question
MySQL Filter JSON_CONTAINS Any value from Array
You have 2 options. Use json_contains for each element of the array or use the newer json_overlaps function if your mysql has access to it.
return $query->whereHas('shopAccess', function (Builder $q) use ($locations) {
$q->where(function ($where) use ($locations) {
foreach ($locations as $location) {
$q->orWhereJsonContains('location_ids', $location);
}
});
});
$locations = $user->getShopAccess()->pluck('id')->values();
return $query->whereHas('shopAccess', function (Builder $q) use ($locations) {
$q->whereRaw('JSON_OVERLAPS(location_ids, ?)', [$locations->toJson()]);
});

Laravel: Querying JSON column containing array, expecting similar results to "whereIn"

I have a database column called support_tags. This is a jsonb column containing a simple array that looks like:
[
"caring",
"budgets",
"careers_employment",
"addictions"
]
I am attempting to query this column using the following:
$services = \App\Models\Service::where("status", "accepted")->whereRaw("JSON_CONTAINS(support_tags, '" . json_encode($categories) . "')")->get();
This doesn't retrieve the results I am hoping for/expecting. If I send the following array:
[
"caring",
"smoking"
]
The results I get back are services that contain all array elements. I need this to work more like a whereIn, in that not all array values need to be present in the database column, but one or more. If anyone knows of a way to do this I'd be very grateful. Thanks.
you can use these eloquent methods: ->whereJsonContains() and ->orWhereJsonContains()
your query will be like this:
$services = \App\Models\Service::where("status", "accepted")
->where(function($query) {
$query->whereJsonContains('support_tags', 'smoking')
->orWhereJsonContains('support_tags', 'caring');
});
Just before I accept the other answer to this question, I thought it may be useful to share my implementation of this which is based on the accepted answer This does the trick:
$categories = request()->infoAdviceCategories;
$services = [];
if (count($categories)) {
$services = \App\Models\Service::where("status", "accepted")->whereJsonContains("support_tags", $categories[0]);
foreach ($categories as $category) {
if ($category !== $categories[0]) {
$services->orWhereJsonContains("support_tags", $category);
}
}
$services = $services->get();
}
return $services;

how to fix Object of class Illuminate\Support\Collection could not be converted to int?

I want to retrieve the data that stores the total questions on a quiz, but when I loop using the result of the total question data that I got using the query below, the result shows Object of class Illuminate \ Support \ Collection could not be converted to int. does anyone know how to solve it?
Here is the error
Here is my code
public function insertStdAnswer(Request $request,$id){
$_num = DB::table('quiz')
->select('question_total')
->where('id', $id)
->get();
for($i = 0; $i <= $_num ; $i++){
$quiz_id = $id;
$user_id_std_quiz = Auth::id();
$std_answer = $request->input('answer-'.$i);
$nomor_quiz = $request->input('soal-'.$i);
DB::insert('insert into studentquiz (`std_quiz_id`,`user_id_std_quiz`,`std_answer`,`nomor_quiz`) values(?,?,?,?)',
[$quiz_id,$user_id_std_quiz,$std_answer, $nomor_quiz]);
}
I think you should use ->value('question_total')
$_num = DB::table('quiz')
->where('id', $id)
->value('question_total');
use count($_num) instead of $_num in for loop
You are comparing $i with a collection.
As you are using $_num to assign data which you have queried form the database using the get() method, your $_num has become a collection object, as any query in Laravel returns a collection object. To use it in your for() loop as in integer value you will have to count the collection to get an integer value. you can use the following code instead of your current for loop:
for($i = 0; $i <= count($_num) ; $i++){
// your code
}

Laravel | Map one collection as property of another collection

I have a table like this:
CourseCategory can have duplicates in this table so that means a courseCategory can have multiple courseNames.
I am fetching all the courseCategory like this:
$courseCategories = Course::all()->pluck('courseCategory')->unique();
But Now I want to map all the courseNames beloging to a courseCategory in this collection.
so that I can have a $courseCategories which I want to be able to access it like this:
foreach($courseCategories as $courseCategory){
foreach($courseCatgory->courseNames as name){
//code
}
}
to get such a $courseCategories So far I have tried:
foreach ($courseCategories as $courseCategory) {
$courseCategories->$courseCategory = Course::where('courseCategory', '=', $courseCategory)->pluck('courseName');
}
dump($courseCategories);
which looks like this which is not quite right:
How Can I do this?
First get all data
$data = Course::all()
Get its category
$categories = $data->pluck('courseCategory')->unique();
Then do some mapping and filtering to get each of those categories child.
$result = $categories->map(function($category) use ($data) {
return $data->filter(function($row) use ($category) {
return $category == $row->courseCategory;
})->pluck('courseName');
});

Laravel collection for loops and mappings

I am trying to refactor my code, and remove a for loop.
$result = [];
foreach ($data as $language) {
$result[$language->{$key}] = $language->{$column};
}
This became:
$result = $data->map(function($language) use ($key, $column){
return [$language->{$key} => $language->{$column}];
});
But now instead of:
[
"key":"value",
"key":"value"
]
I am getting
[
{
"key":"value"
},
{
"key":"value"
}
]
Why doesn't it map like an array?
Please refer this URL
For Example:
$emailLookup = $employees->reduce(function ($emailLookup, $employee) {
$emailLookup[$employee['email']] = $employee['name'];
return $emailLookup;
}, []);
Gives you result like:
const emailLookup = {
'john#example.com': 'John',
'jane#example.com': 'Jane',
'dave#example.com': 'Dave',
};
In your case do like:
$result = $data->reduce(function($language, $a){
$language[$a['any_you_want']] = $a['any_you_want'];
return $language;
}, []);
Hope this helps you!
You probably needed to mapWithKeys:
$result = $data->mapWithKeys(function($language) use ($key, $column){
return [$language->{$key} => $language->{$column}];
});
The method has been available since Laravel 5.3
According to the docs:
The mapWithKeys method iterates through the collection and passes each value to the given callback. The callback should return an associative array containing a single key / value pair:
For your simple use case, pluck() is the method you're looking for. It will build a new collection using one column of an existing array. You can also pass in a second field that will be used to key the new collection.
So, in your case, the data column you're selecting is $column, and the column to use as the key for the new collection is $key. Your code would be:
$result = $data->pluck($column, $key);
This says "give me a collection of all of the $column data, and key it by the $key data".
If you want the plain array instead of the collection, just call all() on the result:
$result = $data->pluck($column, $key)->all();
If you need to "pluck" more than one column of data, you will need to use the mapWithKeys() method already mentioned.
Here in loop you need to use as key value pair
$result = [];
foreach ($data as $key => $language) {
$result[$key] = $language;
}

Categories