createNativeQuery - oci8 returns empty array - doctrine-orm

on a very simple entity :
class Users {
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
protected $userid;
/** #ORM\Column(type="string") */
protected $username;
}[...]
while trying to do a native query
$rsm = new \Doctrine\ORM\Query\ResultSetMapping;
$rsm->addEntityResult('Application\Entity\Users', 'u');
$rsm->addFieldResult('u', 'test', 'username');
$rsm->addFieldResult('u', 'userid', 'userid');
$q = $objectManager->createNativeQuery('SELECT u.username as test, u.userid from users u where u.userid=17',$rsm);
$result = $result->getResult();
$result returns an empty array with oracle (oci8 driver and pdo).
With MySQL, all is ok. The databases are exactly the same between Oracle and MySQL, same tables, same columns.
The table 'users' is not empty, because when using DQL, it works. Works too when using addRootEntityFromClassMetadata() whith a native query.
It seems that the problem occurs only with oci8+addEntityResult().
Any idea ?
Thanks by advance.

Found it in the manual.
ResultSetMapping#addFieldResult();
The first parameter is the alias of the entity result to which the field result will belong. The second parameter is the name of the column in the SQL result set. Note that this name is case sensitive, i.e. if you use a native query against Oracle it must be all uppercase. The third parameter is the name of the field on the entity result identified by $alias into which the value of the column should be set.
$rsm->addFieldResult('u', 'TEST', 'username');
$rsm->addFieldResult('u', 'USERID', 'userid');

Related

Doctrine/MySQL mediumtext + unncessary ALTER TABLEs

