I have an error that i don't understand, few days ago i create a new project for trying resolving this error in a project with fewer files and i solve it, but the solution didn't work in my Real project.
( This error is related to an other question i asked few day ago : The class was not found in the chain configured namespaces, but like i said i solve it so maybe i need to ask a new question ).
I tried to implement two EntityManager in my project for multple database connexion,
and that is the problem, oneof my entityManager searched tables in the wrong place, and don't find theme ... IDK why and most crazy thing it's some function work and other didn't, you'll see that :
In first my doctrine.yaml file :
doctrine:
dbal:
default_connection: default
connections:
default:
# configure these for your database server
url: '%env(resolve:DATABASE_URL)%'
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8mb4
customer:
# configure these for your database server
url: '%env(resolve:DATABASE_CUSTOMER_URL)%'
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8mb4
orm:
default_entity_manager: default
entity_managers:
default:
mappings:
Tdsmo:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity/Tdsmo'
prefix: 'App\Entity\Tdsmo'
alias: Tdsmo
customer:
connection: customer
mappings:
Customer:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity/Customer'
prefix: 'App\Entity\Customer'
alias: Customer
And know an exemple of some function that i can use in my controller :
// this one work, but his not the default connexion ..
// return all agent in agentCustomer that is in the second database.
$agentCustomers = $this->getDoctrine()
->getRepository(AgentCustomer::class, "customer")
->findAll()
;
// And this one, that is the default connexion, didn't work like this same for 'AgentRepository $agentRepository' that can be implemented in the method and called after for ->findAll()
// return an error
$agents = $this->getDoctrine()
->getRepository(Agent::class, "default")
->findAll()
;
BUT This shit work and return a array with all column and values :
$connexion = $this->getDoctrine()->getConnection('default')
$agentsFetch = $connexion->fetchAll("SELECT * from agent");
But i can't use the fetch method 'cause if i do in my template i need to replace all agent.firstName by agent.first_name for example, Furthermore i need to understand why one method work but not the other ...
Finally, the full error is :
An exception occurred while executing 'SELECT t0.id AS id_1, t0.email AS email_2, t0.roles AS roles_3, t0.password AS password_4, t0.firstName AS firstName_5, t0.lastName AS lastName_6, t0.registeredAt AS registeredAt_7, t0.telephone AS telephone_8, t0.fonction_id AS fonction_id_9, t0.subdivision_id AS subdivision_id_10, t0.section_id AS section_id_11 FROM Agent t0':
SQLSTATE[42S22]: Column not found: 1054 Champ 't0.firstName' inconnu dans field list
Thank you for your help !
OKAY
Thanks to Nicolai, he help me found the real problem, it was because of the naming strategy !
I don't know why but if it is not explicitly defined in the context of the use of several entityManager doctrine will generate the tables in the database in camelCase, instead of the snake_case (underscore).In any case that's what was causing me problem
So it must be specified with this line :
naming_strategy: doctrine.orm.naming_strategy.underscore
For where i found to specify 'underscore' :
How to configure naming strategy in Doctrine 2
And the doc For thoses who want :
https://symfony.com/doc/current/bundles/DoctrineBundle/configuration.html#configuration-overview
Related
I have a MySQL function which is used to number the submitted documents. It is implemented directly in MySQL server (pretty much to avoid deadlocks, concurrency and similar nice things). Table stores the last used number for particular document key, and function takes this number, increments it and returns back to Symfony application which assigns this to document.
DELIMITER $$
FUNCTION `NEXT_NUMBER`(requested_key VARCHAR(255)) RETURNS int(11)
BEGIN
DECLARE nextNumber INT;
INSERT INTO `document_numbers` (key, last_number) VALUES (requested_key, 1)
ON DUPLICATE KEY UPDATE last_number = last_number+1;
SELECT last_number INTO nextNumber FROM `document_numbers` WHERE key = requested_key
RETURN nextNumber;
END$$
DELIMITER ;
In application this function is called in separate Doctrine connection, which is not used for standard entity and database operations.
public function getNewNumber(string $key) : int
{
$this->connection->setAutoCommit(false);
try {
$this->connection->beginTransaction();
$statement = $this->connection->prepare("SELECT NEXT_NUMBER(:key);");
$statement->bindValue('series', $key);
$statement->execute();
$this->connection->commit();
$number = (int) $statement->fetchColumn();
$this->connection->close();
} catch (\Throwable $e) {
$this->connection->rollBack();
$this->connection->close();
throw $e;
}
return $number;
}
Now problematic part is that
$number = (int) $statement->fetchColumn();
returns the same value over multiple HTTP requests. Pretty much same behavior as if the result value would be cached. This behavior is not repeating 100% of time. I tried to add SQL_NO_CACHE to SELECT query to no avail.
Setup:
MySQL 5.7
Symfony 3.4
PHP 7.1
Doctrine/DBAL 2.7
OroPlatform 3.1
Doctrine config:
doctrine:
dbal:
default_connection: default
connections:
default:
server_version: '5.7'
driver: "%database_driver%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8MB4
mapping_types:
bit: boolean
json: string
secondary: # this one is used for $this->connection->prepare("SELECT NEXT_NUMBER(:key);");
server_version: '5.7'
driver: "%database_driver%"
host: "%database_host_secondary%" # this is simply pointing to 127.0.1.1
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8MB4
There is no MySQL/Doctrine caching setup present.
So one solution is to turn auto-commit back on $this->connection->setAutoCommit(true);, although this definitely was not the case when had this same piece of code on Symfony 2.8 and previous version of Doctrine.
Also the "SELECT NEXT_NUMBER(:key);" was actually returning incremented number and not some 'cached' version, but not committing it to database table after it was incremented.
I don't know how this was happening since $this->connection->commit(); is clearly executed and does not throw any errors. Maybe MySQL/PHP/Doctrine is somehow creating separate transaction for the operation. However, so far doesn't seem to encounter any problems with autocommit on, so the question itself is probably solved, though any input and description of the behavior would be appreciated.
I'm trying to use SQLite in memory db to run tests in Symfony 4, but I cant get it to work.
My config looks like this:
doctrine:
dbal:
default_connection: default
connections:
default:
driver: 'pdo_sqlite'
url: ':memory:'
memory: true
#charset: utf8mb4
#default_table_options:
# charset: utf8mb4
# collate: utf8mb4_unicode_ci
[EDITED on 2019-01-27]
In my case,
config/packages/test/doctrine.yaml :
doctrine:
dbal:
driver: pdo_sqlite
memory: true
charset: UTF8
But, for the fonctionnal testing, you need to create dB ( db in memory= not created and not persisted !)
Exemple :
<?php
// tests/Repository/MyRepositoryTest.php
namespace App\Tests\Controller;
use App\Repository\MyController;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class MyRepositoryTest extend WebTestCase {
public function testSearch()
{
$container = self::$kernel->getContainer();
$entityManager = $container->get('doctrine')->getManager();
// Runs the schema update tool using our entity metadata
$metadatas = $entityManager->getMetadataFactory()->getAllMetadata();
$schemaTool = new SchemaTool($entityManager);
$schemaTool->updateSchema($metadatas);
// #TODO : now you can test !
}
}
My goal is to have two different document managers connected to different databases that share the same database models.
I have heavily changed my database model and I would like to write a custom migration script that retrieves objects from the old model reads its values and then creates a new object on the new schema with the information of the old object.
I have found a related stackoverflow question here:
Working with two entity managers in the same bundle in Symfony2
Howewer, this solution suggests to use different prefixes for each database and it stores the classes for the data model in different folders:
doctrine:
dbal:
default_connection: default
connections:
default:
driver: %database_driver%
host: %database_host%
port: %database_port%
dbname: %database_name%
user: %database_user%
password: %database_password%
charset: UTF8
second:
driver: %database_sqlite_driver%
host: ~
port: ~
dbname: %database_sqlite_shop_name%
path: %database_sqlite_shop_name%
user: ~
password: ~
charset: UTF8
orm:
auto_generate_proxy_classes: %kernel.debug%
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
YourBundle:
# you must specify the type
type: "annotation"
# The directory for entity (relative to bundle path)
dir: "Entity/FirstDb"
#the prefix
prefix: "Your\Bundle\Entity\FirstDb"
shop:
connection: second
mappings:
YourBundle:
type: "annotation"
#here the second path where entity for the connection stand
dir: "Entity/SecondDb"
#the prefix
prefix: "Your\Bundle\Entity\SecondDb"
I would really just like to have two different document manager objects that share the same models in the same folder but are connected to diffrent databases. Is this possible?
I found out that it is indeed possible to be connected to different databases in the same Symfony Bundle. This answer led me to a possible solution:
https://stackoverflow.com/a/15110867/2174832
#services.yml
acme_app.dynamic_connection:
class: %acme.dynamic_doctrine_connection.class%
calls:
- [setDoctrineConnection, #doctrine.dbal.default_connection]]
<?php
namespace Acme\Bundle\AppBundle;
use Doctrine\DBAL\Connection;
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
use Exception;
class DynamicDoctrineConnection
{
/**
* #var Connection
*/
private $connection;
/**
* Sets the DB Name prefix to use when selecting the database to connect to
*
* #param Connection $connection
* #return SiteDbConnection $this
*/
public function setDoctrineConnection(Connection $connection)
{
$this->connection = $connection;
return $this;
}
public function setUpAppConnection()
{
if ($this->request->attributes->has('appId')) {
$connection = $this->connection;
$params = $this->connection->getParams();
// we also check if the current connection needs to be closed based on various things
// have left that part in for information here
// $appId changed from that in the connection?
// if ($connection->isConnected()) {
// $connection->close();
// }
// Set default DB connection using appId
//$params['host'] = $someHost;
$params['dbname'] = 'Acme_App'.$this->request->attributes->get('appId');
// Set up the parameters for the parent
$connection->__construct(
$params, $connection->getDriver(), $connection->getConfiguration(),
$connection->getEventManager()
);
try {
$connection->connect();
} catch (Exception $e) {
// log and handle exception
}
}
return $this;
}
}
With the solution above one can write a service that can be called to change the database that the current entity manager is connected to to a different database.
Unfortunately the solution above only works for the PDO driver (Mysql).
Since our technology stack includes mongodb and we use the doctrine-mongodb bundle I had to look for a differnt solution.
The doctrine-mongodb documentation has a section about setting up a custom document manager here:
http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/introduction.html
I was unable to get the right doctrine-mongodb odm document mappings to work with the instructions in the doc and therefore the only solution for me was to create a simple PHP mongoclient connection:
$mongo = new \Mongo('mongodb://localhost:27017');
$legacyDbDocumentMangager = $mongo->selectDB('backed_up_prod_db');
$legacyUserCollection = $legacyDbDocumentMangager->selectCollection('User');
$user = $legacyUserCollection->findOne(array('email' => 'matyas#stackoverflow.com'));
The only differnce between this simple php mongodb driver and the doctrine-mongodb odm is that the results of the queries of this driver are associative arrays and the results of the doctrine-mongodb odm diver are objects.
I hope this information is useful to someone.
I'm trying to use Redis as a driver for caching doctrine metadata, query and results. Follwing is my configuration.
auto_generate_proxy_classes: "%kernel.debug%"
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
result_cache_driver:
type: redis
host: %redis_host%
instance_class: Redis
query_cache_driver: redis
#metadata_cache_driver: redis
When I remove the comment from line #metadata_cache_driver: redis, I get an error running a test I have with following error.
TypeError: Argument 1 passed to Doctrine\ORM\Mapping\ClassMetadataFactory::wakeupReflection() must implement interface Doctrine\Common\Persistence\Mapping\ClassMetadata, string given, called in vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php on line 214
My Functional Test looks like Following:
public function testX()
{
//The data in prepared in setup..
$param1 = 'test-id';
$param2 = 'test-key';
$result = $this->em->getRepository('MyBundle:Test')
->findOneByXX($param1, $param2);
$this->assertTrue($result instanceof Test);
}
And My Query looks like following:
$qb->select('c')
->from('MyBundle:Test', 'c')
->where('c.id = :id')
->andWhere('c.key = :key')
->setParameter('id', $id)
->setParameter('key', $key);
$query = $qb->getQuery()
->useResultCache(true);
return $query->getOneOrNullResult();
Do I need additional configuration for Redis? Any Help would be appreciated??
I believe to resolve this you need to set the serializer for redis, the default serializer is probably not aware of the the PHP classes and when the object is removed from cache, and unserialized, it is not the same type as it was prior to serialization.
$redis->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_PHP);
For you case you will probably need to set the configuration option as a part of the driver configuration.
EDIT: Original title" Text environment: "Function "year" does not supported for platform "sqlite""
Incorporating beberlei\DoctrineExtensions in the test environment results in
Uncaught PHP Exception Doctrine\ORM\Query\QueryException: "[Syntax
Error] Function "year" does not supported for platform "sqlite""...
composer show -i includes
beberlei/DoctrineExtensions v1.0.5
The extensions are installed: they exist at "...vendor\beberlei\DoctrineExtensions".
The month() function does NOT throw an error.
config_test.yml
doctrine:
dbal:
default_connection: test
connections:
test:
driver: pdo_sqlite
path: %kernel.cache_dir%/test.sqlite
orm:
dql:
string_functions:
Soundex: Truckee\VolunteerBundle\DQL\Soundex
month: DoctrineExtensions\Query\Sqlite\Month
datetime_functions:
year: DoctrineExtensions\Query\Sqlite\Year
config.yml DQL
dql:
string_functions:
Soundex: Truckee\VolunteerBundle\DQL\Soundex
numeric_functions:
month: Oro\ORM\Query\AST\Functions\SimpleFunction
year: Oro\ORM\Query\AST\Functions\SimpleFunction
function call
public function expiringOppsNotSent()
{
$nextMonth = date_add(new \DateTime(), new \DateInterval('P1M'));
$expiryMonth = date_format($nextMonth, 'm');
$expiryYear = date_format($nextMonth, 'Y');
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('o')
->from('TruckeeVolunteerBundle:Opportunity', 'o')
->leftJoin('TruckeeVolunteerBundle:AdminOutbox', 'a', 'WITH', $qb->expr()->eq('a.oppId', 'o'))
->andWhere($qb->expr()->eq('month(o.expireDate)', ':month'))
->andWhere($qb->expr()->eq('year(o.expireDate)', ':year'))
->andWhere('a.id is NULL')
->setParameter(':month', $expiryMonth)
->setParameter(':year', $expiryYear)
;
$notSent = $qb->getQuery()->getResult();
return $notSent;
}
Edit:
The error does not occur if the dql in config.yml (as shown above) are commented out!
Edit #2:
Error persists in functional test with app_dev.php created.
Functional test uses Liip\FunctionalTestBundle\Test\WebTestCase
The correct namespace for the Sqlite Plugin is:
DoctrineExtensions\Query\Sqlite\Year
Update your config.yml to include:
doctrine:
orm:
dql:
datetime_functions:
year: DoctrineExtensions\Query\Sqlite\Year
I wish I had a rational explanation for why the error(s) have disappeared. I experimented with the order and labeling DQL to no effect. I deleted and re-entered the entries and now no errors. Best guess is there was at least one non-display character in an entry. For now all tests pass in SQLite!