Symfony to set a DateTime in MySQL database - mysql

Question:
In a DB web app, Date/Time field is very common.
When in Symfony, you tried to do either of the follwings:
$creationDate=new DateTime();
$record->setCreationDate($creattionDate);
This will create a "Object DateTime can't be converted to string" error.
Or:
$creationDate=new DateTime();
$datestr=$creationDate->format('Y-m-d H:i:s');
$record->setCreationDate($datestr);
This will create "Calling method format() on a non-ojbect" error.

In your entity you have to map your field with date or datetime type.
#ORM\Column(name="creationDate", type="date", nullable=true)

Solution:
Hack the DateTimeType.php located at: your symfony root/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php for the function named convertToDatabaseValue
Original content:
return ($value !== null)
? $value->format($platform->getDateTimeFormatString()) : null;
It seems Symfony is doing some extra conversion when the parameter is already a string.
Change to:
return ($value !== null)
? $value : null;
will do the trick.

Related

Doctrine2 converts date type value to SQL format in wrong way

I have not yet understood where concretely is the problem is...
I have a Doctrine entity with date field:
/**
* #var \DateTime
*
* #ORM\Column(name="day", type="date")
*/
private $day;
Also I have an entity form with date type field:
$builder->add('day', DateType::class, ['widget' => 'single_text'])
And when I try to save the form with value "2016-02-14" I see that it becomes "2016-02-13" (a day earlier) in MySQL and in PHP after saving. When I began looking for logs, I saw that the query parameter value is "2016-02-13 23:00:00".
But I don't understand why it happens this way.
I have the same time and timezone in system (Ubuntu), PHP and MySQL (Europe/Moscow timezone).
I use date type, not datetime, so there should not be time at all.
When I tried to debug it, I saw that code
#vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return ($value !== null)
? $value->format($platform->getDateFormatString()) : null;
}
works correctly. It makes correct format "Y-m-d", but in the symfony log value is with time.
I need an advice about how to find, where my date transforms.
I had a similar problem and used a quick-and-dirty hack. On the setter used for the $day property, just set the time to be midday.
public function setDay(\DateTime $day)
{
$this->day = $day;
$this->day->setTime(12, 0, 0);
return $this;
}

Dynamically create new columns in laravel

I'm trying to allow admins to create new columns in a sql table from a form that has a fields for the column title, column type and target table. I'm sure I'm not doing this in the most elegant way possible but I'm trying to use the framework rather than have everyone beat me up for directly querying the database. I've created the following controller that almost completely works, however, when I try to use $new_column rather a hard coded string I get an undefined variable exception.
//Capture variables from view
$type = Input::get('type');
$table_name = Input::get('table');
$proposed_name = Input::get('name');
//Convert proposed name into useable column name
$new_column = strtolower(str_replace(' ', '_', (preg_replace('/[^a-zA-Z0-9_ -%][().][\/]/s', '', $proposed_name))));
if($type == 'string')
{Schema::table($table_name, function($table){$table->string($new_column);});}
elseif($type == 'date')
{Schema::table($table_name, function($table){$table->date($new_column);});}
...
//Flash Success
$message = 'Variable "' . $proposed_name . '"" has been successfully created.';
Session::flash('flash_success', $message);
return Redirect::action('VariableManagerController#getIndex');
Is there a way to make this work through the Larval framework or should I just do a raw query to the database?
For the record this will be utilizing a try catch block but that would only further confuse the code above.
$alldata=Input::all();
$table_name = Input::get('table');
Schema::table($table_name, function($table) use ($alldata)
{
$colname=$alldata['name'];
$coltype=$alldata['type'];
$table->$coltype($colname);});
}

PDO query does not return data when inserting date as variable

Im trying to get a hold of OOP and PDO. Did some tutorials. In the tutorial i got the query method (so thats not mine...)
but im having troubles with a pdo query
I want to select orders from the database matching a date..... de date comes from a datepicker and returns 2012-12-16 for example therefor
$dateInputQuery = date("Y-m-d", strtotime(Input::get('datepick')));
$data = $order->getAllOrders('order', 'WHERE DATE(orderdate) = DATE({$dateInputQuery})', false, false);
the strange thing is that when i replace the WHERE clause to WHERE DATE(orderdate) = \'2013-12-16\' it returns all the data but when inserting my date like above it does not....
in the db class the method looks like this
public function getAll($table, $where = NULL, $orderSort = NULL, $limit = NULL) {
$this->query("SELECT * FROM {$table} {$where} {$orderSort} {$limit}")->error();
return $this;
}
and query method in db class
public function query($sql, $params = array()) {
//reset error
$this->_error = false;
if ($this->_query = $this->_pdo->prepare($sql)) {
$x = 1;
if (count($params)) {
foreach ($params as $param) {
$this->_query->bindValue($x,$param);
$x++;
}
}
if ($this->_query->execute()) {
$this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ);
$this->_count = $this->_query->rowCount();
} else {
$this->_error = true;
}
}
return $this;
}
why is this ?
Your immediate problem is caused the fact that $dateInputQuery is unquoted. Date is a string literal and should be quoted. And even though you can easily add quotes around it you really shouldn't do this. See next point.
order is a reserved word in MySQL, therefore the table name should be put in backticks
$data = $order->getAllOrders('`order`', "WHERE DATE(orderdate) = DATE('$dateInputQuery')", false, false);
^ ^ ^ ^
You're not leveraging parameter binding in query() function. Instead on top of it you're using query string interpolation leaving your code vulnerable to sql injections and diminishing the usage of prepared statements. When you use parameter binding you no longer need to quote parameter values.
Your sql query is not index-friendly. You shouldn't apply any functions (in your case DATE()) to the column you're searching on (orderdate). Instead you can rewrite your condition to apply necessary transformations/calculations to the arguments which are constants.
You should avoid using SELECT *. Read Which is faster/best? SELECT * or SELECT column1, colum2, column3, etc and Why is using '*' to build a view bad?
That being said your query should look something like
$sql = "SELECT order_id, orderdate, ...
FROM `order`
WHERE orderdate >= ?
AND orderdate < ? + INTERVAL 1 DAY";
And you should execute it
$this->query($sql, array($dateInputQuery, $dateInputQuery));
Instead of passing whole clauses (e.g. WHERE) you should pass values

