Symfony2 and Doctrine APC Cache - symfony

i have read the documentation of symfony2 in relation to the performance and I have realized the following steps.
Install APC 'php-apc' on my webserver and restart my webserver
Modify my doctrine configuration
doctrine:
dbal:
driver: "%database_driver%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
orm:
auto_generate_proxy_classes: "%kernel.debug%"
auto_mapping: true
metadata_cache_driver: apc
result_cache_driver: apc
query_cache_driver: apc
Now if i call a action to retrieve all users from database i see in the information bar at the bottom that doctrine execute every time 114 queries. Why the queries not cached?
My action look like this:
$users = $this->getDoctrine()->getRepository('AppUserBundle:User')->findAll();
return $this->render('AppUserBundle:User:index.html.twig', array('users' => $users));

Doctrine doesn't cache query results by default. You have to explicitly point that you want to cache query using useResultCache method. For example, if you'd like to cache getting all users, write your own method in User repository class:
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository
{
public function fetchAll()
{
$query = $this->createQueryBuilder('u')->getQuery();
return $query->useResultCache(true)->getResult();
}
}
The method may take additional arguments:
public function useResultCache($bool, $lifetime = null, $resultCacheId = null)
$bool - set to true if you want to cache query result
$lifetime - TTL of cached result in seconds
$resultCacheId - you can pass your own id, in case of null Doctrine will handle that

Related

Multiple entity managers in Symfony 3.3 seams to not work as service arguments

