Laravel Eloquent: JSON comparison - php

I have the following model:
info is a JSON be and the results would look something like this:
{
"cas": "none",
"uom": "6",
"url": "http://www.foobar.com",
"size": "10.00",
"type": "Chemical",
"unit": {
"id": "6",
"name": "gram",
"plural": "grams",
"abbreviation": "g",
"display_order": "6",
"additional_aliases": "gr, gm"
},
"owner": null,
"vendor": "Sigma Aldrich",
"quantity": 8,
"product_name": "Nafion(R), trimethylsilylated",
"catalog_number": "392928-10G",
"ghs_pictograms": [
1
],
"low_level_warning": 3,
"high_level_warning": 7
}
info.quantity tells us how much of this item is left in inventory.
info.low_level_warning / info.high_level_warning is a user entered value where we want to filter by.
So I tried something like this, but it doesn't work:
} else if ($filters['filter'] === 'show_below_min_threshold') {
$query->where(function ($query) {
return $query->where('info->quantity', '<', 'info->low_level_warning');
});
} else if ($filters['filter'] === 'show_above_max_threshold') {
$query->where(function ($query) {
return $query->where('info->quantity', '>', 'info->high_level_warning');
});
}
Any thoughts on what I am doing wrong?

You are comparing two columns. where is for comparing a column to a value.
where('info->quantity', '<', 'info->low_level_warning') is trying to find rows where info->>quantity is less than the string "info->low_level_warning", which doesn't make much sense.
You should use whereColumn
} else if ($filters['filter'] === 'show_below_min_threshold') {
$query->whereColumn('info->quantity', '<', 'info->low_level_warning');
} else if ($filters['filter'] === 'show_above_max_threshold') {
$query->whereColumn('info->quantity', '>', 'info->high_level_warning');
}

Related

Refactor the JSON object in Laravel

