How to manipulate data objects and arrays using Laravel Map function? - php

Code:
$loans = Loan::select('date_release')
->where('invalid',false)
->where('transaction_year', $transyear)
->get()
->groupBy(function($date) {
return Carbon::parse($date->date_release)->format('m'); // grouping by months
});
$loanmcount = [];
$loanArr = [];
foreach ($loans as $key => $value) {
$loanmcount[(int)$key] = count($value);
}
for($i = 1; $i <= 12; $i++){
if(!empty($loanmcount[$i])){
$loanArr[$i] = $loanmcount[$i];
}else{
$loanArr[$i] = 0;
}
}
$loanTypeCount = Loan::select(['loan_type AS name', DB::raw('count(loan_type) AS y')])
->groupBy('loan_type')
->orderBy(DB::raw('y'), 'desc')
->where('transaction_year', $transyear)
->where('invalid', false)
->get();
$loanTypeCount->map(function($loan)use($loanArr) {
$loan->data = $loanArr;
return $loan;
})
Output:
var_dump(json_encode($loanTypeCount));
string(433) "[{"name":"Salary","y":"6","data":{"1":0,"2":0,"3":0,"4":0,"5":2,"6":1,"7":3,"8":1,"9":1,"10":0,"11":0,"12":0}},{"name":"Emergency","y":"1","data":{"1":0,"2":0,"3":0,"4":0,"5":2,"6":1,"7":3,"8":1,"9":1,"10":0,"11":0,"12":0}}]"
Months were represented by numbers instead of month name. Also, the format is not in array form just like the desired output shown below.
Desired Output:
[
{
name: "Salary",
y: "1",
data: [
[
"January",
0.1
],
[
"February",
1.3
],
]
},
{
name: "Emergency",
y: "2",
data: [
[
"March",
0.1
],
[
"April",
1.3
],
]
}
]
What is the solution for this one? Can't figure this out. Please help. THank you.

in PHP the function has a private scope ,so you can't access the variable normally inside a function.you have to manually pass it to the function
try:
$loanTypeCount->map(function ($loan) use ($loanArr) {
$loan->data = $loanArr;
return $loan;
});

Related

How to short this code, i'm doing same for each and declare variable

I think my code is ugly for such thing, i'm doing twice foreach.
$peoples;
$container = [];
foreach( $this->media_model->people as $person )
{
$people = Person::whereHas('jobs', function( Builder $query ){
$query->where('job_name', '=', 'Artist');
})->get();
$peoples = $people;
}
$peoples return json
[
{
"person_id": 1,
"first_name": "Suzuki",
"last_name": "Amanda"
},
{
"person_id": 3,
"first_name": "Stephen",
"last_name": null
}
]
i need to do something again with the data to join first name and last name
foreach( $peoples as $person )
{
array_push($container, $person->first_name.' '.$person->last_name);
}
return $container;
In the end the json data will be
[
"Suzuki Amanda",
"Stephen "
]

How to sort/order a model's fields?

I do $users = User::all(); This gives me a collection of User objects, where each object has fields something like:
[
{
"id":10,
"age":10,
"name":"John",
"bank_no":5454
},
{
"id":20,
"age":10,
"name":"Jane",
"bank_no":457
}
]
I want each User objects keys to be ordered alphabetically,
For ex:
[
{
"age":10,
"bank_no":5454
"id":10,
"name":"John"
},
{
"age":10,
"bank_no":457
"id":20,
"name":"Jane"
}
]
Now I know that this order comes the order in which the fields are defined in the migration file. But, changing the migration is not an option for me.
How can I do this efficiently?
Sorting by keys you can to do this :
$users = User::all();
foreach ($users as $key => $value) {
ksort($key);
}
Try this.
here try this, I'm not sure if this going to be the efficient solutions for a very long list of users but it should work regardless.
$json = '[
{
"id":10,
"age":10,
"name":"John",
"bank_no":5454
},
{
"id":20,
"age":10,
"name":"Jane",
"bank_no":457
}
]';
$arr = json_decode($json, true);
$res = [];
foreach($arr as $arrObj){
ksort($arrObj);
$res[] = $arrObj;
}
print_r($res);
Laravel sort function on collection see documentation here
$collection = collect([
'id' => 22345,
'first' => 'John',
'last' => 'Doe',
]);
$sorted = $collection->sortKeys();
$sorted->all();

Implementing filter of nested arrays in PHP/Wordpress

