Symfony doctrine timestamp field - php

I'm trying to create a timestamp database field type for my Symfony project.
I have created the following database type:
class TimestampType extends Type {
const TIMESTAMP_TYPE_NAME = 'timestamp';
/**
* Gets the SQL declaration snippet for a field of this type.
*
* #param array $fieldDeclaration The field declaration.
* #param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The currently used database platform.
*
* #return string
*/
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return "TIMESTAMP";
}
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return $value;
}
public function convertToPHPValue($value, AbstractPlatform $platform)
{
return $value;
}
/**
* Gets the name of this type.
*
* #return string
*
* #todo Needed?
*/
public function getName()
{
return self::TIMESTAMP_TYPE_NAME;
}
}
In my entity, I have declared the following property:
/**
* #var \DateTime
* #ORM\Column(name="created", type="timestamp", options={"default":"CURRENT_TIMESTAMP"})
*/
protected $created = null;
It all looks good, but when running a database update, I get an error:
An exception occurred while executing 'ALTER TABLE question CHANGE created created TIMESTAMP DEFAULT 'CURRENT_TIMESTAMP' NOT NULL COMMENT '(DC2Type:timestamp)'':
SQLSTATE[42000]: Syntax error or access violation: 1067 Invalid default value for 'created'
For some reason, my default value is being encapsulated in single quotes. This doesn't happen for datetime fields, but then I get an error the default value is invalid.
Is there any way I can make Symfony accept a timetamp field with CURRENT_TIMESTAMP as default value?
I've tried the following in my custom type, by commenting out the appending query Symfony adds:
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return "TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '(DC2Type:" . self::TIMESTAMP_TYPE_NAME . ")' #--";
}
That works, but now Symfony always thinks it needs to update my tables and runs the query for every table that it thinks it needs to update.
My goal is to have a timestamp in the database if I run native insert queries. I know it can be done using HasLifecycleCallbacks and I have them configured, but I want to avoid ORM at some points and use native queries.
Any help would be appreciated. :)

A funny little trick I've seen is this (you wouldn't need the database type you created, just update the mapping):
/**
* #ORM\Column(type="datetime", nullable=false)
* #ORM\Version
* #var \DateTime
*/
protected $created = null;
What happens behind the scenes is that Doctrine will end up casting the datetime to a timestamp since it was combined with #version, and should add the default timestamp schema change.
With that said, this isn't quite the intended use for this feature (http://doctrine-orm.readthedocs.org/en/latest/reference/annotations-reference.html#annref-version), and I'd be curious what happens on subsequent update queries you make.
I know you're looking for the default to be set in MySQL so you can run queries outside of Doctrine, but the clearest way to add a default timestamp for me has always been to add it in the object's constructor:
public function __construct()
{
$this->created = new \DateTime();
}

Related

API Platform "Unexpected non-object value for object property."

I am trying to use API Platform serialization with calculated field as in here https://api-platform.com/docs/core/serialization/#calculated-field
Here is the code
/**
* #Groups({
* "read:actionJeu"
* })
*/
public function getTimePassed(){
return 4;
}
The normalization context is normalizationContext={"groups"={"read:actionJeu"}, "enable_max_depth"=true}
The problem is that when I do return 4, it shows this error
But when I change the return to something else (for example return new \DateTime('now') ), I get it working.
I wonder why this is happening, I tried with string too, but it doesn't work.
I supose that your entity property $timePassed is type of DateTime. Return type of method getTimePassed() also must be the DateTime type. You can change type of property $timePassed to int instead of DateTime or make your custom TimePassedSerializer service.
/** this **/
private int $timePasede;
/** insted of this **/
private DateTime $timePasede
It might be caused by the API Platform core package version.
Try installing version 2.6.8 or 2.6.6.
I was dealing with the same thing on the 2.6.7.
The example defines the return type of the function with : int at the end, so that it's clear that this function will return an integer.
I guess that api-platform will expect an object per default until a different return type has been defined.
So this should work from my understanding. Note that I haven't tested it because I don't have the possibility for it right now.
/**
* #Groups({
* "read:actionJeu"
* })
*/
public function getTimePassed(): int {
return 4;
}

Duplicate field in response symfony2 entity

I am using symfony 2, I have one field in database "old_status". I want to change it to "status" field (change in both database + entity. I changed it because It is making the developer confuse)
/**
* #var integer
*
* #ORM\Column(name="status", type="smallint", nullable=false, options={"unsigned"=true})
*
* #Expose
*/
private $status;
So in the response it will change to "status". But I don't want to change the contract. I want to show both of "old_status" and "status" fields but with same value. So the current partner will continue use "old_status" until they move to new field. The new partner will use "status".
I don't want add more field in database, I want to handler it by use entity.
Can I do it with entity?
You can simply leave the old getter and make them both new and old methods use the new field name status.
something like:
public function getStatus() {
return $this->status;
}
/**
* #deprecated Renamed to getStatus
*/
public function getOldStatus() {
return $this->status;
}
You can do a similar thing with other methods, like setters, if needed.
Since $status is a private field, it's just a matter of public interface.

