How can I access the profile on a unit test context where there is no request?
In my case, I'm making tests to a data access layer that uses some other DBAL, doctrine2 in my case, and I want to assure that only a certain number of queries are made for performance issues. Don't want to put http requests in this scenario.
If I just do code below the collectors don't collect anything, and at the last line an exception is throw because getQueryCount it tries to access a data member set to null:
public function testQueryCount() {
/* #var $profile Profiler */
$em = $this->getContainer()->get('doctrine.orm.entity_manager');
$profile = $this->getContainer()->get('profiler');
$profile->enable();
$eventDataAccess = new EventDataAccess($em);
$e = $eventDataAccess->DoSomething(1);
$a = $profile->get('db');
$numMysqlQueries = $a->getQueryCount();
$this->assertCount(1, $numMysqlQueries)
}
I've got the profiler in the config_test.yml with:
doctrine:
dbal:
connections:
default:
logging: true
framework:
profiler:
only_exceptions: false
collect: true
Related
I'm having a controller which reads an integer from the session array in this manner:
if ($session->has('administration'))
{
$id = $session->get('entity_id');
return $this->getDoctrine()->getRepository('AppBundle:SomeEntity')->find($id);
}
Currently I'm writing tests for this controller action but I can't find how to inject this integer in the sessions array. I tried the following:
$this->client = $this->makeClient(true);
$this->client->getContainer()->set('session.storage.options.entity_id', 12);
This, however doesn't work and seems to just ignore the value. Therefore, my question is how to store values in the session array in a functional test environment? (note: I'm using the Liip test bundle)
Extra configs that may have impact
// config_test.yml
liip_functional_test:
cache_sqlite_db: true
authentication:
username: "%test.username%"
password: "%test.password%"
framework:
test: ~
session:
storage_id: session.storage.mock_file
profiler:
collect: false
Symfony as a Mock of the session for tests, as defined here in the doc
use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage;
use Symfony\Component\HttpFoundation\Session\Session;
$session = new Session(new MockFileSessionStorage());
$session->set('entity_id', 12);
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.
This is my current setup:
snc_redis:
clients:
default:
type: predis
alias: cache
dsn: "redis://127.0.0.1"
doctrine:
metadata_cache:
client: cache
entity_manager: default
document_manager: default
result_cache:
client: cache
entity_manager: [bo, aff, fs]
query_cache:
client: cache
entity_manager: default
I have an API which gets multiple duplicate requests (usually in quick succession), can I use this setup to send back a cached response on duplicate request? Also is it possible to set cache expiry?
From the config sample you provided I'm guessing you want to cache the Doctrine results rather than the full HTTP responses (although the latter is possible, see below).
If so, the easiest way to do this is that whenever you create a Doctrine query, set it to use the result cache which you've set up above to use redis.
$qb = $em->createQueryBuilder();
// do query things
$query = $qb->getQuery();
$query->useResultCache(true, 3600, 'my_cache_id');
This will cache the results for that query for an hour with your cache ID. Clearning the cache is a bit of a faff:
$cache = $em->getConfiguration()->getResultCacheImpl();
$cache->delete('my_cache_id');
If you want to cache full responses - i.e. you do some processing in-app which takes a long time - then there are numerous ways of doing that. Serializing and popping it into redis is possible:
$myResults = $service->getLongRunningResults();
$serialized = serialize($myResults);
$redisClient = $container->get('snc_redis.default');
$redisClient->setex('my_id', $serialized, 3600);
Alternatively look into dedicated HTTP caching solutions like varnish or see the Symfony documentation on HTTP caching.
Edit: The SncRedisBundle provides its own version of Doctrine's CacheProvider. So whereas in your answer you create your own class, you could also do:
my_cache_service:
class: Snc\RedixBundle\Doctrine\Cache\RedisCache
calls:
- [ setRedis, [ #snc_redis.default ] ]
This will do almost exactly what your class is doing. So instead of $app_cache->get('id') you do $app_cache->fetch('id'). This way you can switch out the backend for your cache without changing your app class, just the service description.
In the end I created a cache manager and registered it as a service called #app_cache.
use Predis;
class CacheManager
{
protected $cache;
function __construct()
{
$this->client = new Predis\Client();
}
/**
* #return Predis\Client
*/
public function getInstance()
{
return $this->client;
}
}
In the controller I can then md5 the request_uri
$id = md5($request->getRequestUri());
Check it exists, if it does return the $result
if($result = $app_cache->get($id)) {
return $result;
}
If it doesn't do..whatever...and save the response for next time
$app_cache->set($id,$response);
To set the expiry use the 3rd and 4th parameter ex = seconds and px = milliseconds.
$app_cache->set($id,$response,'ex',3600);
I'd like Symfony to log the Doctrine SQL queries that one of my tasks executes to a log file, much like the web debug toolbar does for non-cli code. Is this possible?
Symfony version: 1.4.12
Doctrine version: 1.2.4
Here's some example code for a task. I would like the SELECT query to be logged in a similar way to how it would be if it was called from an action.
class exampleTask extends sfBaseTask
{
protected function configure()
{
parent::configure();
$this->namespace = 'test';
$this->name = 'example';
}
protected function execute($arguments = array(), $options = array())
{
$databaseManager = new sfDatabaseManager($this->configuration);
$users = Doctrine_Core::getTable('SfGuardUser')
->createQuery('s')
->select('s.first_name')
->execute();
foreach($users as $user) {
print $user->getFirstName()."\n";
}
}
}
Try using the global task option --trace (shortcut -t). Like:
./symfony --trace namespace:task
It logs database queries the moment they are executed.
Don't forget to enable logging in the settings.yml of the application you're running the task in.
So if you're running the task in the dev environment of the backend you would edit apps/backend/config/settings.yml:
dev:
.settings:
logging_enabled: true
Note that this also logs stack traces of exception which might also be very helpful if you need to debug your tasks.
If you turn logging on all queries should be logged to your application log in the log directory. To do this set logging_enabled to true in your settings.yml.
You can then tail -f on the logfile and see what's going on.
The following link contains a nice tutorial on how to enable logging of the Doctrine queries. (translated from the original portuguese to english)
http://translate.google.com/translate?js=n&prev=_t&hl=en&ie=UTF-8&layout=2&eotf=1&sl=pt&tl=en&u=http%3A%2F%2Fwww.michaelpaul.com.br%2Flog-queries-doctrine.html&act=url