Converting MySQL BIT field to BOOLEAN with Doctrine - php

could anyone tell me what's wrong with my code.
Using Doctrine 2 with PHP 5.3 MySQL 5.5
My YAML mapping for a BIT field called IsDefault which has values of 1 or 0 depeding if I want the value to be true or false in my application:
IsDefault:
type: boolean
nullable: false
The generated Entity is:
/**
* #var boolean $IsDefault
*/
private $IsDefault;
/**
* Set IsDefault
*
* #param boolean $isDefault
* #return Model
*/
public function setIsDefault($isDefault)
{
$this->IsDefault = $isDefault;
return $this;
}
/**
* Get IsDefault
*
* #return boolean
*/
public function getIsDefault()
{
return $this->IsDefault;
}
Unfortunately when accessing the data in my app every row returns IsDefault as TRUE.
Does anyone know why?

I have the same problem, and I change Bit(1) to TINYINT(1) and is working well.

Related

Set doctrine entity boolean field to 0 instead of null

Im trying to persist an doctrine entity with a boolean field where the values are 0 or 1.
When the property is set to true, it save it as '1' in database.
But when its 'false' or '0', it save it as NULL on database.
How can I fix this to only save only as 1 or 0 ?
The annotation for the property I use is like following:
#ORM\Column(name="substitute", type="boolean", nullable=true)
When I set nullable to false, I cant persist it because it still want to set to null.
Thanks
When I persist it, the field value is 0
Attempt 1
#ORM\Column(name="substitute", type="boolean", options={"default":"0"}))
error: Can't save null
Attempt 2
#ORM\Column(name="substitute", type="boolean", nullable= true, options={"default":"0"}))
Doesn"t work, it still save null in base
Info 1
The actually insert query is trying to insert 0. But I got this error "ORA-01400: cannot insert NULL into (\"MYBASE\".\"MYTABLE\".\"SUBSTITUTE\")"
Info 2
Same append with another entity
class TestEntity
{
/**
* #ORM\Column(name="test_entity_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="substitute", type="boolean")
*/
private $isSubstitute = false;
}
Persisting
$test = new TestEntity();
$test->setIsSubstitute(false);
$em->persist($test);
Result
request.CRITICAL: Uncaught PHP Exception Doctrine\DBAL\Exception\NotNullConstraintViolationException: "An exception occurred while executing 'INSERT INTO TestEntity (test_entity_id, substitute) VALUES (?, ?)' with params [7, 0]: SQLSTATE[HY000]: General error: 1400 OCIStmtExecute: ORA-01400: cannot insert NULL into ("MYBASE"."TESTENTITY"."SUBSTITUTE") (ext\pdo_oci\oci_statement.c:148)"\n (ext\\pdo_oci\\oci_statement.c:148) at PATH\\vendor\\doctrine\\dbal\\lib\\Doctrine\\DBAL\\Driver\\PDOStatement.php:91)"} []
Info 3
Inserting manually works using oci or oci8 driver
sql> INSERT INTO TestEntity (test_entity_id, substitute) VALUES (13, 0)
[2017-04-06 11:21:15] 1 row affected in 62ms
Just set the SQL Default to 0 (Edit: You need to update the schema after that change):
/**
* #ORM\Column(type="boolean", options={"default":"0"})
*/
protected $isActive;
Also you could initialize the property by default:
/**
* #ORM\Column(type="boolean", options={"default":"0"})
*/
protected $isActive = false;
Nullable shouldn't matter as long as the value is set to either true/false.
If you really set the property to false before saving and it still saves it as null in the DB then something else is going on.
Change your driver from oci to oci8 in your paramters.yml file:
database_driver: oci8
That should do it. Use the Underground PHP and Oracle Manual for installing OCI8.
I think #Cerad's suggestion is correct, can you try:
/**
* #ORM\Column(name="substitute", type="boolean")
*/
protected $substitute = false;
Let us know the result.
I just replicated your case, and I managed to successfully save into the db 1 for true, and 0 for false.
Example:
//Entity
Person: id, name, isMajor(boolean field)
//IMPORTANT: I setted the boolean field inside __construct() method, and let it be, by default, false (0). This means you don't need anymore to have that options={"default":"0"}.
//...
/**
* #var bool
*
* #ORM\Column(name="isMajor", type="boolean", nullable=true)
*/
private $isMajor;
public function __construct()
{
$this->isMajor = false;
}
//Created CRUD against the Entity
//When saving (either using the default actions provided by the CRUD, or by setting the values inside another controller's action):
//AppBundle/Controller/DefaultController.php
/**
* #Route("/new-person")
*/
public function createAction()
{
$person = new Person();
$person->setName('name');
$person->setIsMajor(true); // this saves 1 in the table
$person->setIsMajor(false); // this saves 0 in the table
$em = $this->getDoctrine()->getManager();
$em->persist($person);
$em->flush();
return $this->redirectToRoute('person_index');
}
I hope I did understood well your problem.
You can create fake object using $em->getReference(EntityName::class, "AnyValue");
Happy coding.