Using DQL functions inside Doctrine 2 ORDER BY

I'm doing a project in Symfony 2.3 with Doctrine 2.4 using MySQL database.
I have an Entity of FieldValue (simplified):
class FieldValue
{
/**
* The ID
*
* #var integer
*/
protected $fieldValueId;
/**
* Id of associated Field entity
*
* #var integer
*/
protected $fieldId;
/**
* Id of associated user
*
* #var integer
*/
protected $userId;
/**
* The value for the Field that user provided
*
* #var string
*/
protected $userValue;
/**
* #var \MyProjectBundle\Entity\Field
*/
protected $field;
/**
* #var \MyProjectBundle\Entity\User
*/
protected $user;
The problem I have is the fact that $userValue, while it's LONGTEXT in my database, can represent either actual text value , date or number, depending in the type of the Field.
The Field can be dynamically added. After adding a one to any of the users every other user can also fill it's own value for that Field.
While querying the database I use orderBy to sort on a certain column, which also can be one of those Fields. In that case I need to sort on $userValue. This is problematic when I need to have number fields sorted as numbers, and not as strings ('123' is less than '9' in that case...).
The solution for it (I thought) is to CAST the $sort, so I would get SQL like:
ORDER BY CAST(age AS SIGNED INTEGER) ASC
Since Doctrine does not have a built-in DQL function for that, I took the liberty of adding that to my project as INT DQL function (thanks to Jasper N. Brouwer):
class CastAsInteger extends FunctionNode
{
public $stringPrimary;
public function getSql(SqlWalker $sqlWalker)
{
return 'CAST(' . $this->stringPrimary->dispatch($sqlWalker) . ' AS SIGNED INTEGER)';
}
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->stringPrimary = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
So happy with myself finding an easy solution I did that:
$sort = "INT(".$sort.")";
$queryBuilder->orderBy($sort, $dir);
which produced expected DQL:
ORDER BY INT(age) ASC
But also produced an exception:
An exception has been thrown during the rendering of a template ("[Syntax Error] line 0, col 12272: Error: Expected end of string, got '('") in MyProject...
So I've tried to find out what is going on and got into this in Doctrine\ORM\Query\Parser.php:
/**
* OrderByItem ::= (
* SimpleArithmeticExpression | SingleValuedPathExpression |
* ScalarExpression | ResultVariable
* ) ["ASC" | "DESC"]
*
* #return \Doctrine\ORM\Query\AST\OrderByItem
*/
public function OrderByItem()
{
...
}
Does that mean that there is no possibility to use DQL functions inside ORDER BY?
And if this is the case - is there any other way to achieve this?
UPDATE
I actually already have INT used in my select query, inside CASE WHEN:
if ($field->getFieldType() == 'number') {
$valueThen = "INT(".$valueThen.")";
}
$newFieldAlias = array("
(CASE
WHEN ...
THEN ".$valueThen."
ELSE ...
END
) as ".$field->getFieldKey());
Later on the $newFieldAlias is being added to the query.
Doesn't change anything...
UPDATE 2
Even when I add an extra select to the query, which will result in this DQL:
SELECT age, INT(age) as int_age
and then sort like that:
ORDER BY int_age ASC
I still don't het the correct result.
I've checked var_dump from $query->getResult(), and this is what I got:
'age' => string '57' (length=2)
'int_age' => string '57' (length=2)
Like CAST does not matter. I'm clueless...
Doctrine DQL does not accept functions as sort criteria but it does accept a "result variable". It means that you can do the following:
$q = $this->createQueryBuilder('e')
->addSelect('INT(age) as HIDDEN int_age')
->orderBy('int_age');
Doctrine 2 does not support INT by default, but you can use age+0.
$q = $this->createQueryBuilder('e')
->addSelect('age+0 as HIDDEN int_age')
->orderBy('int_age');
It is problem in your parser.php file. I have similar kind of issue and I solve this issue to replace below code in my parser file.
/**
* OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}*
*
* #return \Doctrine\ORM\Query\AST\OrderByClause
*/
public function OrderByClause()
{
$this->match(Lexer::T_ORDER);
$this->match(Lexer::T_BY);
$orderByItems = array();
$orderByItems[] = $this->OrderByItem();
while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$orderByItems[] = $this->OrderByItem();
}
return new AST\OrderByClause($orderByItems);
}
Just use this:
->orderBy('u.age + 0', 'ASC');

Symfony2 doesn't persist all the columns in a table

I have a entity with several fields. Now I have added to my database a new column "date" that is a datetime object.
But when I add a new register to the database this field always have value null, never caught the value that I put in the form.
The entity have the correct values, but if I saw all the values, the entity manager has a variable called "SelectColumnListSQL", and in this SQL action, doesn't appear the field "date".
The logs doesn't write any error, only store in my database the rest of the fields ok but this not.
If hay use dev enviromnent, in this case all works right :S
Any idea??
--- Entity Info ---
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime", nullable=false)
*/
protected $date;
/**
* Set date
*
* #param \DateTime $date
* #return Quotes
*/
public function setDate($date)
{
$this->date = $date;
return $this;
}
/**
* Get date
*
* #return \DateTime
*/
public function getDate()
{
return $this->date;
}
Thanks!
Please, concrete which ORM are you using (Doctrine, Propel...)
Have you already run the following commands?
php app/console doctrine:generate:entities
php app/console doctrine:schema:update
You must include the info in your Entity dir in your Bundle
I am pretty sure that you forget one setter method or the method is misspelled.
See the example below for more details.
Assume that you have this entity
class Entity{
// ...
private field;
// the getter and setter methods
public function getField(){
// ...
}
public function setField(){ // I guess this function is missing or misspelled
// ...
// If the function is missing or misspelled,
// doctrine will not take into account the changes you did for that field
}
}
best,
Check that you have totaly updated your database by this command:
php app/console doctrine:schema:update --dump-sql

Saving a Zend date in the database with Doctrine 2.1

I want to save a datetime in the database which was created with the doctrine schema tool.
In my form I set a date and time and i want to save it as a datetime in the database.
So i tried this:
$e->setStartDateTime(new Zend_Date('2011-09-01T22:00:00',Zend_date::DATETIME));
But i get the error:
PHP Fatal error: Call to undefined method Zend_Date::format() in /var/www/shared/Doctrine/lib/vendor/doctrine-dbal/lib/Doctrine/DBAL/Types/DateTimeType.php on line 44
Does anyone have experience with this and able to help me with this problem?
You can override the native datatypes to use Zend_Date instead of PHP's native DateTime which is the default for Doctrine data types 'datetime', 'time', and 'date'.
First in your application Bootstrap file, add the following BEFORE you instantiate your Doctrine EntityManager. This code should come before any other Doctrine code:
Doctrine\DBAL\Types\Type::overrideType('datetime', 'yournamespace\types\DateTimeType');
Doctrine\DBAL\Types\Type::overrideType('date', 'yournamespace\types\DateType');
Doctrine\DBAL\Types\Type::overrideType('time', 'yournamespace\types\Time');
Now you simply need to implement the 3 classes. It's easiest to just extend the corresponding Doctrine classes to achieve this. The code is actually the same for all 3 classes, the only difference is the class you extend from and the name of your class. Here is the DateTimeType class as an example:
namespace yournamespace\type;
use Doctrine\DBAL\Types\DateTimeType as DoctrineDateTimeType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**
* Override 'datetime' type in Doctrine to use Zend_Date
*/
class DateTimeType extends DoctrineDateTimeType
{
/**
* Convert from db to Zend_Date
*
* #param string $value
* #param AbstractPlatform $platform
* #return \Zend_Date|null
*/
public function convertToPhpValue($value, AbstractPlatform $platform)
{
if (is_null($value)) {
return null;
}
\Zend_Date::setOptions(array('format_type' => 'php', ));
$phpValue = new \Zend_Date($value, $platform->getDateTimeFormatString());
\Zend_Date::setOptions(array('format_type' => 'iso', ));
return $phpValue;
}
/**
* Convert from Zend_Date to db
*
* #param string $value
* #param AbstractPlatform $platform
* #return string|null
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if (is_null($value)) {
return null;
}
\Zend_Date::setOptions(array('format_type' => 'php', ));
$dbValue = $value->toString($platform->getDateTimeFormatString());
\Zend_Date::setOptions(array('format_type' => 'iso', ));
return $dbValue;
}
}
Now you can still use #Column(type="datetime") annotations in Doctrine. When saving to the database, you can save entity properties of type "datetime" to Zend_Date instances. Also when grabbing entities out of the database, properties of type "datetime" will now be Zend_Dates.
Doctrine2 expects PHP DateTime objects for DQL date and datetime types.
If you are not forced to use a Zend_Date, to this:
->setStartDateTime(new DateTime('2011-09-01T22:00:00'))
Else, convert it to a DateTime:
new DateTime('#' . $zendDate->getTimestamp())
See DateTime docs.
You can implement a Custom Mapping Type or use this ZendDateType implementation.
You may find this guide helpful.

Categories