Running on mysql 5.7 + Doctrine 2.7/DBAL 2.9, with:
/**
* #ORM\Entity()
*/
class Entity {
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=100000, nullable=true)
*/
private $bigText;
}
orm:schema-tool:create creates the table Entity:
CREATE TABLE `Entity` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`bigText` mediumtext COLLATE utf8_unicode_ci,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
However, the schema-tool considers the table in need of update, running orm:schema-tool:update --dump-sql gives:
ALTER TABLE Entity CHANGE bigText bigText MEDIUMTEXT DEFAULT NULL;
Executing the update works fine, but doesn't help: it will still think the table needs updating.
I've tracked the issue to Doctrine\DBAL\Schema\Comparator's diffColumn method where the PlatformOptions of the current and the to-be column definition are compared. The to-be column has no PlatformOptions (which makes sense, as it's not on the platform yet, I suppose), while the current column does have them, namely charset and collation, in my test case here the defaults: ['charset' => 'utf8', 'collation' => 'utf8_unicode_ci'], which are set on the Column object by Doctrine\DBAL\Schema\MySqlSchemaManager->_getPortableTableColumnDefinition().
Poking around a bit in the source, I've found I can use EventListeners: because Doctrine\DBAL\Schema\AbstractSchemaManager->_getPortableTableColumnList() will emit an onSchemaColumnDefinition event and if I set $event->preventDefault() in there and create my own Column object and use $event->setColumn($column), I can circumvent MySqlSchemaManager->_getPortableTableColumnDefinition(). Or, I can go and fiddle with the other side and change the Schema created from the Classes / annotations and use postGenerateSchemaTable (which is much easier and feels less weird than messing with onSchemaColumnDefinition).
While I've learned a tiny bit more about internal flows, this feels way too complicated to handle mediumtext in MySQL without creating unnecessary ALTER TABLEs all the time.
Of course, I can sidestep the issue entirely by using type="text" instead of type="string" + a fixed length (which I've realized while browing issues here before asking this), but out of curiosity: am I missing something fundamental for dealing with fixed length fields in MySQL that fall into mediumtext length?

How to change column charset and collation with Doctrine?

I'm working on a Symfony 3.4 project and I want for some columns of a table to use charset utf8mb4. The aim behind this is to allow emojis.
Here is an abstract of my Doctrine entity:
/**
* #ORM\Table(name="categories")
*/
class Category {
// ...
/**
* #var string
* #ORM\Column(name="description", type="string", length=255)
*/
private $description;
}
I first updated doctrine configuration:
doctrine:
charset: utf8mb4
Then, I updated the Doctrine configuration of the field category.description:
- * #ORM\Column(name="description", type="string", length=255)
+ * #ORM\Column(name="description", type="string", length=191, options={"charset"="utf8mb4"})
Note that I changed the length from 255 to 191 as utf8mb4 uses 4 bytes instead of 3 for utf8.
Finally, I ran update command:
bin/console doctrine:schema:update --dump-sql
What returned:
ALTER TABLE categories CHANGE description description VARCHAR(191) NOT NULL;
As you can see, there is no update about charset. Moreover, we can see a double whitespace between keywords VARCHAR(191) and NOT, letting suppose there should be something here.
The query I expect would be like this:
ALTER TABLE categories MODIFY `description` VARCHAR(191) COLLATE utf8mb4_unicode_ci NOT NULL;
I then ran the update command:
bin/console doctrine:schema:update --force
But when I re-run the --dump-sql command it returns the same query (with the double whitespaces) over and over.
Even when I manually set the charset of the column to utf8mb4 the query is still the same.
I use v2.6.3 of doctrine/orm.
Did I miss something in my configuration?
Does Doctrine handle column charset?
This does not answer my question as it's about changing collation of all tables. In my case I want to change collation of a single column of a specific table.
Short answer
Doctrine won't do this for you.
Alternative solution
Using DoctrineMigrationBundle allows to create migrations in plain SQL. Thus, you can edit auto-generated migrations to add charset/collation on desired columns.
Generate a new blank migration:
bin/console doctrine:migrations:generate
Then, add the ALTER statement:
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20200113131732 extends AbstractMigration {
public function up(Schema $schema) : void {
$this->addSql('ALTER TABLE categories MODIFY `description` VARCHAR(191) COLLATE utf8mb4_unicode_ci NOT NULL;');
}
public function down(Schema $schema) : void {
$this->addSql('ALTER TABLE categories MODIFY `description` VARCHAR(255) COLLATE utf8_unicode_ci NOT NULL;');
}
}
Finally, just run the migration:
bin/console doctrine:migrations:migrate
It looks like doctrine partialy supports the change of collation on columns (only supported by Drizzle, Mysql, PostgreSQL>=9.1, Sqlite and SQLServer).
See doctrine annotations reference
/**
* #ORM\Table(name="categories")
*/
class Category {
// ...
/**
* #var string
* #ORM\Column(name="description", type="string", length=255, options={"collation"="ascii_general_ci"})
*/
private $description;
}

DQL getResult return array

I'm playing with Doctrine, and I think I probably miss something.
So I have a table for some relations, and here is the columns:
id | user_id | workshop_id
Basically, I use this table to know that a user is register for a workshop.
I want to count how many users subscribe for a workshop.
So in my Repository, I use a DQL request:
/** #var EntityManager $entityManager */
$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery("
SELECT COUNT('uw.id')
FROM App\Entity\UserWorkshops AS uw
LEFT JOIN App\Entity\User AS u WITH u.userId = uw.user
WHERE uw.workshop = :workshop_id");
$count = $query
->setParameters(['workshop_id' => $workshopId])
->getResult();
And here, and example of the result:
I just need to retrieve 3, as an integer.
How can I return something different ?
thanks
You can use getSingleScalarResult, from the doc:
Query#getSingleScalarResult(): Retrieves a single scalar value from
the result returned by the dbms. If the result contains more than a
single scalar value, an exception is thrown. The pure/mixed
distinction does not apply.
As example:
$count = $query
->setParameters(['workshop_id' => $workshopId])
->getSingleScalarResult();

Doctrine 2: Entity has no field or association named when trying to leftJoin()

Trying to build a query with doctrine, getting error:
Entity has no field or association named some_id
code (t is test table, it has some_id and i join it with Example ON example.id = test.some_id):
->from('AppBundle:Test', 't')
->leftJoin('AppBundle:Example', 'e', 'WITH', 'e.id = t.some_id')
Test Entity:
/**
* #var integer
*
* #ORM\Column(name="some_id", type="integer", nullable=true)
*/
private $someId;
Column some_id exists in my database, and a simple SQL query works fine, im just getting this error when trying to build it with query manager. Im guessing im missing something in my entity, but not sure what exactly.
Found the answer..
it has to be:
->leftJoin('AppBundle:Example', 'e', 'WITH', 'e.id = t.someId')
someId instead of some_id

Doctrine 2 query missing part of inner join

I'm using Doctrine 2 to try and do a query with an inner join. I have a Site entity and a Page entity. Each Site can have many pages and each page can only belong to one site. I have a site_id foreign key in my pages table rows. In my Site entity, I've created a OneToMany assocatiion where the target entity is my Page entity and the mappedBy is set to Site.
/**
* #var \Doctrine\Common\Collections\ArrayCollection
* #OneToMany(targetEntity="Page", mappedBy="Site", cascade={"persist", "remove"})
*/
private $pages;
In my Page entity, I have a ManyToOne association where the target entity is set to Site.
/**
* #var App\Entity\Site
* #ManyToOne(targetEntity="Site")
*/
private $site;
Here is my query builder statement where I'm passing a specific Site id:
$qb = $this->_em->createQueryBuilder();
$qb->select('s')
->from('App\Entity\Site', 's')
->innerJoin('s.pages', 'p')
->where('s.id = :id')
->setParameter('id', $id);
and here is the actual SQL I get back:
SELECT s0_.id AS id0, s0_.domain AS domain1 FROM sites s0_ INNER JOIN WHERE s0_.id = ?
See how the INNER JOIN information is completely missing? Am I doing something wrong here or is this a problem with Doctrine 2?
You need to explicitly tell Doctrine to fetch those columns:
$qb->select('s', 'p')
->form(...)
->join(...)
Figured it out. My mappedBy statement was incorrect in Site entity. I was pointing at "Site" when it should have been "site".