Symfony doctrine timestamp field

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();
}

Doctrine ODM : PersistentCollection::toArray returns empty array

Here's a definition of the field for the main document:
/**
* #var ArrayCollection
* #MongoDB\ReferenceMany(
* targetDocument="Some\Namespace\Document\Reference",
* sort={"creationDate": "desc"},
* simple=true
* )
* #Expose
* #Groups({"Main"})
* #Type("ArrayCollection<Some\Namespace\Document\Reference>")
* #var \Some\Namespace\Document\Reference[]
*/
protected $references;
I tried to get a list of main documents and serialized them via JMS Serializer, but I found, that references is empty array. After some investigation, I discovered, that for getReferences, documents returns instance of PersistentCollection for which:
count returns 2 [ok]
getMongoData returns array of MongoIds [ok]
toArray returns empty array [invalid]
Looks like that's because of initialize method, that clears mongoData.
I achived the proper outcome with following code:
/**
* #VirtualProperty
* #SerializedName("reference_ids")
* #Groups("Main")
* #return array
*/
public function getReferenceIds()
{
$out = array();
foreach ($this->getReferences()->getMongoData() as $val) {
$out[] = (string)$val;
}
return $out;
}
But it's only a shortcut and I don't feel, that's a proper solution.
If anyone has an idea how to retrieve these ids or whole documents using PersistentCollection and why initialize method clears mongoData ?
Thanks.

TINYINT wrongly converted to NULL instead of boolean in doctrine / symfony2

This is the craziest thing I ever seen.
My Symfony2 and Doctrine has gone mad
I have MySQL database with few different fields that are TINYINT.
Those are not nullable and all records have those values set to 0 or 1.
All doctrine ORM mapping ale set correctly (I doublechecked it hundred times).
All getters are set correctly (doublechecked as well).
But then - for some objects it doesn't work ... some of the TINYINT are not correctly translated to BOOLEAN as it should (and as it works with other objects and with other fields of that enity)... instead it gives NULL - even if in database this TINYINT is set to "1" (or "0" - it doesn't matter).
For some other objects (of the same entity) it works fine TINYINT = "1" is correctly recognized as true and "0" as false boolean.
Those my examples:
Events.orm.xml:
<entity name="Events" table="events">
<change-tracking-policy>DEFERRED_IMPLICIT</change-tracking-policy>
<field name="eventDeleted" type="boolean" column="_event_deleted"/>
Entity: "Events.php"
the mapping:
/**
* #var boolean $eventDeleted
*
* #ORM\Column(name="_event_deleted", type="boolean", nullable=false)
*/
private $eventDeleted;
and getter and setter:
/**
* Set eventDeleted
*
* #param boolean $eventDeleted
*/
public function setEventDeleted($eventDeleted)
{
$this->eventDeleted = $eventDeleted;
}
/**
* Get eventDeleted
*
* #return boolean
*/
public function getEventDeleted()
{
return $this->eventDeleted;
}
The database is:
And as I said - for some objects it works and gives "1" or "0" when accessed by for example:
{{ event[0].getEventDeleted }}
and sometimes it gives NULL.
I found some "similar" issue mentioned in doctrine jira, but no guess what solves that and what could be the reason: http://www.doctrine-project.org/jira/browse/DDC-1967
Any idea?
Try creating the query "by hand" with the query builder.
I also had the same problem on symfony 2.1 while fetching an entity from the session variable, a boolean field was returning null no matter what value it had. Using the query builder made it work for me.
EDIT
Example:
$qb = $em->createQueryBuilder();
$query = $qb->select('e.eventDeleted')
->from('BundleName:Events', 'e')
->where('e.id = :id')
->setParameter('id', $id)
->getQuery();
$eventDeleted = $query->getSingleResult();

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');

Categories