Here's my JSON:
[
{
"ID": 1,
"SOURCEID": 1,
"TIMESTAMP": "2020-04-05 07:05:29",
"VALUE": "30"
},
{
"ID": 4,
"SOURCEID": 2,
"TIMESTAMP": "2020-04-05 07:05:17",
"VALUE": "40"
},
{
"ID": 3,
"SOURCEID": 1,
"TIMESTAMP": "2020-04-06 12:04:59",
"VALUE": "35"
},
{
"ID": 5,
"SOURCEID": 1,
"TIMESTAMP": "2020-06-17 12:01:32",
"VALUE": "1"
},
{
"ID": 6,
"SOURCEID": 2,
"TIMESTAMP": "2021-06-17 13:55:29",
"VALUE": "2"
}
]
I need to refactor the JSON like
I need JSON to be refactor based on timestamp and source id and JSON is dynamic like a number of source id present in the given JSON there are two ids that is 1 and 2. Below I gave the expected output.
I need a Unique time stamp in a separate array-like
[2020-04-05,2020-04-06,2020-06-17,2021-06-17]
{ "sourceid: 1, "data":[30,35,1,0], }, { "sourceid": 2, "data":[40,0,0,2], }
Note: The value fills according to the date. Other it should fill as 0.
I have tried like this :
`$data=json_decode($result);
$timestamp=[];
$dataList=[];
foreach ($data as $value){
$date=\Carbon\Carbon::parse($value->TIMESTAMP)->toDateString();
if(!in_array($date, $timestamp, true)){
array_push($timestamp, $date);
}
if(isset($dataList[$value->SOURCEID])){
array_push($dataList[$value->SOURCEID]['data'],$value->VALUE);
} else{
$dataList[$value->SOURCEID]=[
'SOURCEID'=>$value->SOURCEID,
'data'=>[$value->VALUE]
];
}
}
dump($timestamp);
dump($dataList);`
But it produce like
{ "sourceid: 1, "data":[30,35,1], }, { "sourceid": 2, "data":[40,2]}
but I need like
{ "sourceid: 1, "data":[30,35,1,0], }, { "sourceid": 2, "data":[40,0,0,2] }
You need to find the unique timestamps and source ids and then use that to develop your new array. Since you are using laravel, using a collection makes it easier.
I used the transform modifier to modify the TIMESTAMP field so we can "query" it easier via ->where
Please note, I am using the 2nd param true modifier for the json_decode function to get an associative array rather than an object.
$data = json_decode($result, true); // note true
$collection = collect($data);
// transform the timestamp column
$collection->transform(function ($item) {
$item['TIMESTAMP'] = \Carbon\Carbon::parse($item['TIMESTAMP'])->toDateString();
return $item;
});
// get all unique timestamps and source ids
$timestamps = $collection->pluck('TIMESTAMP')->unique();
$sourceIds = $collection->pluck('SOURCEID')->unique();
$dataList = [];
foreach ($sourceIds as $sourceId) {
$items = $collection->where('SOURCEID', $sourceId);
$dataList[$sourceId]['sourceid'] = $sourceId;
foreach ($timestamps as $timestamp) {
$occurrence = $items->where('TIMESTAMP', $timestamp)->first();
if ($occurrence) {
$dataList[$sourceId]['data'][] = $occurrence['VALUE'];
} else {
$dataList[$sourceId]['data'][] = 0;
}
}
}
dd($dataList);
Note you might want to cast $occurrence['VALUE']; to int via (int) $occurrence['VALUE'];
For more information on collections click here.
Output:

Why is one function returning an array and the other one an object in Laravel

I have two Laravel functions that use Eloquent to retreive information from the DB(Postgres) which are virtually the same except that the where in one is a greater or equal than and the other one is less than (dates)
First function:
events = Event::join('cat_status_event', 'event.status', '=', 'cat_status_event.id_cat_status_event')
->join('place', 'event.place', '=', 'place.id_place')
->take(50)
->get(['event.name','place.name as place', 'cat_status_event.description as status'])
->where('start', '>=', date('Y-m-d'));
And is returning this response (an array):
[
{
"name": "Christmas",
"start": "2020-12-24 17:29:23",
"status": "OK"
},
{
"name": "Thanksgiving",
"start": "2021-11-26 01:19:30",
"status": "OK"
}
]
And the second function:
events = Event::join('cat_status_event', 'event.status', '=', 'cat_status_event.id_cat_status_event')
->join('place', 'event.place', '=', 'place.id_place')
->take(50)
->get(['event.name','place.name as place', 'cat_status_event.description as status'])
->where('start', '<', date('Y-m-d'));
And is returning this json (an object):
{
"2": {
"name": "Birthday",
"start": "2020-11-21 01:17:07",
"status": "OK"
},
"3": {
"name": "Anniversary",
"start": "2020-11-21 01:17:07",
"status": "OK"
}
}
I want both functions to return arrays (like the first one), and the only difference between theme is at the where statement at the end

array filter for array values

i have this code which does the job for searching option name how can i use it to search the option value from array.
$productspp ='[{
"id": 4674388066436,
"title": "1st march",
"options": [{
"id": 6046836162692,
"product_id": 4674388066436,
"name": "Size",
"position": 1,
"values": ["12", "24", "36"]
}, {
"id": 6067871875204,
"product_id": 4674388066436,
"name": "z",
"position": 2,
"values": ["blue", "green"]
}, {
"id": 6067871907972,
"product_id": 4674388066436,
"name": "Material",
"position": 3,
"values": ["silk", "cotton"]
}],
}, {
"id": 4674394325124,
"title": "2nd march",
"options": [{
"id": 6046844190852,
"product_id": 4674394325124,
"name": "Title",
"position": 1,
"values": ["Default Title"]
}],
}, {
"id": 4679851704452,
"title": "3rd marchhh",
"options": [{
"id": 6053112545412,
"product_id": 4679851704452,
"name": "Title",
"position": 1,
"values": ["Default Title"]
}]
}]';
$array = json_decode($productspp,1);
$filter_name555 ='options';
$dummytstt ='values';
$filter_value= blue;
$expected = array_filter($array, function($el) use ($filter_name555, $dummytstt, $filter_value) {
return ( stripos($el[$filter_name555][0][$dummytstt], $filter_value) !== false );
}
});
if the user searched option_value and it matches then it should list that product so in this case if user searches silk then it should list that product else not
for option name it works for option value it does not work as stripos expect it to be string but here in data it is array.
we tried in_array also to filter but that also did not work
when we search anything like 12 or 24 or 36 or blue or green then it should list this part of json. i mean this product and the code i have given above does the same but for option name. u can see that option value is array. it can have more than one values so my code is failing.
{
"id": 4674388066436,
"title": "1st march",
"options": [{
"id": 6046836162692,
"product_id": 4674388066436,
"name": "Size",
"position": 1,
"values": ["12", "24", "36"]
}, {
"id": 6067871875204,
"product_id": 4674388066436,
"name": "z",
"position": 2,
"values": ["blue", "green"]
}, {
"id": 6067871907972,
"product_id": 4674388066436,
"name": "Material",
"position": 3,
"values": ["silk", "cotton"]
}],
}
You need to distinguish between an array value or regular value, because they need to be matched differently.
One thing you could do is write logic for if a value is an array, and then force any other kind of value into an array of just one element.
$key = 'options';
$attr = 'values';
$search = 'blue';
$expected = array_filter($array, function($el) use ($key, $attr, $search) {
$values = $el[$key];
if (is_array($value)) {
$values = array_column($value, $attr);
} else {
$value = array($value);
}
foreach ($value as $body) {
foreach ((array)$body as $contents) {
if (stripos($contents, $search) !== false) {
return true;
}
}
}
return false;
}
Your basic problem is that you have a multidimensional array and you're not iterating on all the levels. To make this work, I had to rework the entire logic, but I did in hope this will be a good learning exercise.
The array_filter function only works with the given level. You could keep it, but I'll propose a solution that merely uses nested loops:
/**
* Selects products where options match the searched one by removing the ones that don't match.
*/
function selectProductsWithMatchingOptions(array $products, string $filterName, string $filterValue): array
{
foreach ($products as $key => $product) {
if (!hasMatchingOption($product['options'], $filterName, $filterValue)) {
unset($products[$key]);
}
}
return $products;
}
/**
* Checks whether the searched filter is within any of the options for the product.
*/
function hasMatchingOption(array $options, string $filterName, string $filterValue): bool
{
foreach ($options as $option) {
// this part takes care of "values", which is an array
if (is_array($option[$filterName]) && valueIsListed($option[$filterName], $filterValue)) {
return true;
// this part takes care of "name", which is a string
} elseif (is_string($option[$filterName]) && stringMatches($filterValue, $option[$filterName])) {
return true;
}
}
return false;
}
/**
* Checks if a value is in the list of values.
*/
function valueIsListed(array $values, string $filterValue): bool
{
// we have to iterate and check with stripos because in_array can't handle case
foreach ($values as $value) {
if (stringMatches($filterValue, $value)) {
return true;
}
}
return false;
}
function stringMatches(string $needle, string $haystack): bool
{
return stripos($needle, $haystack) !== false;
}
Now if you test this with your array:
$filtered = selectProductsWithMatchingOptions($array, 'name', 'mAterial');
or
$filtered = selectProductsWithMatchingOptions($array, 'values', 'BLUE');
dumping $filtered should show desired results.
I've made a couple of functions because I prefer extracting smaller pieces of logic. It makes for cleaner code. The alternative would be storing boolean values in variables to know whether we've found a match or not and that tends to lower readability. Plus, this way we can abandon the loop more elegantly, by returning true as soon as we find a match. That's nicer than having to break, potentially through multiple levels.
Note that I haven't passed the 'options' key as a parameter, because it's the only one that can be iterated - this function won't work for 'id' or 'title'. It can be modified to handle those too, but I'll leave that up to you.
Also note that this functionality would still work even if the number of options changes (you mentioned in the comment that max is 3), because it is agnostic of the number of elements. Always aim for more general solutions whenever it doesn't require too much effort. You'll thank yourself later.

match closest number value elasticsearchDSL(php)

I'm having trouble finding an answer on SO, elastic search docs, or google to find this answer for my use case:
Find the closest number to X input that is still lower then X.
I have a mapping that looks like this:
{
"rule": {
"properties": {
"price": { "type": "long" },
"from": { "type": "long" }
}
}
}
What I need the closest matching from, that is less then the input value.
So for example I have these rules:
{
{ "rule": {"from": 1, "price": 5} },
{ "rule": {"from": 50, "price": 4} },
{ "rule": {"from": 100, "price": 3} },
{ "rule": {"from": 150, "price": 2} }
}
If I search for the from with the value off 75, I'd want the rule for "from": 50.
Most of the answers I found were relating to geo/ip or text, I could not find an example that made it click for me.
Range query can be used to get all rules which are less than equal to input value. Top 1 sorted document(by from ) can be returned
Query:
{
"query": {
"range": {
"rule.from": {
"lte": 75
}
}
},
"size": 1,
"sort": [
{
"rule.from": {
"order": "desc"
}
}
]
}

Is there any option to use eloquent with relation based on if condition?

Is there any option to use eloquent with relation based on if condition.
I am trying to make an object based on if condition. please look at the blow posted code. every time the last condition object is returning. but I need the full object. if you see the code there have two objects CountryDetails and stateDetails. but this code returning only last object stateDetails. I need both CountryDetails and stateDetails.
if you see the code there have two objects CountryDetails and stateDetails. but this code returning only last object stateDetails. I need both objects CountryDetails and stateDetails.
if($registrationFieldObj->country == true){
$userDetails->with(['userMeta'=>function($query){
$query->with(['CountryDetails'=>function($query2){
$query2->select('id','country_name');
}]);
}]);
}
if($registrationFieldObj->state == true){
$userDetails->with(['userMeta'=>function($query){
$query->with(['stateDetails'=>function($query2){
$query2->select('id','country_id','state_name');
}]);
}]);
}
{
"has_error": 0,
"msg": "Successfully Logged in",
"api_token": "ey8S7XN3vdNgFravuw6Zmt8H3n2ol2UX1GA9q4l7XdRcitQymZ7ETv2W4lAk",
"user": {
"id": 2,
"name": "Jhon Smith",
"email": "jhon#yopmail.com",
"phone": "9123378019",
"usertype": "APP",
"profile_pic": "no_profile_img.png",
"status": "A",
"created_at": "2019-07-31 05:17:34",
"updated_at": "2019-08-08 07:36:30",
"user_meta": {
"id": 1,
"user_id": 2,
"address": "Kolkata",
"country_id": 101,
"state_id": 39,
"city_id": 5226,
"pincode": "700001",
"device_id": 458945132565,
"device_token": "fsas576dfsbsfjn6qe7q",
"device_type": "A",
"updated_at": "2019-08-07 13:03:10",
"created_at": null,
"state_details": {
"id": 39,
"country_id": 101,
"state_name": "Uttarakhand"
}
}
}
}
Put the condition inside the subQuery
$withCoutry = $registrationFieldObj->country == true;
$withState = $registrationFieldObj->state == true;
$userDetails->with(['userMeta'=>function($query) use ($withCountry, $withState) {
$withArray = [];
if ($withCountry) {
$withArray['CountryDetails'] = function($query2){
$query2->select('id','country_name');
}
}
if ($withState) {
$withArray['stateDetails'] = function($query2){
$query2->select('id','country_id','state_name');
}
}
$query->with($withArray);
}]);
or if you get rid of the select part in them
$withCoutry = $registrationFieldObj->country == true; //just to make it simpler
$withState = $registrationFieldObj->state == true;
$withRelation = ['userMeta'];
if ($withCountry) {$withRelation[] = 'userMeta.CountryDetails';}
if ($withState) {$withRelation[] = 'userMeta.stateDetails';}
$userDetails->with($withRelation)->get();
there is alots of option for condition
you can try this
if($registrationFieldObj->has('country')){
//Your Logic
}
or
if(!empty($registrationFieldObj){
//Your Logic
}
or
if(!$registrationFieldObj->isEmpty()){
//Your Logic
}
Hope this helps

Categories