I have a code that displays reviews graph of the entire website and divides them into sections according to ratings. The problem is that this code calculates the total reviews of the whole website. I would need to filter the total reviews count according to the currently chosen language. I use WPML. Any advice?
Code:
function display_all_product_review_histogram($minimum_rating, $maximum_rating){
$all_product_review_average_rating = get_all_product_review_average_rating($minimum_rating, $maximum_rating);
$total_ratings = $all_product_review_average_rating[0]["total_ratings"];
$get_all_product_review_counts_by_ratings = get_all_product_review_counts_by_ratings($minimum_rating, $maximum_rating);
if($get_all_product_review_counts_by_ratings){
$output = '';
$sum = 0;
$total = 0;
$raw_percentages_array = array();
$percentages_array = array();
//When working with rounded percentages, we must make sure the total percentages add up to 100%.
//Creating array of rating values and its percentage
foreach ($get_all_product_review_counts_by_ratings as $key => $rating) {
$percentage = round($rating["amount"] / $total_ratings, 2) * 100;
$raw_percentages_array[] = array("value" => $rating["value"], "percent_of_total" => $percentage, 'amount'=> $rating["amount"]);
}
//Counting the total of our percents
foreach($raw_percentages_array as $key => $percent) {
$total += $percent[ "percent_of_total" ];
}
//Creating an array that will have the actual percentages after the rounding has been applied to it.
//This will help to see if we have 100% or we are not aligned
foreach($raw_percentages_array as $key => $percent){
$percentages_array[$percent["value"]] = round(($percent["percent_of_total"]/$total) * 100, 0);
}
$sum = array_sum($percentages_array); //Again counting the total of our new percents to see if it adds up to 100%
if($sum != 100){ //If we do not have 100%, then we will alter the highest percentage value so that we get a total of 100%
$highest_percentage_key = array_keys($percentages_array, max($percentages_array)); //Getting key of the highest percentage value
$percentages_array[$highest_percentage_key[0]] = 100 - ($sum - max($percentages_array)); //Alterning the percentage
}
//Now we are ready to create the output that will give us 100% in total
$r_count = 0;
$output .= "<div class='product-review-histogram'>";
foreach ($percentages_array as $key => $percentage) {
$output .= "<div class='histogram-row star-rating-". $key ."'>";
$output .= "<div class='histogram-col-1'>". $key ." star</div>";
$output .= "<div class='histogram-col-2'><div class='histogram-meter-bar'><div class='histogram-bar-temperature' style='width: ". $percentage ."%'></div></div></div>";
$output .= "<div class='histogram-col-3'>". $raw_percentages_array[$r_count]['amount'] ."</div>";
$output .= "</div>";
$r_count++;
}
$output .= "</div>";
return $output;
}else{
return;
}
}
UPDATE - sharing code of get_all_product_review_average_rating function:
function get_all_product_review_average_rating($minimum_rating, $maximum_rating){
$get_all_product_review_counts_by_ratings = get_all_product_review_counts_by_ratings($minimum_rating, $maximum_rating);
if($get_all_product_review_counts_by_ratings){ //If we have reviews
$average_rating_results = array();
$total_ratings = 0;
$total_rating_value = 0;
foreach ($get_all_product_review_counts_by_ratings as $key => $rating) {
$total_ratings = $total_ratings + $rating["amount"];
$current_rating_value = $rating["amount"] * $rating["value"];
$total_rating_value = $total_rating_value + $current_rating_value;
}
$average_rating = number_format($total_rating_value / $total_ratings, 1); //Rounding value to one decimal place
$average_rating_results[] = array(
"total_ratings" => $total_ratings,
"average_rating" => $average_rating
);
return $average_rating_results;
}else{
return;
}
}
UPDATE2 - sharing more of the functions related to the graph
function get_all_product_review_ratings(){
global $wpdb;
if ( false === ( $review_ratings = get_transient( 'all_product_review_ratings' ))){ //Checking if we have previously cached query results in order to save resources and increase speed
$review_ratings = $wpdb->get_results("
SELECT meta_value
FROM {$wpdb->prefix}commentmeta as commentmeta
JOIN {$wpdb->prefix}comments as comments ON comments.comment_id = commentmeta.comment_id
WHERE commentmeta.meta_key = 'rating' AND comments.comment_approved = 1
ORDER BY commentmeta.meta_value
", ARRAY_A);
$expiration = 60 * 5; //Expiring query results after 5 minutes
set_transient( 'all_product_review_ratings', $review_ratings, $expiration ); //Temporarily storing cached data in the database by giving it a custom name and a timeframe after which it will expire and be deleted
return $review_ratings;
}else{
return $review_ratings;
}
}
WHAT DO WE NEED TO CHANGE TO GET THIS IS PROBABLY THIS PART OF THE CODE:
$review_ratings = $wpdb->get_results("
SELECT meta_value
FROM {$wpdb->prefix}commentmeta as commentmeta
JOIN {$wpdb->prefix}comments as comments ON comments.comment_id = commentmeta.comment_id
WHERE commentmeta.meta_key = 'rating' AND comments.comment_approved = 1
ORDER BY commentmeta.meta_value
", ARRAY_A);
UPDATE 3:
function get_all_product_review_counts_by_ratings($minimum_rating, $maximum_rating){
$all_product_review_ratings = get_all_product_review_ratings();
if($all_product_review_ratings){ //If we have reviews
$all_product_review_ratings_one_dimensional_array = array_map("current", $all_product_review_ratings); //Converting two dimensional array to one dimensional array
$rating_counts = array_count_values($all_product_review_ratings_one_dimensional_array); //Creating array that consists of rating counts
$ratings = array();
while($maximum_rating >= $minimum_rating){
if(array_key_exists($maximum_rating, $rating_counts)){
$star_count = $rating_counts[$maximum_rating];
}else{
$star_count = 0;
}
//Creating array that contains information about
$ratings[] = array(
"value" => $maximum_rating,
"amount" => $star_count
);
$maximum_rating--;
}
return $ratings;
}else{
return;
}
}
function get_all_product_review_ratings() {
global $wpdb;
if (false === ( $review_ratings = get_transient('all_product_review_ratings'))) { //Checking if we have previously cached query results in order to save resources and increase speed
$review_ratings = array();
$args = array(
'status' => 'approve',
'type' => 'review',
'paged' => 0,
'meta_query' => array(
array(
'key' => 'verified',
'value' => 1
)
));
// The Query for getting reviews - WPML respected
$comments_query = new WP_Comment_Query;
$comments = $comments_query->query($args);
foreach ($comments as $comment) {
$review_ratings[] = get_comment_meta($comment->comment_ID, 'rating', 1);
}
$expiration = 60 * 5; //Expiring query results after 5 minutes
set_transient('all_product_review_ratings', $review_ratings, $expiration); //Temporarily storing cached data in the database by giving it a custom name and a timeframe after which it will expire and be deleted
return $review_ratings;
} else {
return $review_ratings;
}
}
I need to cache the results from Steam API parsing. And so the cached result lasts 15 minutes. I have a code:
public function load()
{
if (Auth::guest()) return response()->json(['success' => false, 'msg' => 'You need login!']);
$inventory = $this->getInventory(file_get_contents('http://steamcommunity.com/inventory/' . $this->user->steamid64 . '/570/2?l=russian&count=5000', true));
if (!$inventory) {
return response()->json(['success' => false, 'msg' => 'Error']);
}
$items = [];
$items_with_prices = json_decode(\Storage::get('prices.txt'));
$items_with_prices_by_key = [];
foreach ($items_with_prices->items as $item_price_key => $item_price_data) {
$items_with_prices_by_key[$item_price_key] = $item_price_data->price;
}
foreach ($inventory['rgInventory'] as $info) {
$item = $inventory['rgDescriptions'][$info['classid'] . '_' . $info['instanceid']];
if ($item['tradable'] == 0) continue;
$price = 0;//InventoryController::getItemPrice($item['market_hash_name']);
if (array_key_exists($item['market_hash_name'], $items_with_prices_by_key)) {
$price = $items_with_prices_by_key[$item['market_hash_name']];
}
if (!$price) continue;
if ($price < 1) $price = 0.64;
$type = $this->getType($item['type']);
$items[] = [
'id' => $info['id'],
'classid' => $item['icon_url'],
'price' => round($price, 2),
'type' => $type
];
}
usort($items, function($a, $b){
return ($b['price'] - $a['price']);
});
return response()->json(['success' => true, 'items' => $items]);
}
This code only works when a site user clicks on the "show my items" button and a request is sent to the list of user items in Steam Dota 2. Now if click constantly to get a list of items, Steam can block the server’s IP address for 24 hours. As I understand it, I need to throw the result of a $inventory variable into the cache. I create database table cache with fields id, user_id, items, date.
How can I now cache the result from a $inventory variable of 15 minutes?
Here is basic caching in laravel
$rememberTimeInSeconds = 3600;
$cachedResult = Cache::remember('name_of_your_cache', $rememberTimeInSeconds, function(){
// It can be any logic I just showing a simple query.
$users = User::all();
return $users;
});
I have built an Employee Management System using Laravel 5.7. Calculating salary works fine for 100-150 employees (but taking a long time to process the data) & for more than 150 employees it is showing a time out error.
I want to add the feature to calculate the salary of all employees in one go & also reduce the processing time for the same. Would it be appropriate to use chunk() for that? If yes, how do I implement that?
Note:
The web application is for a BPO so there are so many types of logic to apply while calculating a salary. The code is too lengthy to share here, but if anyone wants to see the code, I can share it.
Front-end form screenshot
Page after calculation
<?php
public function calculate_salary(Request $request){
$this->validate($request,[
'employee_id' => 'required',
'attendance_types' => 'required',
'month' => 'required_without:choose_date',
'choose_date' => 'required_without:month'
]);
$download_salary = false;
$submit_salary = false;
if ($request->has('submit_data'))
{
$submit_salary = true;
}
else
{
$submit_salary = false;
}
if ($request->has('download_data'))
{
$download_salary = true;
}
else
{
$download_salary = false;
}
$dept = $request->input('salary_department');
$role = $request->input('salary_role');
$process = $request->input('salary_process');
$salary_dept = Department::where('id', $dept)->pluck('dept_name')->first();
$salary_role = Role::where('id', $role)->pluck('role_name')->first();
$salary_process = Process::where('id', $process)->pluck('process_name')->first();
if ($submit_salary == true || $download_salary == true)
{
$months = $request->input('month');
$month = $months[0];
$employee_ids = $request->input('employee_id');
$attendance_types = $request->input('attendance_types');
$attendance_type_temp_insert = $attendance_types[0];
$attendance_type = explode(',', $attendance_type_temp_insert);
if (!empty($request->input('choose_date')))
{
$dates = $request->input('choose_date');
$date = $dates[0];
}
else
{
$date = "";
}
}
else
{
$month = $request->input('month');
$employee_ids = $request->input('employee_id');
$attendance_type = $request->input('attendance_types');
$attendance_type_temp_insert = implode(',', $request->input('attendance_types'));
$date = $request->input('choose_date');
}
// Get start & end dates
$data = $this->get_salary_dates($month, $date);
$start_date = $data['start_date'];
$end_date = $data['end_date'];
$salary_start_date = $start_date->toDateString();
$salary_end_date = $end_date->toDateString();
$working_days_by_month = $this->get_working_days_of_given_month($start_date, $end_date);
$working_days = $working_days_by_month['working_days'];
$no_days = $working_days_by_month['no_days'];
Schema::create('temp_salary', function (Blueprint $table) {
$table->increments('id');
$table->integer('employee_id');
$table->string('employee_name');
$table->integer('working_days');
$table->float('emp_working_days');
$table->float('basic');
$table->float('hra');
$table->float('conveyance');
$table->float('spcl_inc');
$table->float('gross_salary');
$table->float('pf');
$table->float('esi');
$table->integer('headphone');
$table->integer('id_card');
$table->float('deductions');
$table->integer('pl_balance');
$table->integer('combo_balance');
$table->float('net_payable');
$table->string('month')->nullable();
$table->string('attendance_types');
$table->string('choose_date')->nullable();
$table->string('department')->nullable();
$table->string('role')->nullable();
$table->string('process')->nullable();
$table->timestamps();
$table->temporary();
});
$csv[] = ['Employee ID', 'Name','Department','Role','Process','Bank','Bank Account Number', 'Basic', 'HRA', 'Conveyance', 'Spcl Inc', 'Gross Salary','PF', 'ESI', 'Deductions', 'Total Working Days', 'Net Payable','Salary Month', 'Salary Start Date','Salary End Date'];
$csv1[] = ['Employee ID', 'Name','Department','Role','Process','Bank','Bank Account Number', 'Basic', 'HRA', 'Conveyance', 'Spcl Inc', 'Gross Salary','PF', 'ESI', 'Deductions', 'P','H','A','WO','WOP','WOH','UAL','Paid Leaves','PL','Combo','ual_deduct','Total Working Days', 'Net Payable','Salary Month', 'Salary Start Date','Salary End Date'];
foreach($employee_ids as $id) {
if($submit_salary == true){
DB::table('attendances')->where('employee_id', '=', $id)->whereBetween('attendance_date', [$start_date, $end_date])->update(['salary_status' => 1]);
}
$employee = Employee::find($id);
$name = $employee->name;
$emp_role = $employee->empRole->role_name;
$emp_department = $employee->empDepartment->dept_name;
$emp_process = $employee->empProcess->process_name;
$bank_account_number = $employee->bank_account_number;
$bank_name = $employee->bank_name;
$salary = Salary::where('employee_id', $id)->first();
$pl_balance = $salary->pl_balance;
$combo_balance = $salary->combo_balance;
$attendances = DB::table('attendances')->where('employee_id', '=', $id)->get();
if ($attendances->count() > 0)
{
foreach($attendances as $attendance)
{
$att_id = $attendance->id;
$att_date = Carbon::parse($attendance->attendance_date);
$dialer_in = strtotime($attendance->dialer_in_time);
$dialer_out = strtotime($attendance->dialer_out_time);
$bio_in = strtotime($attendance->biometric_in_time);
$bio_out = strtotime($attendance->biometric_out_time);
$bio_diff = $bio_out - $bio_in;
$crm_in = strtotime($attendance->crm_in_time);
$crm_out = strtotime($attendance->crm_out_time);
$week_off = $attendance->week_off;
$combo = $attendance->combo;
$holiday = $attendance->holiday;
$ual = $attendance->ual;
$dialer_duration = $attendance->dialer_difference;
$bio_duration = $attendance->biometric_difference;
$crm_duration = $attendance->crm_difference;
$chkatt = $this->get_attendance($attendance_type, $attendance->dialer_in_time, $attendance->dialer_out_time,$attendance->crm_in_time,$attendance->crm_out_time, $dialer_duration, $bio_duration, $crm_duration);
$attendance_status = $this->attendance_status($id, $chkatt, $holiday, $week_off, $ual);
DB::table('attendances')->where('id', $att_id)->update(['attendance_status' => $attendance_status]);
//echo $attendance_status;
}
}
$totalWorkingDays = $this->get_work_days($id,$start_date,$end_date,$combo_balance,$pl_balance, $submit_salary);
$totalWorkingDays = number_format((float)$totalWorkingDays, 2, '.', '');
$salary_data = $this->get_calculated_salary_data($no_days, $totalWorkingDays, $id, $submit_salary);
$basic = $salary_data['basic'];
$hra = $salary_data['hra'];
$conveyance = $salary_data['conveyance'];
$spcl_inc = $salary_data['spcl_inc'];
$gross_salary = $salary_data['gross_salary'];
$pf = $salary_data['pf'];
$esi = $salary_data['esi'];
$hp_charges = $salary_data['hp_charges'];
$idcard_charges = $salary_data['idcard_charges'];
$deductions = $salary_data['deductions'];
$net_payable = $salary_data['net_payable'];
DB::table('temp_salary')->insert(['employee_id' => $id, 'employee_name' => $name, 'working_days' => $working_days, 'emp_working_days' => $totalWorkingDays, 'basic' => $basic, 'hra' => $hra, 'conveyance' => $conveyance, 'spcl_inc' => $spcl_inc, 'gross_salary' => $gross_salary, 'pf' => $pf, 'esi' => $esi, 'headphone' => $hp_charges, 'id_card' => $idcard_charges, 'deductions' => $deductions, 'pl_balance' => $pl_balance, 'combo_balance' => $combo_balance, 'net_payable' => $net_payable, 'month' => $month, 'attendance_types' => $attendance_type_temp_insert, 'choose_date' => $date, 'department' => $salary_dept, 'role' => $salary_role, 'process' => $salary_process]);
$csv[] = [$id, $name, $emp_department, $emp_role, $emp_process, $bank_name, $bank_account_number, $basic, $hra, $conveyance, $spcl_inc, $gross_salary, $pf, $esi, $deductions, $totalWorkingDays, $net_payable, $month, $start_date, $end_date];
$get_days_status = $this->get_days_status($id, $start_date, $end_date);
$pCount = $get_days_status['pCount'];
$HalfDaysCount = $get_days_status['hCount'];
$lateLeaveCount = $get_days_status['late_leave'];
$wopCount = $get_days_status['wopCount'];
$leaves_count = $get_days_status['leaves_count'];
$UalCount = $get_days_status['UalCount'];
$woCount = $get_days_status['woCount'];
$wohCount = $get_days_status['wohCount'];
$ual_deduct = ($UalCount * 1.5) - $UalCount;
$get_paid_leaves_row = $get_days_status['pl_leaves'];
$csv1[] = [$id, $name, $emp_department, $emp_role, $emp_process, $bank_name, $bank_account_number, $basic, $hra, $conveyance, $spcl_inc, $gross_salary, $pf, $esi, $deductions, $pCount,$HalfDaysCount,$leaves_count,$woCount,$wopCount,$wohCount,$UalCount,$get_paid_leaves_row,$pl_balance, $combo_balance,$ual_deduct, $totalWorkingDays, $net_payable, $month, $start_date, $end_date];
}
$datas = DB::table('temp_salary')->get();
if($submit_salary == true){
return Excel::create('Employee_salary_report', function($excel) use ($csv) {
$excel->sheet('Employee_salary_report', function($sheet) use ($csv) {
$sheet->fromArray($csv, null, 'A1', false, false)
->getStyle('A1')
->getAlignment()
->setWrapText(true);
});
})->download('csv');
}
if($download_salary == true){
return Excel::create('salary_report', function($excel) use ($csv1) {
$excel->sheet('salary_report', function($sheet) use ($csv1) {
$sheet->fromArray($csv1, null, 'A1', false, false)
->getStyle('A1')
->getAlignment()
->setWrapText(true);
});
})->download('csv');
}
Schema::drop('temp_salary');
$departments = Department::all();
$processes = Process::all();
$roles = Role::all();
return view('sys_mg.salaries.get-salary')->with(['datas'=>$datas,'departments' => $departments, 'processes' => $processes, 'roles' => $roles, 'salary_dept' => $salary_dept, 'salary_role' => $salary_role, 'salary_process'=>$salary_process, 'salary_month'=>$month,'salary_startDate' => $salary_start_date, 'salary_endDate' => $salary_end_date, 'attendance_check_type' => $attendance_type]);
}
This is a huge process, doing many things at once, some of them deeply nested in each other.
The function is very, very long, which means it probably needs to be abstracted into various methods and classes (better OOP).
You may also be running into the N+1 problem with some of your Laravel queries, although it's hard to say at a glance.
I would recommend using Laravel's Queues, and adding each employee's payroll calculation to the queue: https://laravel.com/docs/5.8/queues
You can then use a worker process to perform each one individually.
All the database updates and transactions means PHP is constantly going to-and-fro from your database. Try to perform as many operations in pure code as possible, and then once completed, write to the DB. (Where possible, this is not a blanket rule).
Initially I would say, consider creating a SalaryCalculator class with methods such as getAttendances() and calculateSalaryFromEmployeeAttendances().
Separately, create a CSV exporter class. Process all your payroll calculations first, store the results in the DB, and then convert to CSV on demand later.
You'll refactor this later once you are able to look at all the different parts, someone else may be able to suggest a better way to break it down, but in some ways there are no right answers... just start with some OOP and abstraction of methods, and it will get better.
Use microtime(true) to get and calculate the time differences between when you start and finish operations, to start to track how long each function runs for... and go looking for the big optimisations first. What is the slowest part? Why?
You could probably ask a dozen Stack Overflow questions to optimise each of those methods, and that's Ok!
I'm trying to use my Flickr account as a 'host' for an image gallery. I've tagged 251 photos with a common tag 'golftournament' and each one with the year and any players in the photo.
So, for example, three random photos may have the following tags:
golftournament dan steve 2005 (dan and steve in this photo from 2006)
golftournament 2006 (no players in this photo from 2006)
golftournament 2008 paul dan (paul and dan in this photo from 2008)
When I make an API call, it returns an inconsistent total number of photos if I set the 'tags' part of the API call to tags=golftournament,dan,2005 and the tag_mode to tagmode=all.
Sometimes I get 13 photos in the result, sometimes I get 12 photos and sometimes I actually get the correct number of photos (14)
I'm using a PHP library, but that's irrelevant because I see the same results in the Flickr API Explorer: http://www.flickr.com/services/api/explore/?method=flickr.photos.search)
Is there any reason why the Flickr API is so inconsistent in this regard?
Cheers,
Dan
Just to update on this, in the end I simply got all photos from a photoset and then built two arrays of years and players from the tags. Finally, I used what the request was to return the correct photographs. I'm using the excellent phpFlickr library and here's my entire API script:
<?php
// Initialise Flickr API library and authentication
require_once("phpFlickr.php");
$f = new phpFlickr('<api key>', '<secret>');
$f->setToken('<token>');
// Get all photos from the photoset
$page = 0;
$perpage = 500;
$all = array('photos' => array(), 'total' => 0);
while ($perpage * $page < $all['total'] || $all['total'] == 0) {
$p = $f->photosets_getPhotos('<photoset number>', 'tags', NULL, $perpage, $page + 1);
$all['total'] = (integer) $p['photoset']['total'];
$all['photos'] += $p['photoset']['photo'];
$page++;
}
// Get all the available years/players from the tags
$years = array();
$players = array();
foreach ($all['photos'] as $key => $photo) {
$photo_tags = explode(' ', $photo['tags']);
$all['photos'][$key]['tags'] = $photo_tags;
foreach ($photo_tags as $tag) {
if (preg_match('/^[0-9]{4}$/', $tag)) {
if (!in_array($tag, $years)) {
$years[] = $tag;
}
} else {
if (!in_array($tag, $players)) {
$players[] = $tag;
}
}
}
}
rsort($years);
sort($players);
// Set year/player tags if set
$tags = array();
if (isset($_GET['year']) && in_array($_GET['year'], $years)) {
$tags['year'] = $_GET['year'];
}
if (isset($_GET['player']) && in_array($_GET['player'], $players)) {
$tags['player'] = $_GET['player'];
}
// Build output array and filter by year/person
$output = array(
'years' => $years,
'players' => $players,
'photos' => array()
);
foreach ($all['photos'] as $key => $photo) {
if (!isset($tags['year']) || in_array($tags['year'], $photo['tags'])) {
if (!isset($tags['player']) || in_array($tags['player'], $photo['tags'])) {
$output['photos'][] = array(
'thumbnail' => $f->buildPhotoURL($photo, 'Square'),
'image' => $f->buildPhotoURL($photo, 'Large'),
'tags' => implode(' ', $photo['tags'])
);
}
}
}
$output['totals']['total'] = count($output['photos']);
// Calculate paging
$output['perpages'] = array(32, 64, 128, 256);
if (isset($_GET['perpage']) && in_array($_GET['perpage'], $output['perpages'])) {
$output['perpage'] = $_GET['perpage'];
} else {
$output['perpage'] = $output['perpages'][0];
}
if ($output['totals']['total'] <= $output['perpage']) {
$output['pages'] = 1;
} else {
$output['pages'] = ceil($output['totals']['total'] / $output['perpage']);
}
if (isset($_GET['page']) && intval($_GET['page']) > 0 && intval($_GET['page']) <= $output['pages']) {
$page = $_GET['page'];
} else {
$page = 1;
}
$output['totals']['recordstart'] = (($page == 1) ? 0 : (($page - 1) * $output['perpage']));
$output['totals']['recordend'] = ($output['totals']['recordstart'] + $output['perpage']) - 1;
if ($output['totals']['recordend'] > ($output['totals']['total'] - 1)) {
$output['totals']['recordend'] = $output['totals']['total'] - 1;
}
$photos = array();
foreach ($output['photos'] as $index => $photo) {
if ($index >= $output['totals']['recordstart'] && $index <= $output['totals']['recordend']) {
$photos[] = $photo;
}
}
unset($output['photos']);
$output['photos'] = $photos;
ob_start("ob_gzhandler");
echo json_encode($output);
Hope someone finds this useful!
Dan
Apparently the flickr search functionality is bugged at the moment and flickr is working on a fix.