I have in the process of moving some code from the front-end (in JavaScript) to the server-side (which is PHP) where it will be filtered and sent out in an API call, and I can't seem to get the filter working properly on the back-end. The code takes an array of objects and filters it for the objects where a certain nested field (which is also an array of objects) contains certain values. The basic shape of the API:
{
"id": 1217,
"name": "Best product ever",
"tags": [
{
"id": 125,
"name": "Important Value",
"slug": "important-value"
},
{
"id": 157,
"name": "Value",
"slug": "value"
},
{
"id": 180,
"name": "Value",
"slug": "value"
},
{
"id": 126,
"name": "Value",
"slug": "value"
},
{
"id": 206,
"name": "Other Important Value",
"slug": "other-important-value"
}
}
The working JS code:
let productAttributes = ['important-value', 'value', 'value', 'value', 'other-important-value'];
filterResults(results) {
let filteredResults = results.filter(product => {
return product.tags.find(tag => {
return tag.slug === this.productAttributes[0];
});
});
if (this.productAttributes[0] !== 'certain important value') {
filteredResults = filteredResults.filter(product => {
return product.tags.find(tag => {
return tag.slug === this.productAttributes[4];
});
});
}
return filteredResults;
}
And the (not yet working) PHP code:
function get_awesome_products() {
$baseRequest = 'https://myawesomeapi/wp-json/wc/v3/products/?
consumer_key=xxxx&consumer_secret=xxxx&per_page=100&page=';
for ($count = 1; $count <= 9; $count++ ) {
$request = wp_remote_get( $baseRequest . (string)$count);
$body = wp_remote_retrieve_body( $request );
$data = array_values( json_decode( $body, true ));
if ($count < 2) {
$completeProductList = $data;
} else {
$completeProductList = array_merge($completeProductList, $data);
}
}
// The code above this comment is doing what I expect, the code below is not.
$filteredProducts = null;
foreach ($completeProductList as &$product) {
$tagArray = $product['tags'];
if (in_array($reg_test_array[0], $tagArray, true) &&
in_array($reg_test_array[4], $tagArray, true))
{
array_push($filteredProducts, $product);
}
unset($product);
return new WP_REST_Response($filteredProducts, 200);
The impression I get is that I need to write a custom function to take the place of Array.prototype.find(), but I'm not strong in PHP and am having trouble wrapping my head around it.
EDIT: Edited to add example of object being filtered and additional PHP code
You could also use the PHP equivalent function array_filter (among a few other array-specific functions) for this task.
Example:
// Your 0 and 4 index values from $reg_test_array
$importantTags = [ "important-value", "other-important-value" ];
$filteredProducts = array_filter($completeProductList, function($product) use ($importantTags) {
return (bool)array_intersect($importantTags, array_column($product['tags'], 'slug'));
});
return new WP_REST_Response($filteredProducts , 200);
Sandbox
This should be equivalent to the JavaScript code you posted, but done without looping through the filtered results twice.
Without knowing the context of important-value and other-important-value, and how they come to be ordered in the $attributes array, it's a little difficult to improve upon the conditional checks used. What I've written thus far however feels like a code smell to me, because it's reliant hard coded values.
function filterResults(array $results, array $attributes)
{
return array_reduce($results, function ($filteredResults, $result) use ($attributes) {
// Extract tag slugs from result
$tagSlugs = array_column($result['tags'], 'slug');
// Append result to filtered results where first attribute exists in tag slugs;
// Or first attribute is not *other-important-value* and fourth attribute exists in tag slugs
if (in_array($attribute[0], $tagSlugs) && ($attribute[0] === 'other-important-value' || in_array($attribute[4], $tagSlugs))) {
$filteredResults[] = $result;
}
return $filteredResults;
}, []);
}

PHP - Perform parsing rules on nested array

So I have a nested array, that mimics a table layout (columns and rows):
{
"1": [
{
"row": "My name is Trevor\n"
},
{
"row": "Can you see me?\n"
},
{
"row": "\f"
}
],
"2": [
{
"row": Hey there! Some other text.\n"
},
{
"row": "What is up?\n"
},
{
"row": "\f"
}
],
"3": [
{
"row": "Some text on the third column. First row."
},
{
"row": "\f"
}
]
}
So "1", "2", "3" are the columns and then under each column, there can be any number of rows.
Now I am trying to do, so my users can perform various parsing rules on either:
All columns and all rows.
Specific columns and all rows.
Whenever a column / row has been parsed, it should be returned to the "original array".
For this, I have created a class that will apply the different parsing rules I have in specified. Getting the parsing rule works fine. I am currently stuck in the actual text transformation/parsing aspect.
Consider I have a parsing rule called "regexTextReplace", that looks like this:
class regexTextReplace
{
private $pattern;
private $replacement;
public function __construct(array $arguments)
{
$this->pattern = $arguments['pattern'];
$this->replacement = $arguments['replacement'];
}
public function apply(array $table, $column = false): array
{
$table = $column ? $table[$column] : $table;
return array_map('self::regex_replace', $table);
}
public function regex_replace(array $table)
{
return preg_replace($this->pattern, $this->replacement, $table);
}
}
This is how I'm using it:
$options = [
'pattern' => '/Trevor/i',
'replacement' => 'Oliver',
];
$engine = new regexTextReplace($options);
$columns = $engine->apply($document->content, 1); //"1" is the specific column.
$columns returns:
[
{
"row": "My name is Oliver\n"
},
{
"row": "Can you see my?\n"
},
{
"row": "\f"
}
]
Two problems here:
It successfully apply the parsing rule (Trever is replaced with Oliver). But it only returns the first column, but I want the entire original array to be transformed.
If I remove the 1 from the apply() method, I get below error:
Array to string conversion
on below line:
return preg_replace($this->pattern, $this->replacement, $table);
Can anyone guide me in the right direction, so I can perform my parsing rule on any column or on all columns, and return the transformed data back to my original array?
I would rewrite the apply function to loop over the entire table, processing each column if the column argument is not set, or if it matches the current table column:
public function apply(array $table, $column = false): array
{
$out = array();
foreach ($table as $col => $rows) {
if ($column === false || $col == $column) {
$out[$col] = array_map('self::regex_replace', $rows);
}
else {
$out[$col] = $rows;
}
}
return $out;
}
Demo on 3v4l.org
You could rewrite your apply method to this:
public function apply(array $table, $columns = false): array
{
$columns = $columns === false ? array_keys($table) : (array)$columns;
return array_map(function ($column) use ($table, $columns) {
return in_array($column, $columns) ? array_map('self::regex_replace', $table[$column]) : $table[$column];
}, array_keys($table));
}
You can pass either a single column, or an array of columns, or nothing (false) to specify the columns you want adjusted.
Demo: https://3v4l.org/Kn4FY
Just loop it and do the regex on all subarrays:
$content = json_decode($json, true);
$options = [
'pattern' => '/Trevor/i',
'replacement' => 'Oliver',
];
$engine = new regexTextReplace($options);
foreach($content as $key => $v){
$columns[$key] = $engine->apply($content, $key);
}
var_dump($columns);
Working demo:
https://3v4l.org/Pk2rC
The benefit of looping in the "PHP" side instead of in the class is that you can still apply the regex to only one or two of the subarrays.
If you loop in the class then you need to pass more arguments to restrict the looping or do some type of array slicing.

PHP using json_encode, must output the specific data

I'm currently stuck at this scenario, now the other developer wants to output the API structure as seen on attached image.
json_required_format
But I tried as far as I can but I only got these result:
"all_projects": {
"TEST TOWNHOMES": {
"unit_types": [
{
"unit": "TOWNHOUSE 44.00"
}
]
},
"TEST HOMES": {
"unit_types": [
{
"unit": "DUPLEX WITH OUT GARAGE 44.50"
}
]
},
"TEST HOMES II": {
"unit_types": [
{
"unit": "DUPLEX WITH OUT GARAGE 44.50"
}
]
},
"TEST VILLAGE": {
"unit_types": [
{
"unit": "TOWNHOUSE 44.00"
},
{
"unit": "DUPLEX WITHOUT GARAGE 52.30"
}
]
}
I am using MVC framework,
This is my model looks like:
public function all_south_projects()
{
$this->db->distinct();
return $this->db->select('Project as project_name')->from('lots')
->where('available','YES')
->get()->result();
}
public function get_unit_types($projName)
{
$this->db->distinct();
return $this->db->select('UnitType as unit')->from('lots')
->where('Project',$projName)
->where('Available','YES')
->get()->result();
}
And then my controller is:
$resp = $this->MyModel->all_south_projects();
$test_array = array();
foreach ($resp as $value) {
$units = $this->MyModel->get_unit_types($value->project_name);
$allunits = array("unit_types"=>$units);
$allunits = (object) $allunits;
$test_array[$value->project_name] = $allunits;
}
//var_dump($test_array);
$stat = 200;
$message = 'Successfully fetched.';
if(empty($test_array)){
$empty=json_decode('{}');
json_output2($stat,'all_projects',$message,$empty);
}else{
json_output2($stat,'all_projects',$message,$test_array);
}
json_output2 is on my helper to customize json format:
Here is my code:
function json_output2($statusHeader,$responseName,$message,$response)
{
$ci =& get_instance();
$ci->output->set_content_type('application/json');
$ci->output->set_status_header($statusHeader);
$ci->output->set_output(json_encode(array('status' =>
$statusHeader,'message' => $message,$responseName =>$response)));
}
NOTE: Scenario is:
The API must give all the projects having available units,
if the project is available, then it needs to get its corresponding available units to view. I know I can make another API call but this time, we need to improve the UX.
Can someone enlighten me to get through this? Thank you!
Change this part :
foreach ($resp as $value) {
$units = $this->MyModel->get_unit_types($value->project_name);
$allunits = array("unit_types"=>$units);
$allunits = (object) $allunits;
$test_array[$value->project_name] = $allunits;
}
To :
foreach ($resp as $value) {
$units = $this->MyModel->get_unit_types($value->project_name);
$test_array[] = [
"project_name" => $value->project_name,
"unit_types" => $units
];
}
You don't have to cast your associative array to object like you did there : $allunits = (object) $allunits; because an associative array will always be serialized as a JSON object (associative arrays do not exist in JSON).

Categories