Dynamically creating WHERE statements - php

The snippet below print out a query that fetches users from a DB who's DOB are in a certain age range. In this case, users that are either between 11 and 12 or 17 and 18 years old. I'm trying to dynamically create this query in CodeIgniter Active Record syntax.
This snippet
$age_count = 0;
foreach( $range as $r )
{
$start_date = strtotime($r[0] . "years ago");
$stop_date = strtotime($r[1] . "years ago");
$range = array('dob <' => $start_date, 'dob >' => $stop_date);
( $age_count == "0" || $age_count%1 ) ? $this->db->where($range) : $this->db->or_where($range);
$age_count++;
}
$users = $this->db->get("users")->result_array();
produces this query
SELECT * FROM (`users`) WHERE `dob` < 956351611 AND `dob` > 924729211 OR `dob` < 766962811 OR `dob` > 735426811
The last OR should of course be AND. How can I achieve this? It comes down to knowing when to use or_where() or simply where(). I thought every odd pass through the foreach should be an OR but I'm not quite there yet.
This function might just receive one range (11-12) or several ones.

With the database API, you can use multiple where() to get a WHERE … AND ….
$this->db->where('name', $name);
$this->db->where('title', $title);
$this->db->where('status', $status);
// WHERE name = 'Joe' AND title = 'boss' AND status = 'active'
(tied from the documentation)

Related

Substract a date compared to current date and show actual remaining days