Dapper And System.Data.OleDb DbType.Date throwing 'OleDbException : Data type mismatch in criteria expression'

Not sure if I should raise an issue regarding this, so thought I would ask if anybody knew a simple workaround for this first. I am getting an error when I try to use Dapper with OleDbConnection when used in combination with MS Access 2003 (Jet.4.0) (not my choice of database!)
When running the test code below I get an exception 'OleDbException : Data type mismatch in criteria expression'
var count = 0;
using (var conn = new OleDbConnection(connString)) {
conn.Open();
var qry = conn.Query<TestTable>("select * from testtable where CreatedOn <= #CreatedOn;", new { CreatedOn = DateTime.Now });
count = qry.Count();
}
I believe from experience in the past with OleDb dates, is that when setting the DbType to Date, it then changes internally the value for OleDbType property to OleDbTimeStamp instead of OleDbType.Date. I understand this is not because of Dapper, but what 'could' be considered a strange way of linking internally in the OleDbParameter class
When dealing with this either using other ORMs, raw ADO or my own factory objects, I would clean up the command object just prior to running the command and change the OleDbType to Date.
This is not possible with Dapper as far as I can see as the command object appears to be internal. Unfortunately I have not had time to learn the dynamic generation stuff, so I could be missing something simple or I might suggest a fix and contribute rather than simply raise an issue.
Any thoughts?
Lee
It's an old thread but I had the same problem: Access doesn't like DateTime with milliseconds, so you have to add and extension method like this :
public static DateTime Floor(this DateTime date, TimeSpan span)
{
long ticks = date.Ticks / span.Ticks;
return new DateTime(ticks * span.Ticks, date.Kind);
}
And use it when passing parameters:
var qry = conn.Query<TestTable>("select * from testtable where CreatedOn <= #CreatedOn;", new { CreatedOn = DateTime.Now.Floor(TimeSpan.FromSeconds(1)) });
Unfortunately, with current Dapper version (1.42), we cannot add custom TypeHandler for base types (see #206).
If you can modify Dapper (use the cs file and not the DLL) merge this pull request and then you do not have to use Floor on each parameters :
public class DateTimeTypeHandler : SqlMapper.TypeHandler<DateTime>
{
public override DateTime Parse(object value)
{
if (value == null || value is DBNull)
{
return default(DateTime);
}
return (DateTime)value;
}
public override void SetValue(IDbDataParameter parameter, DateTime value)
{
parameter.DbType = DbType.DateTime;
parameter.Value = value.Floor(TimeSpan.FromSeconds(1));
}
}
SqlMapper.AddTypeHandler<DateTime>(new DateTimeTypeHandler());

Doctrine 2 mysql FIELD function in order by

I'm trying to use the MySQL FIELD function in an order by clause in a query. I'm assuming that Doctrine 2 doesn't support the FIELD function out of the box - is that true? If so, how can I use it? Will I have to turn my whole query into a native query? Is there a Doctrine 2 extension that adds this functionality?
Jeremy Hicks, thanks for your extension.
I didn`t know how to connect your function to doctrine, but finally i find answer.
$doctrineConfig = $this->em->getConfiguration();
$doctrineConfig->addCustomStringFunction('FIELD', 'DoctrineExtensions\Query\Mysql\Field');
I need FIELD function to order my Entities that i select by IN expression. But you can use this function only in SELECT, WHERE, BETWEEN clause, not in ORDER BY.
Solution:
$qb
->select("r, field(r.id, " . implode(", ", $ids) . ") as HIDDEN field")
->from("Entities\Round", "r")
->where($qb->expr()->in("r.id", $ids))
->orderBy("field");
To avoid adding field alias into your result row you need put HIDDEN keyword. So this how to be able order values in IN expression in Doctrine 2.2.
You could add support for the FIELD() DQL function but instead implement it as standard SQL CASE .. WHEN expression. This way your function would work both on MySQL and Sqlite, which is particularly useful if you are like me and like to run your unit tests on in-memory sqlite.
This class is largely based on the work by Jeremy Hicks (I simply changed the getSql() method)
class Field extends FunctionNode
{
private $field = null;
private $values = array();
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
// Do the field.
$this->field = $parser->ArithmeticPrimary();
// Add the strings to the values array. FIELD must
// be used with at least 1 string not including the field.
$lexer = $parser->getLexer();
while (count($this->values) < 1 ||
$lexer->lookahead['type'] != Lexer::T_CLOSE_PARENTHESIS) {
$parser->match(Lexer::T_COMMA);
$this->values[] = $parser->ArithmeticPrimary();
}
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
$query = '(CASE ' . $this->field->dispatch($sqlWalker);
for ($i=0, $limiti=count($this->values); $i < $limiti; $i++) {
$query .= ' WHEN ' . $this->values[$i]->dispatch($sqlWalker) . ' THEN ' . $i;
}
$query .= ' END)';
return $query;
}
}
You can write your own DQL extension.
In case that the field that you want to "order by" is an ENUM data type, then ORDER BY will work in the order in which the values were defined for that ENUM field.
For example, I had a filed defined as enum('n','pe','o','ap','c') that was giving a weird ordering. The ordering got fixed after updating the enum to: enum('ap','c','n','o','pe')