Order results of a query by values - php

I'm trying to create a function to populate an ajax calendar with events.
I'm querying a date (format Ymd) from custom field in my wordpress database.
$metakey2 = 'jour';
$jours = $wpdb->get_col($wpdb->prepare("SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = %s ORDER BY meta_value ASC", $metakey2) );
if ($jours) {
$years = substr($jours, 0, 4); // OUTPUT YEAR FROM DATE
$months = substr($jours, 5, 2); // OUTPUT MONTH FROM DATE
$days = substr($jours, 7, 2); // OUTPUT DAY FROM DATE
foreach $years as $year {
foreach $months as $month {
if ($y == $year AND $m == $month) {
$events = array(
foreach $days as $day {
$day => array(
0 => array(
"0" => "Lorem ipsum dolor 111",
)
);
}
);
}
}
}
}
`
I'm definitely no expert and I've been trying to make this work for hours now... Can someone point me towards the right direction please?
Thank you,
Manue

$metakey2 = 'jour';
$jours = $wpdb->get_col($wpdb->prepare("SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = %s ORDER BY meta_value ASC", $metakey2) );
// Chaeck the data returned isn't null, blank, 0-length array, etc.
if (!empty($jours)) {
// Initialize events array
$events = array();
// Loop through the data
foreach ($jours as $jour) {
$year = substr($jour, 0, 4); // OUTPUT YEAR FROM DATE
$month = substr($jour, 5, 2); // OUTPUT MONTH FROM DATE
$day = substr($jour, 7, 2); // OUTPUT DAY FROM DATE
// PROCESS YOUR DATA HERE
...
}
}
The key is the foreach loop after the empty check. You need to loop through the data that is returned from the db as an array.

Related

Json encode in the Wrong Format

I am trying to make some statistics over visits on my site. I know that I could use Google Analytics(which I do), but I want to try to do something myself and it is a good way to learn it.
Problem:
I select dates in my database and sort them to fit this week. After that, I want to add them to a json file. That json file are used by CanvasJS to make a Chart. I have tried some different ways, just to get it simi-working. But the format of the json array, is not the one CanvasJS want.
What I need:
{ visits:[[2019-02-12, 49,],[2019-02-13,40,],[2019-02-14,46,],[2019-02-15,37,], [2019-02-16,31,],[2019-02-17,38,],[2019-02-18,4,] }
What I get:
{ "visits":{"2019-02-12":49,"2019-02-13":40,"2019-02-14":46,"2019-02-15":37,"2019-02-16":31,"2019-02-17":38,"2019-02-18":4} }
My PHP Script:
// Get first and last day of the current week
$first_day = date('Y-m-d', strtotime("- 6 days"));
$last_day = date('Y-m-d', strtotime("+ 1 days "));
// Return all results the past 7 days
$sql = "SELECT date FROM table WHERE date >= '" . $first_day . "' AND date < '" . $last_day . "'";
if($result = $conn->query($sql)){
$response = array();
$visits = array();
while($row = $result->fetch_array(MYSQLI_ASSOC)){
$old_date = $row['date'];
$old_date_timestamp = strtotime($old_date);
$new_date = date('Y-m-d', $old_date_timestamp);
// I don't need the keys, but I cant avoid it to
// get it to work....
$visits[] = array(
'date' => $new_date
);
}
// Add sum of Dates
$response['visits'] = array_count_values(array_column($visits, 'date'));
// Save to json File
$fp = fopen('results.json', 'w');
fwrite($fp, json_encode($response));
fclose($fp);
}
$conn->close();
Thanks to anyone able to help.
Ignoring any quote-related issues you might think are problems (but aren't), it seems your main difference is between what you want...
[[2019-02-12, 49,],...
and what you have...
{"2019-02-12":49,...
This is because array_count_values() creates an associative array, with your dates as keys.
Your issue could be greatly simplified by getting your database to group and count instead of doing it in PHP. You can also benefit from using a prepared statement instead of direct value injection.
// Get first and last day of the current week
$first_day = date('Y-m-d', strtotime("- 6 days"));
$last_day = date('Y-m-d', strtotime("+ 1 days "));
$sql = <<<_SQL
SELECT DATE(`date`), COUNT(1)
FROM `table` WHERE `date` BETWEEN ? AND ?
GROUP BY DATE(`date`)
_SQL;
$stmt = $conn->prepare($sql);
$stmt->bind_param('ss', $first_day, $last_day);
$stmt->execute();
$stmt->bind_result($date, $count);
while ($stmt->fetch()) {
$visits[] = [$date, $count];
}
$response = [ 'visits' => $visits ];
// Save to json File
$fp = fopen('results.json', 'w');
fwrite($fp, json_encode($response));
fclose($fp);
What I understand is you want to group same date as array inside visits, if I understand correctly here is a solution.
Add one for more loop to make format php array before change it to json string.
$first_day = date('Y-m-d', strtotime('- 6 days'));
$last_day = date('Y-m-d', strtotime('+ 1 days '));
$sql = "SELECT date FROM table WHERE date >= '" . $first_day . "' AND date < '" . $last_day . "'";
if ($result = $conn->query($sql)) {
$response = [];
$visits = [];
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
$old_date = $row['date'];
$old_date_timestamp = strtotime($old_date);
$new_date = date('Y-m-d', $old_date_timestamp);
$visits[] = [
'date' => $new_date
];
}
// here the change start
$format = [];
foreach ($visits as $visit) {
$format[$visit['date']][] = $visit['date'];
}
$response['visits'] = array_values($format);
// here the change end
$fp = fopen('results.json', 'w');
fwrite($fp, json_encode($response));
fclose($fp);
}
$conn->close();
if you don't need the key date here another solution
$first_day = date('Y-m-d', strtotime('- 6 days'));
$last_day = date('Y-m-d', strtotime('+ 1 days '));
$sql = "SELECT date FROM table WHERE date >= '" . $first_day . "' AND date < '" . $last_day . "'";
if ($result = $conn->query($sql)) {
$response = [];
$visits = [];
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
$old_date = $row['date'];
$old_date_timestamp = strtotime($old_date);
$new_date = date('Y-m-d', $old_date_timestamp);
$visits[$new_date][] = $new_date; // change here
}
$response['visits'] = array_values($visits); // change here
$fp = fopen('results.json', 'w');
fwrite($fp, json_encode($response));
fclose($fp);
}
$conn->close();
Explanation
in PHP there are two type of array indexed and associative, when you change PHP array to json string, indexed array goes to be array and associative goes to be object.
example
$indexed = [
0 => 'foo',
1 => 'bar'
];
$associative = [
'one' => 'foo',
'two' => 'bar'
];
var_dump(json_encode($indexed));
// [
// "foo",
// "bar"
// ]
var_dump(json_encode($associative));
// {
// one: "foo",
// two: "bar"
// }
in my code I use visit date as a key, that way the same date will goes into same array, and I array_values to convert associative to indexed

WordPress PaidMembershipsPro track day of the membership

I was looking for a plugin that allowed only paid members to access certain WP pages and to display the content based on their day of the course (the course is 200 days long). I used PaidMembershipsPro, but I had to modify it to my needs and I post it here in case might be useful for someone else.
If someone buys a different "membership level" it extends it instead of overwriting (I had to make 3 levels for 3 payment plans, 1/3/6 months, I named them all the same but if someone buys 3 months and then another month it reset the duration, so there is a filter for it below). I asked on SO already, but I didn't get an answer, so I wrote the functions myself. It might be bit sloppy though.
the table:
tt_subs s_id(int) s_user(int) s_months(int) s_since(timestamp)
This is the function that puts the data into a table (it takes the duration as days, because all my levels were timed in days)
add_action("pmpro_after_checkout", tt_AddToSubs);
function tt_AddToSubs($user_id){
global $wpdb, $current_user;
$days = $current_user->data->membership_level->expiration_number; //get days from current order
$months = ceil($days/30);
$today = date('Y-m-d H:i:s',strtotime('today midnight'));
$result = $wpdb->get_results("SELECT s_user, s_months, s_since FROM tt_subs WHERE s_user = $user_id", ARRAY_A);
$rowCount = $wpdb->num_rows;
if($rowCount == 0)//if it's a new user
{
$wpdb->insert('tt_subs',array('s_user' => $user_id,
's_months' => $months,'s_since' => $today),
array('%s','%d','%s')
);
}
else
{
$sincedays = ($result[0]['s_months']*30); //counted without the new month
$since = strtotime("-$sincedays days", strtotime('today midnight'));
$newsince = (strtotime($result[0]['s_since']) <= $since ? $since : strtotime($result[0]['s_since'])); //if membership has passed, shift s_since so user is back in his day
$newsince = date('Y-m-d H:i:s', $newsince);
$months += $result[0]['s_months']; //add months to the existing ones
$wpdb->update('tt_subs',
array('s_months' => $months,'s_since' => $newsince),
array( 's_user' => $user_id ), //where array(
'%d', // value1
'%s' // value2
),
array( '%d' ) //where format
);
}
}
this is a filter that I found for extending the memberships instead of overwriting them
add_filter("pmpro_checkout_level", "level_extend_memberships");
function level_extend_memberships($level)
{
global $pmpro_msg, $pmpro_msgt, $current_user;
//does this level expire? are they an existing members with an expiration date?
if(!empty($level) && !empty($level->expiration_number) && pmpro_hasMembershipLevel() && !empty($current_user->membership_level->enddate))
{
//get the current enddate of their membership
$expiration_date = $current_user->membership_level->enddate;
//calculate days left
$todays_date = time();
$time_left = $expiration_date - $todays_date;
//time left?
if($time_left > 0)
{
//convert to days and add to the expiration date (assumes expiration was 1 year)
$days_left = floor($time_left/(60*60*24));
//figure out days based on period
if($level->expiration_period == "Day")
$total_days = $days_left + $level->expiration_number;
elseif($level->expiration_period == "Week")
$total_days = $days_left + $level->expiration_number * 7;
elseif($level->expiration_period == "Month")
$total_days = $days_left + $level->expiration_number * 30;
elseif($level->expiration_period == "Year")
$total_days = $days_left + $level->expiration_number * 365;
//update number and period
$level->expiration_number = $total_days;
$level->expiration_period = "Day";
}
}
return $level;
}
and finally a function that returns the day the user is on
function tt_myDay()
{
global $wpdb;
if(current_user_can('add_users'))
{
return 999;
}
else
{
$resultq = $wpdb->get_results("SELECT s_since, s_months FROM tt_subs WHERE s_user = ".get_current_user_id(), ARRAY_A);
$day = ceil((time() - strtotime($resultq[0]['s_since']))/86400);
return ($day > ($resultq[0]['s_months']*30) ? ($resultq[0]['s_months']*30) : $day );
}
}

Arrays and loops through days

I have a problem creating this function. My code is a mess and I'm stuck, so I'd rather not post it. I'd rather ask for a fresh solution.
I have an array (mysql rows), fetched with today's date as a condition. I want to create a new array based on data from the previous array and insert it into the database also by today's date. Limit is 15. So if there are already 10 rows by this date, insert only 5, and continue on the next date, for as long as there are rows from the first array.
I am using php and code igniter.
I don't know how you're fetching the data or how you are generating new data, but something like this;
//loop through days that you want to check/update
//fetch existing data for this day into $data
if( count( $data ) >= 15 ) continue; //skip days that already have 15+
for( $x = 0; $x < 15 - count( $data ); $x++ )
{
//insert one new row here
}
//end days loop
Here is how i did it and it does what i want it to do. Not sure if it is the best way to achieve this functionality. Here i was using a temporary array just for testing. Each element will be a new row for the database. Used 3 as maximum(instead of 15) for testing only.
$subscriptions = Subscription::all(array('conditions' => 'token != "" ', 'order' => 'id asc'));
$startTime = strtotime('2013-08-15');
$temparray = array();
$projects = Project::all(array('conditions' => 'end = "'.date("Y-m-d", $startTime).'" '));
if ($projects){$counter = count($projects);}else{$counter = 0;}
while (list($key, $value) = each($subscriptions))
{
if ($counter == 3)
{
do
{
$startTime = strtotime('+1 day', $startTime);
$projects = Project::all(array('conditions' => 'end = "'.date("Y-m-d", $startTime).'" '));
if (count($projects) < 3)
{
$counter = count($projects);
break;
}
} while ($counter <= 3);
$temparray[] = $value->date . " " . date("Y-m-d", $startTime);
continue;
}
$temparray[] = $value->date . " " . date("Y-m-d", $startTime);
$counter++;
}

populating multidimensional array from timestamp

I have a list of mysql timestamps.
I want to produce an html menu, which has got years as first LI and months as nested LI.
for example:
2012 ->
december,
may,
april.
2011 ->
november,
january.
2010 ->
april,
march,
february.
as you see, I want the menu to be an array of years and within months, just the months (and years) which I have in my database.
It's simple for sure, but I can't realize how to perform this.
I'm querying all my timestamps and cycling them, but then when I have to fill the array I can't manage.
The SQL you want is probably something like this:
SELECT YEAR(date) AS year, MONTH(date) AS month, COUNT(*) AS entries FROM my_table
GROUP BY year,month ORDER BY year ASC, month ASC;
And the code would be:
$previous_year = null;
foreach ($result_rows as $row) {
if ($previous_year == null || $row['year'] != $previous_year) {
if ($previous_year != null) {
echo '</ul>/li>';
}
echo '<li><span class="year">'.$row['year'].'</span><ul>';
$previous_year = $row['year'];
}
echo '<li>'.$row['month'].' ('.$row['entries'].')</li>';
}
if ($previous_year != null) {
echo '</ul></li>';
}
Edit:
Alternative PHP based on another answer, this is tidier:
$grouped = array();
foreach ($result_rows as $row) {
$grouped[$row['year']][$row['month']] = $row;
}
foreach ($grouped as $year => $months) {
echo "<li>$year";
echo "<ul>";
foreach ($months as $month => $row) {
echo "<li>$month (".$row['entries'].")</li>";
}
echo "</ul></li>";
}
Have you tried something like this?
<?php
// this would've been pulled from mysql
$data = array(
array('date' => "2010-11-12 12:34:56", 'some' => 'data'),
array('date' => "2010-12-12 12:34:56", 'some' => 'data'),
array('date' => "2010-12-13 12:34:56", 'some' => 'data'),
array('date' => "2011-01-01 12:34:56", 'some' => 'data'),
);
$grouped = array();
foreach ($data as $row) {
$year = substr($row['date'], 0, 4);
$month = substr($row['date'], 6, 2);
$grouped[$year][$month][] = $row;
}
var_dump($grouped);
You must group your queries using GROUP BY , and use loop in PHP which create suitable structure.
In slight peusdo code.
$r = query(SELECT * FROM TBL_WHATEVER ORDER BY TS ASC);
$last_year = null;
while($row as mysql_fetch_assoc($r)){
$year = date('Y', $r['ts']);
if($year != $last_year){
echo $year;
$last_year = $year;
}else{
$month = 1;
while($month < 12){
echo date('F', $r['ts']);
}
}
}
There are many posts about it like this one:
PHP: Loop through all months in a date range?

Trouble inserting two values with array_push() in a while loop

I am trying to count the occurrences of certain dates in my MySQL table using a PHP while loop and place the date and number of repetitions in an array. I am able to properly add the date to the array, but I cannot seem to add the number of repetitions to it.
Example:
function counter() {
//The 'timestamp' column uses the MySQL timestamp type
$query = mysql_query("SELECT timestamp FROM table1 ORDER BY timestamp DESC");
$date_c;
$counter = 0;
$date_array();
while($row = mysql_fetch_array($query)) {
//gets the year, month, and day from the timestamp
$year = substr($row['timestamp'], 0, 4);
$month = substr($row['timestamp'], 5, 2);
$day = substr($row['timestamp'], 8, 2);
$date = $month.'/'.$day.'/'.$year;
if($date == $date_c) {
$counter += 1;
} else {
array_push($date_array, $date, $counter);
$counter = 0;
}
$date_c = $date;
However, when I echo part of the array the counter does not update. Here is an example using the first repeated date in table1:
>>> echo $date;
06/15/2012
>>> echo $counter;
25
>>> echo $date_array[0];
06/15/2012
>>> echo $date_array[1];
0
I have played around with this for a while but I can't seem to find my error. Does anyone know what I am doing wrong?
Is there anything else you want to do with those rows? Because this seems easier to me:
SELECT DATE_FORMAT(timestamp,'%m/%d/%Y') as 'date', COUNT(*) as 'count'
FROM table1
GROUP BY DATE_FORMAT(timestamp,'%m/%d/%Y')
ORDER BY timestamp DESC
... and the problem in the php code might be the last date / count isn't pushed to $date_array (the array_push will have to run once more after the while loop is finished, if the last iteration did NOT push a counter on the array.....)
Agreed with Wrikken that you can get the count of unique dates in SQL, rather than needing to do it in PHP.
However, if you do want to count the instances of each date, I would use the associative array feature of arrays in PHP. So something like:
$query = mysql_query("SELECT timestamp FROM table1");
$date_array = array();
while($row = mysql_fetch_array($query)) {
$year = substr($row['timestamp'], 0, 4);
$month = substr($row['timestamp'], 5, 2);
$day = substr($row['timestamp'], 8, 2);
$date = $month.'/'.$day.'/'.$year;
// Using the # symbol to suppress warnings for missing index
# $date_array[$date]++;
}
print_r($date_array);
At the end of that loop, $date_array is an associative array with the date as the key, and the number of occurrences as the value.

Categories