If I want to do something like this (notice the expires line):
$duration=24; //in hours
$reset=new PasswordReset();
$reset->code=md5(uniqid());
$reset->expires="now() + interval $duration hours";
$reset->User=$user;
$reset->save();
How can I get Doctrine to NOT quote it before sending it to postgresql? I know I could do this in PHP, but I'd like to know for future reference.
You might want to take a look at Using Expression Values
The given example looks like this (quoting the doc) :
$user = new User();
$user->username = 'jwage';
$user->updated_at = new Doctrine_Expression('NOW()');
$user->save();
And generates this SQL query :
INSERT INTO user (username, updated_at_) VALUES ('jwage', NOW())
I Suppose it would do, in your case ?
(I don't have a Doctrine-enabled project right here, so can't test for you)
As a sidenote : I don't have a PostGreSQL server to test on ; but what you propose doesn't work on MySQL : it seems you have to use "now() + interval 2 hour", and not "now() + interval 2 hours" -- still, I don't know about PG
EDIT after the comment
Ergh, you're right, this is not a correct solution ; it doesn't work :-(
Well... Interesting question, so I dug a bit more, and went to Doctrine's source-code ; I think I may have find something interesting.
If you look at the source of Doctrine_Query_Where::parseValue source, you'll notice this portion of code :
// If custom sql for custom subquery
// You can specify SQL: followed by any valid sql expression
// FROM User u WHERE u.id = SQL:(select id from user where id = 1)
} elseif (substr($trimmed, 0, 4) == 'SQL:') {
$rightExpr = '(' . substr($trimmed, 4) . ')';
// simple in expression found
I have absolutly not tried, but this might be interesting...
Maybe you could do something like this :
$reset->expires="SQL:(now() + interval $duration hours)";
If you try that, I'm very interested in knowing if it would work!
Might be useful, one day or another ;-)
BTW, it seems it's used in Doctrine_Search too ; looking at this one, maybe it'll work without the parentheses, like this :
$reset->expires="SQL:now() + interval $duration hours";
Well... I Hope, this time, it helps... Because I don't see much other way to do what you're trying to get (and google doesn't help me either ^^ )
EDIT after the second (third, if counting mine) comment.
I will get this working... else I won't sleep well ^^
Well, I might have found a way (and, this time, I tested it ^^ ) ; not really as great as one would like, but, for updates anyway, it seems to be working...
Let's say I have a table created this way :
CREATE TABLE `test1`.`test` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(32) NOT NULL,
`value` varchar(128) NOT NULL,
`date_field` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
The corresponding model class looks like this :
Probably not perfect : it's a test-class I've written for something else, that I've mutated to fit this one ^^
class Test extends Doctrine_Record
{
public function setTableDefinition()
{
$this->setTableName('test');
$this->hasColumn('id', 'integer', 4, array(
'type' => 'integer',
'length' => 4,
'unsigned' => 0,
'primary' => true,
'autoincrement' => true,
));
$this->hasColumn('name', 'string', 32, array(
'type' => 'string',
'length' => 32,
'fixed' => false,
'notnull' => true,
));
$this->hasColumn('value', 'string', 128, array(
'type' => 'string',
'length' => 128,
'fixed' => false,
'primary' => false,
'notnull' => true,
'autoincrement' => false,
));
$this->hasColumn('date_field', 'integer', 4, array(
'type' => 'timestamp',
'notnull' => true,
));
}
}
I'll insert two lines into this table :
$test = new Test();
$test->name = 'Test 1';
$test->value = 'My Value 1';
$test->date_field = "2009-01-30 08:30:00";
$test->save();
$test = new Test();
$test->name = 'Test 2';
$test->value = 'My Value 2';
$test->date_field = "2009-02-05 08:30:00";
$test->save();
Which gets me this data from SQL :
BTW : I don't have a pg server, so I'll test everything with MySQL -- should work on pg too, still...
mysql> select * from test;
+----+--------+------------+---------------------+
| id | name | value | date_field |
+----+--------+------------+---------------------+
| 1 | Test 1 | My Value 1 | 2009-01-30 08:30:00 |
| 2 | Test 2 | My Value 2 | 2009-02-05 08:30:00 |
+----+--------+------------+---------------------+
2 rows in set (0.00 sec)
So, two lines, with dates long ago in the past.
Now, to be sure, let's just fetch the line #1 :
$testBefore = Doctrine::getTable('Test')->find(1);
var_dump($testBefore->toArray());
I'm getting this kind of output :
array
'id' => string '1' (length=1)
'name' => string 'Test 1' (length=6)
'value' => string 'My Value 1' (length=10)
'date_field' => string '2009-01-30 08:30:00' (length=19)
And, now, the interesting part : let's update that line, using an expression like the one you provided to set the date_field value :
$query = new Doctrine_Query();
$query->update('test')
->set('date_field', 'NOW() - interval 2 hour')
->where('id = ?', 1)
->execute();
var_dump($query->getSql());
The SQL that I get as output is this one :
string 'UPDATE test SET date_field = NOW() - interval 2 hour WHERE id = ?' (length=65)
Which kinda look like what you want, if I'm not mistaken ;-)
And, just to be sure, let's fetch our line once again :
$testAfter = Doctrine::getTable('Test')->find(1);
var_dump($testAfter->toArray());
And I get this result :
array
'id' => string '1' (length=1)
'name' => string 'Test 1' (length=6)
'value' => string 'My Value 1' (length=10)
'date_field' => string '2009-08-04 21:26:30' (length=19)
Considering the date and time, it seems this worked -- hoorray !
And, to be sure, let's query the data directly from the DB :
mysql> select * from test;
+----+--------+------------+---------------------+
| id | name | value | date_field |
+----+--------+------------+---------------------+
| 1 | Test 1 | My Value 1 | 2009-08-04 21:26:30 |
| 2 | Test 2 | My Value 2 | 2009-02-05 08:30:00 |
+----+--------+------------+---------------------+
2 rows in set (0.00 sec)
And... Yeeeepe !
Well, now, the not so good parts : to be able to use that syntax, I had to create the query "by hand", to use the set() method, instead of doing it "nicely" with the model class and the save() method :-(
It's now up to you to see you that could be integrated into your model class... Have fun with that ;-)
And if you find a way, some day, to use expressions like this one in other parts of the query, or to do it a cleaner way, I'd really appreciate if you could post a comment to let me know ;-)
And, this, time, I sincerely hope I found the way ^^
Related
I have a very specific problem. Even with the great CakePHP doc, I still don't know how to fix my pb.
I'm currently web developping using the CakePHP framework. Here is my situation :
I have a Table "TableA" which contains parameters "name", "type"(1 to 6) and "state"(OK and NOT OK) . What I want is getting all the Table lines which are type 5 OR 6 and which have not a same name line with "state" OK.
There are different lines of the table which have the same "name". I'm interesting to the lines from the same name where there is no OK state.
For example, there are :
name : example1 state : NOT OK
name : example1 state : NOT OK
name : example1 state : NOT OK
And there is no example1 with the state OK and this is this kind of line I want to get.
I would like to do this with the cakePHP syntax, with conditions in the TableRegistry::get function.
Thanks for helping. Waiting for your return.
PS:
What I achieved now is not the best solution :
$tablea_NOTOK = TableRegistry::get("TableA")->find('all', array(
'conditions' => array(
'OR' => array(
array('TableA.type' => 5),
array('TableA.type' => 6),
),
'Etudes.state' => 'NOT OK'
)
));
$this->set(compact('tablea_NOTOK'));
$tablea_OK = TableRegistry::get("TableA")->find('all', array(
'conditions' => array(
'OR' => array(
array('TableA.type' => 5),
array('TableA.type' => 6),
),
'Etudes.state' => 'OK'
)
));
$this->set(compact('tablea_OK'));
And then in my view, i compared each line of the tablea_OK with the tablea_NOTOK. But there is a lot of data so the code is not perfect and slow
You may consider creating a view table in your database which holds the combination of data needed. Since the data will all be from a single table, you wouldn't need to loop through the data and compare it.
I don't know all your table relationships, but I made a simple table with these fields and data:
id name type state
1 Harry 5 OK
2 Harry 6 NOT OKAY
3 Harry 6 NOT OKAY
4 John 5 NOT OKAY
Then I wrote a query which would group by name and count the state values:
SELECT `name`, `type`, `state`,
(SELECT COUNT(state) FROM TableA as TableA1 WHERE `state` = 'OK' AND TableA.name = TableA1.name) as okay_count,
(SELECT COUNT(state) FROM TableA as TableA2 WHERE `state` = 'NOT OKAY' AND TableA.name = TableA2.name) as not_okay_count
FROM TableA
GROUP BY name;
The results look like this:
name type state okay_count not_okay_count
Harry 5 OK 1 2
John 5 NOT OKAY 0 1
You can adjust the query as needed and create your database view table and then call that in CakePHP.
$my_view_table = TableRegistry::get("MyViewTable")->find('all');
You can learn more about MySQL view tables here
I have access to a database similar to this:
users:
id | name
====|========
1 | Tom
2 | Dick
3 | Harry
4 | Sally
exlusions:
user_id | exclusion
========|==============
1 | French only
3 | No magazine
4 | English only
1 | No magazine
Is it possible to query the database to get a result like this?
$users = [
[
'id' => 1,
'name' => 'Tom',
'english_only' => false, // unset or null would be okay
'french_only' => true,
'no_magazine' => true,
],
// . . .
];
I've been playing around with things like GROUP_CONCAT, PIVOT, and other examples and can't figure out how or if any of it applies.
SQL Fiddle -- thought I could modify this to match my scenario. I didn't get all that far, and as you can see I have no idea what I am doing...
You can use IF in your select to make your 3 columns out of the 1 based on their values.
SELECT id, name,
IF(exclusion='English only',true,false) as english_only
IF(exclusion='French only',true,false) as french_only
IF(exclusion='No magazine',true,false) as no_magazine
FROM users, exclusions
WHERE users.id=exclusions.user_id
I started with #RightClick's answer, but had to tweak it a bit for my SQL server.
SELECT User.id, User.name,
CASE WHEN Ex.exclusion = 'English Only' THEN 1 ELSE 0 END as english_only,
CASE WHEN Ex.exclusion = 'French Only' THEN 1 ELSE 0 END as french_only,
CASE WHEN Ex.exclusion = 'No magazine' THEN 1 ELSE 0 END as no_magazine
FROM users as User
LEFT JOIN exclusions Ex on User.id = Ex.user_id;
So much simpler than what I thought it was going to be after googling and searching SO all day...
I'm trying to create a kind of REGEX with dynamic variables
with PHP using some pre-defined values stored in a Database table, in order to
validate a Dial String from Asterisk.
The database has
a table for outgoing rules, which each user may create to be applied to his/her extension.
Variables in PHP can be like this:
$N = '[23456789]';
$Z = '\d*'; //(any digit, any occurrence)
$X = '[0-9]';
$x = '[0-9]';
numbers in the rule will be considered as number itself
symbols in the rule will be considered as symbol itself (only * and # will be accepted in the database as symbols)
The table is something like this:
+----+-----------+-------+-------------+------------+----------+-----------+--------+--------+
| id | extension | order | description | ruledigits | ruletype | subtract | prefix | suffix |
+----+-----------+-------+-------------+------------+----------+-----------+--------+--------+
| 1 | 1005 | 1 | | 9XX | Block | null | null | null |
| 2 | 1005 | 2 | | 302NXXXXXX | Mod | null | 1 | null |
| 3 | 2005 | 1 | | 00Z | Mod | 2 | 011 | null |
+----+-----------+-------+-------------+------------+----------+-----------+--------+--------+
So, if 1005 Extension DIALS 908 (no more digits), that call should be blocked
according to the Action field. if 1005 Extension Dials 3025555555 (no more
digits), that Call will be prefixed with number 1.
Iif 1005 Extension Dials 00325698289115 (international), that Call will NOT
me modified according to Rule # 3, since that rule ONLY applies to EXT 2005, so
the CALL will be sent as it was Dialed to the Server.
If that same last Call was made by 2005, the number would MATCH the rule,
since it begins with 00 and Z is ANY DIGIT ANY OCURRENCE. So, the dialed number will be Subtracted "2" digits from the beginning, and then prefixed with "011" before being sent to the trunk.
The important field here I think is ruledigits (and of course extension),
which will be used by PHP for the ARRAY of rules per EXT. then the action will
be read only if the condition was meet.
And the condition something like this, although this one only represents just
1 rule and case:
if(preg_match("/^1$N$X$N(555)$Z/", $number))
{
// $number format matches
echo "yes"; // apply the action according the table
}
else
{
echo "no"; // no action applied, proceed with the call.
}
I would need condition(s) that must be created on the fly as soon as the actual
Call (requesting the access to the database through a php script) finds that
there are not just 1, but some rules created for THAT extension individually.
How can I make or design a General function that can Access the database "rules"
table, and ARRAY the group of all the conditions created under the caller
extension in order to apply them to the actual call?
Note: The field
"ruledigits" on the table "rules" will only accept the following characters:
N, Z, *, #, or Any number.
Here's how I would personally approach using MySQL data to build Regex patterns for checking data:
//Simplified array
$dbResults = array(
array(
'id' => 1,
'extension' => 1005,
'order' => 1,
'description' => 'rule 1',
'ruledigits' => '9XX',
'prefix' => null,
'ruletype' => 'Block'
),
array(
'id' => 2,
'extension' => 1005,
'order' => 2,
'description' => 'rule 2',
'ruledigits' => '302NXXXXXX',
'prefix' => 1,
'ruletype' => 'Mod'
),
array(
'id' => 3,
'extension' => 2005,
'order' => 3,
'description' => 'rule 3',
'ruledigits' => '00Z',
'prefix' => '001',
'ruletype' => 'Mod'
)
);
$regexParts = array(
'N' => '[2-9]'
,'Z' => '\d*'
,'X' => '\d'
,'x' => '\d'
);
//Static test vars
$userExt = 1005;
$dialTests = array('00325698289115','908','3025555555');
echo 'Testing user extension: '.$userExt;
echo '<br /><br />';
//This loop is for testing purposes only, the contents are all the live system would use
foreach($dialTests as $testNo)
{
$actionTaken = 'None';
//By default, if nothing happens, make sure the final number is the original one we saw
$finalNo = $testNo;
foreach($dbResults as $row)
{
if($userExt != $row['extension']) continue;//If it's not the right extension, skip the rest of this loop iteration's code and move on to the next row
$regex = '';
$tokens = str_split($row['ruledigits']);//Turn the string into an array, so we can parse each character individually
foreach($tokens as $token)
{
if(isset($regexParts[$token])) $regex .= $regexParts[$token];//If the letter has a special meaning, use that
else $regex .= $token;//else just throw that exact letter/number in
}
if(preg_match('#^'.$regex.'$#',$testNo)>0)
{
$actionTaken = $row['ruletype'];//Log what action has been taken
if($actionTaken=='Mod')
{
$finalNo = $row['prefix'].$testNo;//Do the mod action
}
else if($actionTaken=='Block')
{
$finalNo = false;//Nullify the final number, so we know to block the call later on
}
}
}
//Here we just dump some info for testing purposes
echo $testNo.': Action taken = '.$actionTaken;
if($actionTaken=='Block') echo ' - Call terminated.';
if($actionTaken=='Mod') echo ' - New number = '.$finalNo;
echo '<hr />';
}
View the output demo on phpfiddle (click Run/F9)
You can use asterisk realtime architecture and correct view to match extensions table.
Or you can use mysql query with REGEXP/RLIKE construction.
https://dev.mysql.com/doc/refman/5.1/en/regexp.html
You can easy change asterisk regexp to general regexp by using simple rewrites like
'.' -> '.*'
'X' -> '[0-9]'
etc
I've been picking at my brain for a couple of days with the following conundrum.
I've basically the following complex mySQL table:
| ID | TITLE | DESCRIPTION | EVENT | DATE |
|----|--------------|-------------------|-------|-------------------------|
| 1 | Big painting | 3-day work | No | 03/10/2013 |
| 2 | Meeting | With reps. | Yes | 02/15/2013 09:00 -05:00 |
| 3 | Presentation | 5 paintings | Yes | 08/02/2013 22:00 +02:00 |
| 4 | Round paint. | One week | No | 04/05/2013 |
| 5 | Conference | On Picasso | Yes | 04/22/2013 18:00 -05:00 |
(EDIT: Perhaps I need to clarify that the DATE column is not set as DATE or DATETIME [due to the need to include the Timezone in the data] but rather as VARCHAR, which is why the organization is complicated from the start.)
As you can see, it's a table which comprises all "What's New" items for a painter, for example. The idea is that on this painter's website PHP will call out values in order to display them as a "What's New" news ticker.
All the basics are there but I'm hitting a snag.
Initially, I would like to filter and order my data at the SELECT stage, but I was having trouble doing that. As you can see, bot the event and non-event items have dates, but the non-event items just have them as a way to organize the overall data. The specific date and time is only important for the events, because that way the user will be able to know when these things are occurring. So the basic idea would be to pick out from the table a certain number of the LATEST items. So all items would be read from the table, placed in order of DATE, and then, say, 20 of them would be brought out.
As I said, I had initially though of doing this at the SELECT stage, but I think it might be too complex. So I just extracted all items and set a PHP code after them in order to filter them.
So the snag appears when I try to order the dates. I'm trying to convert dates to timestamps for the ordering process, then back to dates for the displaying. However, I can't get the date-timestamp or timestamp-date (or both) conversion to work. I always end up with dates that are different to those I started with.
As you can see, the entire thing is made more complex because of the Timezones, which are very important, as a user must be able to know where these things are happening or, rather, at what time according to where they're happening.
I've tried simply converting back and forth like so:
$timestamped = strtotime($date);
$datetimed = date('m/d/Y h:i P',$timestamped);
And it doesn't work, so I guessed it had something to do with the date format I'm using in my table.
So then I tried this:
$var = DateTime::createFromFormat('m/d/Y H:i P',$date)->getTimestamp();
To no avail, yet...
I'm thinking that perhaps I should rather set the timestamp at the beginning of the process, i.e. when inserting the data items. But, here also, I would need to convert a "human" date to a timestamp, and if I can't manage this correctly, nothing will properly work.
I understand this question is complex and perhaps my explanation isn't the clearest! Perhaps an output example might help. What I'm trying to achieve is a "What's New" news ticker that includes a list of things that are going on: some of them just information (like status updates, lets say) without visible dates (here dates are only for internal organization; this is where the EVENT column comes in, as it filters which items must show their dates and which must not), others, actual events to which the user is invited and which display their dates for the user. I even have a code that calculates whether the date and time is in the past or the future in order to display an "UPCOMING" tag on those events that are not yet past. However, I'm having trouble handling the dates and ordering them.
This is what this example should more or less look like at the end:
Any and all help will be GREATLY appreciated! (As well as feedback on what you guys think will be the most practical and most clean/elegant/pro way of handling this data retrieval/organization process... if at data input, if at mySQL SELECT stage, if later, etc.)
P.S. I might perhaps add that in the same table I handle other data. This specific "What's New" data is selected by a SELECT function that looks for a specific WHATSNEW column to have a value of TRUE in any row that will be retrieved for this specific "What's New" news ticker.
RESOLUTION (THOUGH NOT ANSWER)
Because the question was about organizing times and timezones as one string, so to speak, then I'm not sure I can mark any of these two great answers as correct for that specific issue. What I did end up doing was stripping the timezones and putting them in a separate column. Then I formatted my date column as Datetime. So I had that solved, because mySQL Select could take care of the order for me. Now, about the timezones, I ended up cheating a bit: I figured our "artist" couldn't possibly be at two events in two different timezones at the same time, so, really, it's rather improbable that we would need to order two events that are so close together that the timezones of each make a real difference which comes first. So I just have the thing order itself by dates and then whipped up a snippet to take care of the timezones and turn them into "GMT+1", "GMT-5" displays, so the users will know where the location is of that local time. I ended up using DateTime::createFromFormat()->getOffset(), which I've now seen my second answerer recommended, and said I was on the right track, so I'm happy I kept it in there In order to further clarify this, I added a Location column, where the webmaster will be able to specify the city, say "Paris", say "London", and so on. So the user will end up having something very similar to that which is shown in my example, except that it will say ... (Paris, GMT+1) and so on.
Anyway, for anyone out there that has the exact same issue and ends up thinking the exact same things and that this way out is more practical, here goes the heart of the code I ended up with. The rest is just "fill-in". Enjoy! And thanks to both darling persons who were so kind as to take time from their days to help me out in finding a resolution for this issue! (I may have an extra }... sorry for that. The re-formatting when one pastes it into SO is really tedious! I've revised the code twice and can't find any problems, though.)
if(isset($item) && $item['event'] == '1') {
$event = $item['event'];
$date = $item['date'];
$date_array = date_parse($date);
$minute = $date_array['minute'];
if($minute<10) {
$minute = '0'.$minute;
}
$timezone = $item['timezone'];
if($timezone!=='') {
$timezone = DateTime::createFromFormat('P',$timezone)->getOffset();
$timezone = $timezone/-3600;
if($timezone<0) {
$timezone = $timezone;
} else
if($timezone==0) {
$timezone = '-0';
} else {
$timezone = '+'.$timezone;
}
$timezone = 'Etc/GMT'.$timezone;
$timezone_real = $item['timezone'];
$timezone_real = DateTime::createFromFormat('P',$timezone)->getOffset();
$timezone_real = $timezone_real/-3600;
if($timezone_real<0) {
$timezone_real = str_replace('-','+',$timezone_real);//.':00';
} else
if($timezone_real==0) {
$timezone_real = '+0';//:00';
} else {
$timezone_real = '-'.$timezone_real;//.':00';
}
$timezone_real = 'GMT'.$timezone_real;
date_default_timezone_set($timezone);
}
$today = date('n/j/Y G:i', time());
$today = strtotime($today);
$event_date = $date_array['month'].'/'.$date_array['day'].'/'.$date_array['year'].' '.$date_array['hour'].':'.$minute;
$event_date_unformatted = strtotime($event_date);
if($date_array['hour'] == '0') {
$hour_convert = '12';
$hour_suffix = 'a.m.';
} else if($date_array['hour']<12) {
$hour_convert = $date_array['hour'];
$hour_suffix = 'a.m.';
} else if($date_array['hour'] == '12') {
$hour_convert = $date_array['hour'];
$hour_suffix = 'p.m.';
} else {
$hour_convert = $date_array['hour']-12;
$hour_suffix = 'p.m.';
}
$date_convert = array('1' => 'January', '2' => 'February', '3' => 'March', '4' => 'April', '5' => 'May', '6' => 'June', '7' => 'July', '8' => 'August', '9' => 'September', '10' => 'October', '11' => 'November', '12' => 'December');
$event_date = $date_convert[$date_array['month']].' '.$date_array['day'].', '.$date_array['year'].', '.$hour_convert.':'.$minute.' '.$hour_suffix;
if(($event_date_unformatted-$today)>0) {
echo '<h5>UPCOMING:</h5>';
echo '<h6>'.$item['location'].', '.$event_date.' <sup>('.$timezone_real.')</sup></h6>';
}
}
You say that DateTime::createFromFormat doesn't work, but don't tell us what the error message is. I guess its because your format string doesn't represent the format you are passing. See the manual page for acceptable formats.
Having said that, I believe that you were on the right track with DateTime::createFromFormat, that is the approach I would take. Somebody with more powerful SQL foo than mine could probably come up with a way of doing this with a query, but here is my purely PHP approach to getting you an array of events sorted on date:-
//first we connect to our database
$dsn = 'mysql:dbname=stackoverflow;host=127.0.0.1';
$user = '********';
$password = '*********';
try {
$dbh = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
//then we get our list of events from the table
$sql = "select * from datetable";
$select = $dbh->prepare($sql);
$select->execute();
//We now have an associative array of events, but in the wrong order.
$results = $select->fetchAll(PDO::FETCH_ASSOC);
$events = array();
//we want to sort on date, so lets get the dates into a format we can sort on
foreach($results as $result){
$event = $result;
$date = explode(' ', $result['date']);
if(isset($date[1])){
$event['date'] = \DateTime::createFromFormat('m/d/Y H:i', $date[0] . ' ' . $date[1]);
$event['tz'] = $date[2];
} else {
$event['date'] = \DateTime::createFromFormat('m/d/Y', $date[0]);
}
$events[] = $event;
}
//our sorting function
$byDate = function(array $eventA, array $eventB){
return $eventB['date'] > $eventA['date'] ? -1 : 1;
};
//sort the array
usort($events, $byDate);
//we now have our array sorted correctly
var_dump($events);
Result:-
array (size=5)
0 =>
array (size=6)
'id' => string '2' (length=1)
'title' => string 'Meeting' (length=7)
'description' => string 'With reps.' (length=10)
'event' => string 'Yes' (length=3)
'date' =>
object(DateTime)[4]
public 'date' => string '2013-02-15 09:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/London' (length=13)
'tz' => string '-05:00' (length=6)
1 =>
array (size=5)
'id' => string '1' (length=1)
'title' => string 'Big painting' (length=12)
'description' => string '3-day work' (length=10)
'event' => string 'No' (length=2)
'date' =>
object(DateTime)[3]
public 'date' => string '2013-03-10 23:18:05' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/London' (length=13)
2 =>
array (size=5)
'id' => string '4' (length=1)
'title' => string 'Round paint.' (length=12)
'description' => string 'One week' (length=8)
'event' => string 'No' (length=2)
'date' =>
object(DateTime)[6]
public 'date' => string '2013-04-05 23:18:05' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/London' (length=13)
3 =>
array (size=6)
'id' => string '5' (length=1)
'title' => string 'Conference' (length=10)
'description' => string 'On Picasso' (length=10)
'event' => string 'Yes' (length=3)
'date' =>
object(DateTime)[7]
public 'date' => string '2013-04-22 18:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/London' (length=13)
'tz' => string '-05:00' (length=6)
4 =>
array (size=6)
'id' => string '3' (length=1)
'title' => string 'Presentation' (length=12)
'description' => string '5 paintings' (length=11)
'event' => string 'Yes' (length=3)
'date' =>
object(DateTime)[5]
public 'date' => string '2013-08-02 22:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/London' (length=13)
'tz' => string '+02:00' (length=6)
The main issue still outstanding is the timezones. Your database stores offsets, not timezones. There are some questions on this on SO (this one for example), so I won't double up on those efforts here, but the offsets are available in the array should you find a way to us them.
I noticed in your comments that you are considering adding a time zone column, this is a good idea. However, I would advise you to store them as TZ strings from this list, they can then be passed directly into the constructor of DateTimeZone to give you advantages such as allowance for daylight savings etc.
The SQL query should be
$data= mysql_query( "SELECT * FROM (table name) ORDER BY date ASC")
I am adding an algorithm given your latest comments.
Firstly:
Your data type for the DATE column needs to be uniform, If it is of the datetime or timestamp format it should order the data correctly.
This link provides you with a comprehensive list of date and time functions. It is worth reading to give you a better feel for solving your problem.
http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html
See this link that addresses the correct formatting of dates - (however for a cleaner table, I suggest you have only the one format to be accepted into your table):
Order by descending date - month, day and year
For online use:
I think somehow you need to either "standardise" the timezones. The timestamp can be created within the same timezone for all events, the user could then view these times according to their chosen timezone., hence all the times are adjusted for each user.
this link discusses session variables for timezones:
http://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html
Per-connection time zones. Each client that connects has its own time zone setting, given by the session time_zone variable. Initially, the session variable takes its value from the global time_zone variable, but the client can change its own time zone with this statement:
mysql> SET time_zone = timezone;
However for local use, where an individual may receive a flyer of the updates, with a view to attending them, then you need to display the timezone for that locality, with the locality mentioned. Adding for eg GMT +2.00 does not give many users an indication of the time with reference to their own timezone, they would usually have to convert it themselves. This puts an added frustration for the user. It would be worth the effort to convert it for them, or offer some explanation for the time differences - so the user can get a clear understanding of "when" this event is happening with respect to their timezone.
To deal with the timezones, it is worth going to this link:
http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_convert-tz
CONVERT_TZ() converts a datetime value dt from the time zone given by from_tz to the time zone given by to_tz and returns the resulting value. Time zones are specified as described in Section 10.6, “MySQL Server Time Zone Support”. This function returns NULL if the arguments are invalid.
If the value falls out of the supported range of the TIMESTAMP type when converted from from_tz to UTC, no conversion occurs. The TIMESTAMP range is described in Section 11.1.2, “Date and Time Type Overview”.
mysql> SELECT CONVERT_TZ('2004-01-01 12:00:00','GMT','MET');
-> '2004-01-01 13:00:00'
mysql> SELECT CONVERT_TZ('2004-01-01 12:00:00','+00:00','+10:00');
-> '2004-01-01 22:00:00'
Note
To use named time zones such as 'MET' or 'Europe/Moscow', the time zone tables must be properly set up. See Section 10.6, “MySQL Server Time Zone Support”, for instructions.
I think if you add an extra column, this will help your problem.
I think by
checking your data types
standardising your timezones
Adding another column to show the timezone
you will have no trouble ordering your table.
I hope this helps. Please let me know.
I need a way to determine the type of a database column (varchar/numeric/date/...) when reading from the DB with PDO.
When fetching values from the DB, PDO produces only string values, regardless of the actual type of the table column.
Is there any non driver specific way to get this information? I know that there are SQL statements that retrieve the types for any given table but i'd prefer a more generic solution.
EDIT:
PDOStatement::getColumnMeta() is of no use to me, because it's not supported by the PDO driver I use at the moment (Oracle).
Take a look at this method: PDOStatement->getColumnMeta
This is how I did it in my WraPDO class:
$tomet = $sth->getColumnMeta($column_index);
$tomet['type'] = $this->_translateNativeType($tomet['native_type']);
private function _translateNativeType($orig) {
$trans = array(
'VAR_STRING' => 'string',
'STRING' => 'string',
'BLOB' => 'blob',
'LONGLONG' => 'int',
'LONG' => 'int',
'SHORT' => 'int',
'DATETIME' => 'datetime',
'DATE' => 'date',
'DOUBLE' => 'real',
'TIMESTAMP' => 'timestamp'
);
return $trans[$orig];
}
$sth: PDOStatement->getColumnMeta
It's marked as "experimental", but the PDOStatement->getColumnMeta method looks like it will do what you want.
I wrote a function a while ago which extracted table column information. I ended up doing something like this:
SHOW COLUMNS FROM <table> WHERE Field = ?
For a typical primary key, that produces this:
+-------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
+-------+---------+------+-----+---------+----------------+
I then parsed the output into a usable array. However, that was pre-PHP 5.1.0. Now you can probably use PDOStatement->getColumnMeta.
If you're working with Oracle:
select COLUMN_NAME,
DATA_TYPE,
DATA_LENGTH,
DATA_PRECISION,
DATA_SCALE
from user_tab_cols
where table_name = '<Table Name>'
order by column_id
but it isn't portable
Many SQL flavours also have
DESCRIBE <Table Name>
If you're working with Postgres:
select
CHARACTER_MAXIMUM_LENGTH,
COLUMN_NAME,
IS_NULLABLE,
COLUMN_DEFAULT,
NUMERIC_PRECISION,
NUMERIC_SCALE,
UDT_NAME
from
INFORMATION_SCHEMA.COLUMNS
where
TABLE_NAME='table_name'