I have configured two connections to the database. One connection is called user and other is called client. This is the configuration in the config.yml file:
doctrine:
dbal:
default_connection: client
connections:
client:
driver: pdo_mysql
host: '%client_database_host%'
port: '%client_database_port%'
dbname: '%client_database_name%'
user: '%client_database_user%'
password: '%client_database_password%'
charset: UTF8
mapping_types:
enum: string
user:
driver: pdo_mysql
host: '%user_database_host%'
port: '%user_database_port%'
dbname: '%user_database_name%'
user: '%user_database_user%'
password: '%user_database_password%'
charset: UTF8
mapping_types:
enum: string
orm:
auto_generate_proxy_classes: '%kernel.debug%'
default_entity_manager: ~
entity_managers:
client:
naming_strategy: doctrine.orm.naming_strategy.underscore
mappings:
ProjectModelBundle: ~
connection: client
user:
naming_strategy: doctrine.orm.naming_strategy.underscore
mappings:
BaseModelBundle: ~
ProjectModelBundle: ~
connection: user
But I am always getting the first entity manager no matter what. This is how I am using entity manager's in services:
# BASE
htec.project_model_bundle.repository.database.client_base:
class: Project\BaseModelBundle\Repository\Database\DatabaseRepository
arguments: ['#service_container', '#doctrine.orm.client_entity_manager', '#form.factory']
htec.project_model_bundle.repository.database.user_base:
class: Project\BaseModelBundle\Repository\Database\DatabaseRepository
arguments: ['#service_container', '#doctrine.orm.user_entity_manager', '#form.factory']
But no matter what I do, I always get the first entity manager that I have defined under orm->entity_managers settings. For example if configure orm like this:
orm:
auto_generate_proxy_classes: '%kernel.debug%'
default_entity_manager: ~
entity_managers:
client:
naming_strategy: doctrine.orm.naming_strategy.underscore
mappings:
ProjectModelBundle: ~
connection: client
user:
naming_strategy: doctrine.orm.naming_strategy.underscore
mappings:
BaseModelBundle: ~
ProjectModelBundle: ~
connection: user
I will always get the client entity manager even if I supply '#doctrine.orm.user_entity_manager' as service argument.
If I configure orm like this:
orm:
auto_generate_proxy_classes: '%kernel.debug%'
default_entity_manager: ~
entity_managers:
user:
naming_strategy: doctrine.orm.naming_strategy.underscore
mappings:
BaseModelBundle: ~
ProjectModelBundle: ~
connection: user
client:
naming_strategy: doctrine.orm.naming_strategy.underscore
mappings:
ProjectModelBundle: ~
connection: client
I will always get the user entity manager even if I supply '#doctrine.orm.client_entity_manager' as service argument.
What am I doing wrong here?
Recently I had a similar issue. The comment of #dbrumann gave me a good clue for resolve it. I used the next approach:
If you use the console command debug:autowiring you will see there is a service aliased as doctrine.
Doctrine\Common\Persistence\ManagerRegistry alias to doctrine
You can get access to that service in your target class by type-hinting, put the name of the service class (or interface) as argument in the constructor of your target class.
Now you can access all the methods of the service in your target class, by using the method getManager() you will get any of your managers:
use Doctrine\Common\Persistence\ManagerRegistry;
$protected $managerRegistry;
public function __construct(ManagerRegistry $managerRegistry)
{
$this->managerRegistry = $managerRegistry;
}
public function foo()
{
// asuming your managers names are default and second
$firstManager = $this->managerRegistry->getManager('default');
$secondManager = $this->managerRegistry->getManager('second');
}
So you defined a service named DatabaseRepository that exists in two variations. At one point it receives the 'client' entity manager, and at another point it receives 'user' entity manager?
I think it always returns you the 'client' manager most probably because that is the first time the service has been defined for the service container. It does not matter what you define it afterwards in the same .yml code.
Now, there is a big "why" rhetoric question for you. Why didn't you define two service classes, each for particular manager? If you really had to do it that way, that indicates some serious design problems in the remaining code of your Project.
Another suggestion: Do name your classes somewhat more descriptive. An entity manager and repository class certainly do something about a database. Naming classes, properties, variables in a proper way I find to be one of the most challenging part of the work, but it makes our lives easier.
Btw. Avoid passing entire container as service parameter:
arguments: ['#service_container',
because it's too expensive resources wise.

How to create a Second database connection with Symfony2?

I'm trying to connect a second database to my project in Symfony2. First, I added into parameters.yml some parameters to create the connection.
Then, I edited my config.yml, and now looks like:
doctrine:
dbal:
default_connection: default
connections:
default:
driver: pdo_mysql
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
circutor3:
driver: pdo_sqlsrv
host: "%database_host_circutor3%"
port: "%database_port_circutor%"
dbname: "%database_name_circutor%"
user: "%database_user_circutor3%"
password: "%database_password_circutor3%"
charset: UTF8
orm:
auto_generate_proxy_classes: "%kernel.debug%"
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
Finally, I tried to get connected, using the following code in my controller:
$em = $this->getDoctrine()->getManager('circutor3');
And, the error returned by Symfony2:
Doctrine ORM Manager named "circutor3" does not exist.
The circutor3 makes connection to a database, external to my system, so I don't need to create entities or objects. I only need to execute some SELECT to get information and store it using an array.
Is creating a typical mysqli connection the best way to solve my problem?
I don't know how to solve this with Symfony.
Thank you in advance.
you could access to the database connection in the controller as follow:
$connection = $this->getDoctrine()->getConnection('circutor3');
then use the connection as:
$stmt = $connection->prepare($sql);
$stmt->execute();
return $stmt->fetchAll();
Some help here and here
Hope this help
According to Symfony documentation (http://symfony.com/doc/current/cookbook/doctrine/multiple_entity_managers.html), you only defined a connection, not an entity manager :
You have to create an entity_manager for each connection.
orm:
default_entity_manager: default
entity_managers:
default:
...
circutor3:
connection: circutor3
mappings:
AppBundle: ~

Mapping entities across 2 (or more) databases

Context
I'm developing a website where I have to use data from two distinct databases (one local with full access, one external on read only).
One of "local" entities needs mapping to an "external" entity.
The external entity won't have its data changed since I can't persist these to the DB anyway.
Question
Is there a way to mark this mapping so that the external entity is pulled along when I retrieve the local entity ?
Short answer, No.
You can setup multiple database connections and use the same entity classes for both of them. But a single entity will not be able to have properties that map to different databases. You may have reference fields on there but those will need to just be keys that you can use to look them up using the other connection. For example imagine the following setup:
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
customer:
driver: '%database_driver2%'
host: '%database_host2%'
port: '%database_port2%'
dbname: '%database_name2%'
user: '%database_user2%'
password: '%database_password2%'
charset: UTF8
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
AcmeBundle: ~
customer:
connection: customer
mappings:
AcmeBundle: ~
Both managers will use the entity classes in the AcmeBundle. Then you can do something like
public function someControllerAction(){
// Get customer from the default connection
$customer = $this->getDoctrine()
->getManager() // If no value is provided the default is implied
->getRepository('AcmeBundle:Customer')
->findOneBy([
'id'=>12
]);
// Get the customers details from another connection
$customerDetails = $this->getDoctrine()
->getManager('customer')
->getRepository('AcmeBundle:CustomerDetails')
->findOneBy([
'customer_details_id' => $customer->getDetailsId()
]);
...
}

Using different connections with Doctrine

My application pulls data from an external database and then stores in the application database after minor processing. How would I set-up the mappings for the external database since it's not tied to entities? This is what I currently have:
dbal:
default_connection: default
types:
json: Sonata\Doctrine\Types\JsonType
connections:
default:
driver: pdo_mysql
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
rnr:
driver: pdo_mysql
host: "%database_host2%"
port: "%database_port2%"
dbname: "%database_name2%"
user: "%database_user2%"
password: "%database_password2%"
charset: UTF8
orm:
default_entity_manager: default
auto_generate_proxy_classes: "%kernel.debug%"
entity_managers:
default:
connection: default
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
rnr:
connection: rnr
mappings:
AppBundle: ~
With this implementation, I get the error below:
[Doctrine\ORM\ORMException]
Unknown Entity namespace alias 'AppBundle'.
Here's how I implement the function:
protected function execute(InputInterface $input, OutputInterface $output)
{
$em = $this->getContainer()->get('doctrine')->getManager();
$q = $em->createQueryBuilder();
$q->select('t')->from('AppBundle:TransactionSync', 't')->orderBy('t.id', 'DESC')->setMaxResults(1);
$sync = $q->getQuery()->getResult();
$em1 = $this->getContainer()->get('doctrine')->getManager('rnr');
$conn = $em1->getConnection();
$query = "SELECT id, merchant, client, phone, traderTransIdent AS member_id, transaction_id, transaction_type_id, value AS amount, points, DATE_FORMAT(STR_TO_DATE( transaction_date, '%d-%m-%Y' ), '%Y-%m-%d') AS transaction_date FROM merchant_transactions WHERE id > ". $sync->getId();
$stmt = $conn->prepare($query);
$stmt->execute();
$results = $stmt->fetchAll();
if(count($results) > 1)
{
$ts = new TransactionSync();
$ts->setStartTime(new \DateTime());
$id = 0;
foreach($results as $result)
{
$transaction_type = $em->getRepository('AppBundle:TransactionType')->find($result['transaction_type_id']);
$member = $em->getRepository('AppBundle:Member')->find($result['member_id']);
$transaction = new Transaction();
$transaction->setAmount($result['amount']);
$transaction->setPoints($result['points']);
$transaction->setClient($result['client']);
$transaction->setPhone($result['phone']);
$transaction->setTransactionId($result['transaction_id']);
$transaction->setTransactionDate(new \DateTime($result['transaction_date']));
$transaction->setTransactionType($transaction_type);
$transaction->setMember($member);
$em->persist($transaction);
$id = $result['id'];
}
$ts->setLastId($id);
$ts->setRecords(count($results));
$ts->setEndTime(new \DateTime());
$em->persist($ts);
$em->flush();
}
$output->writeln($text);
}
Unfortunately, you cannot use auto_mapping with multiple connections and you cannot map the same Bundle and/or the same alias to different connections. Doctrine is probably looking for AppBundle entities in connection default, completely ignoring rnr; to check all entities known by Doctrine run:
app/console doctrine:mapping:info
and see if your classes are listed correctly.
To workaround your problem, you need to move the TransactionSync entity out of AppBundle. For example, you can create a 'ExtEntity' directory and change your configuration to something like this:
orm:
default_entity_manager: default
auto_generate_proxy_classes: "%kernel.debug%"
entity_managers:
default:
connection: default
naming_strategy: doctrine.orm.naming_strategy.underscore
mappings:
AppBundle: ~
rnr:
connection: rnr
mappings:
ExtEntityMapping:
arbitrary_key:
type: xml # or annotation/yml
dir: %kernel.dir%/../src/AppBundle/ExtEntity
prefix: AppBundle\ExtEntity\
alias: ExtEntity
To avoid some headache, you can also use the method Registry:: getEntityManagerForClass to retrieve the proper ObjectManager for the object.
For example you can do the following in your code:
$registry = $this->getContainer()->get('doctrine');
$transSyncManager = $registry->getEntityManagerForClass('ExtEntity:TransactionSync');
$transTypeManager = $registry->getEntityManagerForClass('AppBundle:TransactionType');
Going off-track, note that you are using prepared statement in a wrong way, which is prone to SQL-injection (maybe not if IDs are always generated by DBMS); use parameters like this:
$query = "SELECT id, merchant, client, phone, traderTransIdent AS member_id, transaction_id, transaction_type_id, value AS amount, points, DATE_FORMAT(STR_TO_DATE( transaction_date, '%d-%m-%Y' ), '%Y-%m-%d') AS transaction_date FROM merchant_transactions WHERE id > :minId";
$stmt = $conn->prepare($query);
$stmt->execute(array('minId' => $sync->getId());
And even better -- if you can modify your database schema -- use DateTime for transaction_date column and avoid the string conversion madness. :)

Symfony2 and sqlite, SQLSTATE[HY000] caught

I'm trying to use sqlite3 with symfony2.
This is my database configuration in app/config/parameters.yml
parameters:
database_driver: pdo_sqlite
database_host: 127.0.0.1
database_port: null
database_name: librarian
database_user: root
database_password: null
database_path: librarian
And my app/config/config.yml
# Doctrine Configuration
doctrine:
dbal:
driver: %database_driver%
host: %database_host%
port: %database_port%
dbname: %database_name%
user: %database_user%
password: %database_password%
charset: UTF8
path: %kernel.root_dir%/%database_path%.db
orm:
auto_generate_proxy_classes: %kernel.debug%
auto_mapping: true
I've created a simple table to do some test. This table have an integer auto-increment 'id' field, a 'link' text field and a 'index' integer field.
The controller is simple :
class testController extends Controller {
public function indexAction() {
$issue = new Issue();
$issue->setIndex(0);
$issue->setLink(array('http://example.com'));
$em = $this->getDoctrine()->getManager();
$em->persist($issue);
$em->flush();
return $this->render('TestLibrarianBundle:Test:index.html.twig');
}
}
When I try to get execute the request by calling this controller, I got the following error:
request.CRITICAL: Uncaught PHP Exception Doctrine\DBAL\DBALException: "An exception occurred while executing 'INSERT INTO issue (index, link) VALUES (?, ?)':
SQLSTATE[HY000]: General error: 1 near "index": syntax error" at /.../.../vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php line 47
Any hint on how to solve this ?
Thanks
Can you name a field index if it's a sqlite keyword?
Your problem is that INDEX is an SQL keyword.
If you cannot configure your ORM to escape names properly (writing them as "index" instead), you have to change the field name to something else.

Resources