This is my function below:
function Active()
{
............
$num_rows = $db->doQuery('SELECT PremiumDays, PremiumStartTime FROM Premium WHERE AccountID = ?', $_SESSION['AccountID']);
if ($num_rows == -1)
{
$this->Error('ERROR');
$db->getError();
return;
}
$data = $db->doRead();
$data['Status'] = $num_rows == 0 ? '<:SHOW_PREMIUM_STATUS:>' : '<b><font size="2" color="red">Premium is active - <%Days_Remaining%> days remaining.</font></b>';
$replace = array
(
'account_status' => $data['Status'],
'days_remaining' => number_format($data['PremiumDays'])
);
$this->content = Template::Load('account-template', $replace);
}
PremiumDays column contains numbers like 10,15,30 etc.
PremiumStartTime contains a date in this format 2018-12-17 21:13:00
What I am trying to achieve is to show the actual days of premium remaining with days_remaining. So, I believe I need to substract from PremiumDays the days that passed since the premium started based on the second column PremiumStartTime.
Something like that I believe, however, I am not sure how to implement it correctly in PHP. Any help is greatly appreciated. Thank you in advance!
days_remaining = PremiumDays - (NumberOfDaysSincePremiumStarted(DateToday - PremiumStartTime))
To get the difference in PHP, you can create a DateInterval object using DateTime::diff between the current time (output of date_create()) and a DateTime object created from your PremiumStartTime variable. You can then access the days value of this object to get the total number of days from the PremiumStartTime to the current time. For example:
$data['PremiumStartTime'] = '2018-12-12 21:13:00';
$data['PremiumDays'] = 20;
$days_remaining = $data['PremiumDays'] - date_create($data['PremiumStartTime'])->diff(date_create())->days;
echo number_format($days_remaining);
Output:
13
Demo on 3v4l.org
Do it in the DB as follows:
//SQL Server
$num_rows = $db->doQuery('SELECT PremiumDays, PremiumStartTime,
(PremiumDays - datediff(day,PremiumStartTime,getdate())) DaysRemaining
FROM Premium WHERE AccountID = ?', $_SESSION['AccountID']);
//MySQL
$num_rows = $db->doQuery('SELECT PremiumDays, PremiumStartTime,
(PremiumDays - datediff(now(),PremiumStartTime)) DaysRemaining
FROM Premium WHERE AccountID = ?', $_SESSION['AccountID']);
Then:
$replace = array
(
'account_status' => $data['Status'],
'days_remaining' => $data['DaysRemaining']
);
Proof here: https://www.db-fiddle.com/f/xzuc89C9gQUHpdTTy9M4vf/0
Or if you really want to do it in PHP:
$replace = array
(
'account_status' => $data['Status'],
'days_remaining' => $data['PremiumDays'] - date_diff(date_create(), date_create($data['PremiumStartTime']))->days
);

Compare a date in php and show the result

I've got the following function on my website:
function Active()
{
............
$num_rows = $db->doQuery('SELECT PremiumDays, PremiumStartTime FROM Premium WHERE AccountID = ?', $_SESSION['AccountID']);
if ($num_rows == -1)
{
$this->Error('ERROR');
$db->getError();
return;
}
$data = $db->doRead();
$data['Status'] = $num_rows == 0 ? '<:SHOW_PREMIUM_STATUS:>' : '<b><font size="2" color="red">Premium is active - <%Days_Remaining%> days remaining.</font></b>';
$replace = array
(
'account_status' => $data['Status'],
'days_remaining' => number_format($data['PremiumDays'])
);
$this->content = Template::Load('account-template', $replace);
}
PremiumDays column contains numbers like 10,15,30 etc.
PremiumStartTime contains a date in this format 2018-12-17 21:13:00
What I am trying to achieve is to show the actual days of premium remaining with days_remaining. So, I believe I need to substract from PremiumDays the days that passed since the premium started based on the second column PremiumStartTime.
Something like this:
days_remaining = PremiumDays - (NumberOfDaysSincePremiumStarted(DateToday - PremiumStartTime))
I will greatly appreciate if you can help me out with this. Thank you in advance!

PHP Auto Aging Task for MyBB

So, I was wondering if anybody would mind checking over this task and correcting it? I'm very sure I've muddled Python in with what little PHP I know, and that there are open tags.
Basically, there'll be a field where the nasty decimal age goes ($age, which will later be replaced by the appropriate field id). Our site works in months for juveniles and then years and seasons for adults. Using the nasty age, I'm trying to calculate the rounded age values and then store them as a string value which will then be set as the value of the field that will display the age ($displayagefid, will be replaced later with the appropriate field id). Only certain usergroups will be updated (the list is huge, so I left it out).
I also have no idea how to set a variable as a string using both string and the value of another variable.
Please know that I'm a complete newbie to PHP.
This is intended to run as a task on a self-hosted MyBB forum.
Thank you in advance!
<?php
function task_age($task)
{
global $mybb, $db;
$increment = 0.04167
$age = $age + $increment
floor($age*4) = $seasons
floor($age) = $years
floor($age*12) = $months
if ($year < 1) {
$display_age = $months, "mnths"
}
elseif ( ! filter_var($year, FILTER_VALIDATE_INT) ){
$display_age = $year, "yrs"
}
else {
$display age = $display_age = $years, "yrs", $seasons, "s"
};
$query = $db->query("
UPDATE mybb_userfields userfields
for ($usergroup = a || b || d || all that other crap) {
SET $dispalyagefid = $display_age;
};
");
add_task_log($task, "The age characters task successfully ran.");
I had a cursory look over your code and the first thing which sticks out is you have some of your variable assignments back to front:
$increment = 0.04167
$age = $age + $increment
floor($age*4) = $seasons
floor($age) = $years
floor($age*12) = $months
Whatever is on the left gets set to whatever is on the right, so your first two are OK but the last three need switching around.
Having said that it seems to me you are not approaching this correctly. I enter my decimal age into your site but how are you going to work out seasons? It might be my birthday tomorrow, it might have been my birthday yesterday.
You would be better off having the user enter a date of birth, from that calculate their age.
$birthday=date_create("2013-03-15");
$today=date_create('now');
$diff=date_diff($birthday,$today);
Now in the $diff variable you can check all the elements of a PHP date. So first check if they are under 18:
if ($diff->format("%y") < 18) {
$ageInMonths = ($diff->format("%y") * 12) + $diff->format("%m");
$age = "$ageInMonths months";
}
If they are over 18 you want age in years, then calculate seasons from the remaining months.
else {
$ageInYears = $diff->format("%y");
$ageInSeasons = floor($diff->format("%m") / 4);
if ($ageInSeasons > 0) {
$age = "$ageInYears years and $ageInSeasons seasons";
} else {
$age = "$ageInYears years";
}
}

MySQL order by time stored as HH:MM:SS

I have a varchar typed field which stores strings in this format HH:MM:SS i.e. 01:25:59 (and sometimes without HH part e.g. 25:59).
I want to have a descending order of results based on this time and for that I came with [str_to_date()][1] function and currently I'm using str_to_date($field_value,'%l:%i:%s') DESC to achieve this kind of sorting.
The odd thing is by using this format %l:%i:%s all posts having this field in MM:SS format are ordered correctly but those in HH:MM:SS aren't.
1-So if I have these values:
11:35
15:20
48:00
01:57:47
01:20:26
2-They are sorted as:
48:00
01:20:26
15:20
11:35
01:57:47
3-Which is wrong and should be:
01:57:47
01:20:26
48:00
15:20
11:35
As you see in (2) only times in format of HH:MM:SS are not placed correctly (DESC)
How can I have the right sorting?
What about this?
SELECT * FROM tbl
ORDER BY TIME_TO_SEC(IF(LENGTH(str_time)<6,CONCAT("00:",str_time),str_time)) DESC
fiddle demo: http://sqlfiddle.com/#!9/4b5da/3
This is your query:
SELECT IF(LENGTH( columnName ) >5, STR_TO_DATE(columnName, '%h:%i:%s'), STR_TO_DATE(columnName, '%i:%s')) as modDate
FROM `tableName` WHERE 1 order by modDate desc
SQL Fiddle: http://sqlfiddle.com/#!9/b6a52/1
wanna do something at application side ??? bit lengthy, but it works.
$tim_arr = array ('11:35', '15:20', '48:00', '01:57:47', '01:20:26');
$new_arr = array();
foreach ($tim_arr AS $tim){
$tim_chk = $key = '' ; $ntim_arr =array();
$tim_chk = substr_count($tim, ":");
$ntim_arr = explode(':',$tim);
if($tim_chk == 2){
$ntim = ( (int)$ntim_arr[0]*60 + (int)$ntim_arr[1] ).':'.$ntim_arr[2];
$key = ( (int)$ntim_arr[0]*60 + (int)$ntim_arr[1] );
}
else{
$ntim = $tim;
$key = $ntim_arr[0];
}
$new_arr[$key] = $ntim ;
}
krsort($new_arr);
foreach ($new_arr AS $tim)
{
$ntim_arr = explode(':',$tim);
if((int)$ntim_arr[0] >= 60){
echo str_pad(floor($ntim_arr[0] /60),2,"0",STR_PAD_LEFT).":".
str_pad($ntim_arr[0] %60,2,"0",STR_PAD_LEFT).":".$ntim_arr[1]."<br/>";
}
else{
echo $tim."<br/>";
}
}

Replacing redundant dates in DB query with symbol (MySQL/PHP)

I have a database query that displays a list of historic events in chronological order, like this:
(URL = MySite/Calendar/January_1)<br>
On this day in history...<br>
1968 - A volcano erupted.<br>
1968 - A country was invaded.<br>
1968 - Someone had a hit song.<br>
1970 - A famous person was born.
I'd like to know if there's a way to display a year just once, so the display looks like this:
1968 - A volcano erupted.<br>
• A country was invaded.<br>
• Someone had a hit song.<br>
1970 - A famous person was born.
Let's start with a database table (calendar_px) that lists the dates of various historic political events. The table has five fields -
1) N (a simple numerical key)
2) URL (values - such as May_1 - match page URL's)
3) Year (e.g. 1970, but the field type is INT, not Year, which only goes back to 1901)
4) Brief (some brief content)
5) Date (field type will be either date or datetime; I'm not actually using this field at the moment)
Here's what my code looks like (where $MyURL equals the page URL; e.g. January_1):
$stm = $pdo->prepare("SELECT Cal2.N, Cal2.URL, Cal2.Date, Cal2.Year, Cal2.Brief
FROM calendar_px Cal2
WHERE Cal2.URL = :MyURL
ORDER BY Cal2.Year");
$stm->execute(array(
'MyURL'=>$MyURL
));
while ($row = $stm->fetch())
{
$Year = $row['Year'];
$Brief[] = ''.$Year.' – '.$row['Brief'].'';
}
Then, I display a list of historic events like this...
echo join( $Brief, '<br>' );
I don't think it really changes anything, but I should mention that I have a similar set up on several websites; everything is the same except for the table names:
calendar_gw, calendar_gz, calendar_gs, calendar_px, calendar_sl
Accordingly, I've joined all five tables together with a UNION command. Here's a portion of the query:
$stm = $pdo->prepare("SELECT CGW.N, CGW.URL, CGW.Date, CGW.Year, CGW.Brief
FROM calendar_gw CGW
WHERE CGW.URL = :MyURL
UNION ALL
SELECT CGZ.N, CGZ.URL, CGZ.Date, CGZ.Year, CGZ.Brief
FROM calendar_gz CGZ
WHERE CGZ.URL = :MyURL
UNION ALL
SELECT CSL.N, CSL.URL, CSL.Date, CSL.Year, CSL.Brief
FROM calendar_sl CSL
WHERE CSL.URL = :MyURL
ORDER BY Year");
$stm->execute(array(
'MyURL'=>$MyURL
));
Anyway, my goal is the same; to replace redundant dates (years) with some sort of "dingbat" or symbol.
$prevYear = null;
while ($row = $stm->fetch())
{
$Year = $row['Year'];
if ($Year == $prevYear) {
$YearStr = '• ';
} else {
$YearStr = $Year . ' $#8211; ';
$prevYear = $Year;
}
$Brief[] = $YearStr.$row['Brief'];
}
P.S. You don't need to concatenate '' at each end of the string.
Looks like you need to group resultset before outputting:
$events = array();
while($row = $stm->fetch()){
$year = $row['Year']; // current year
if(!isset($events[$year]){ // if no such group
$events[$year] = array(); // create group
}
$events[$year][] = $row['Brief']; // add data to year group
}
// Output:
foreach($events as $year => $event){
echo $year, ':<br>'; // show year;
foreach($event as $data){
echo $data, '<br>'; // show row;
}
}
Also, you may change output to your model, easily:
foreach($events as $year => $event){
echo $year, ' • ', implode('<br>• ', $event);
}

Categories