Is there any built in possibility (or an external bundle) to cache data in Symfony2?
I don't want to cache the page itself, but data inside the application, using a simple key -> value store on the file system for example.
There's no built in solution, but I recommend you giving APC, Redis or Memcache a try (they're all in-memory datastores).
You can use LiipDoctrineCacheBundle to integrate cache drivers from Doctrine common into your Symfony project.
i'm using winzouCacheBundle. it gives you a streamlined cache api on different backends (apc,file,memcache,array,xcache, zenddata).
For now, there is no unique solution for caching in Symfony2. Some parts of the framework use Doctrine Common.
There are discussions about a "standard" caching solution if Symfony2, but we will have to wait for some time...
I think the DoctrineCacheBundle is currently the way to go.
The DoctrineCacheBundle allows your Symfony application to use different caching systems through the Doctrine Cache library.
Docs # Symfony.com
Code # Github
If I understand well, you would like to store data (attached to the session) and reload them when the same session will call again a new controller, in order to avoid to execute the same procedure more times (for example to read a table from a database).
You can use the session system in your controllers:
<?php
namespace YourStuff\YourBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class YourController extends Controller
{
$session = $this->get("session");
$variabile = 4;
$session->set("variableName",$variable); // setter
if ($session->has("variableName") // to check if the variable exists
{
$variableName = $session->get("variableName"); // getter
}
}
This is an example; the "variableName" could be accessed next time the same session will be called, if the lifetime of the session is not yet expired.
The "session" uses the __SESSION variable of PHP, so be sure to set correctly the session.cookie_lifetime and session.gc_maxlifetime, in order to give the desired lifetime.
Related
I need to set up a live demo of a Symfony app.
How can I make everything read-only? The users should be able to try all the features but not make any persistent change visible to others.
I could remove the INSERT and UPDATE privileges to the mysql user, but that would be an ugly error 500 when they try to save something...
Quick and dirty way to make your entire app Read-Only.
AppBundle/EventSubscriber/EntitySubscriber.php
namespace AppBundle\EventSubscriber;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\PreFlushEventArgs;
class EntitySubscriber implements EventSubscriber
{
public function getSubscribedEvents()
{
return [
'preFlush'
];
}
public function preFlush(PreFlushEventArgs $args)
{
$entityManager = $args->getEntityManager();
$entityManager->clear();
}
}
services.yml
app.entity_subscriber:
class: AppBundle\EventSubscriber\EntitySubscriber
tags:
- { name: doctrine.event_subscriber, connection: default }
I suppose that you've already made it. But if not:
Use dummy database. Copy it from original DB. Let them play. Drop it when you don't need it.
If you have no access to database creation and drop you can still do the trick. Just add temporary prefixes to table names in Doctrine entities. No need to rewrite the entire app, just a few lines. Run migrations to create new tables. Drop them whenever you want later.
Use virtual machine. Make a snapshot before the show. Roll back to the saved snapshot after the show.
These are more or less easy ways and they are platform independent.
Changing this based on the Symfony app level might have one of two disadvantages. You either do not save anything and thus your demo is not working so nice to show it to the customer. Or you have to do to much manipulations with the code and throw this huge work away right after the show.
Maybe you can use Session to do that or Memcache that you can implement in Symfony (Some examples are available on the web). Hope this will help.
I need a way to store temporary data for anonymous users.
Apparently this is not possible with:
\Drupal::service('user.private_tempstore')
Unless you write a custom constructor for the session management and stuff, which seems a little far-fetched to me?
I tried using
\Drupal::service('user.shared_tempstore')
But that saves the temp data for all anonymous users. So it's not linked to a single user.
Using raw $_SESSION['data'] works fine, but I'm not sure if I'm supposed to be doing this in Drupal and how safe/unsafe it is to do this?
Sessions (Drupal 8) are used via the simple Session implementation of SessionInterface interface. See Complete Tutorial of Sessions (Drupal 8).
Example:
use Symfony\Component\HttpFoundation\Session\Session;
$session = new Session();
$session->start();
// set and get session attributes
$session->set('name', 'Yash');
$session->get('name');
// set flash messages
$session->getFlashBag()->add('notice', 'Profile updated');
// retrieve messages
foreach ($session->getFlashBag()->get('notice', array()) as $message) {
echo '<div class="flash-notice">'.$message.'</div>';
}
I am not answering your specific question (regarding $_SESSION) because I have successfully used:
$session = \Drupal::service('user.private_tempstore')->get('your_module');
$session->set('whatever', $whatever);
from within procedural code (i.e. hooks, themes) without problems.
Pay attention that this private tempstore has to be assigned to a module (for the lack of a better way of saying this) which is the purpose of this line
$session = \Drupal::service('user.private_tempstore')->get('your_module')
After you get the private tempostore you can now set and get the session values:
$session->get('whatever');
$session->set('whatever', $whatever);
EDIT
Sorry, you explained correctly. I didn't get the critical part 100% ;)
You can always access the Session object from the request.
$session = \Drupal::request()->getSession();
$session->set('whatever', 'hello');
$value = $session->get('whatever', 'default');
I've been using plain PHP $_SESSION variables for a while now.
Did some research on them and they should be perfectly safe to use.
They're working correctly everywhere I use them and they have been working correctly for a while.
Don't think there's any issue using them in Drupal 8.
I am running a Symfony2 app and I have a question about caching.
There is a comment on an answer here in SO that says:
you could create a command that only updates this one cached route. or
maybe consider using a kernel event listener that newly registers the
route on every request if you can afford the performance impact.
How could I update only this one cached route?
Where are cached routes stored?
The cache classes for url matching/generation can be found in app/cache/environment and are called appEnvironmentUrlGenerator.php and appEnvironmentUrlGenerator.php with "environment" being one of dev,prod, .. etc.
API reference:
http://api.symfony.com/2.3/Symfony/Component/Routing/Matcher/UrlMatcher.html
http://api.symfony.com/2.3/Symfony/Component/Routing/Generator/UrlGenerator.html
How does it work?
The router service receives a url-matcher and a url-generator when being constructed. Those are then being used inside the match() and generate() methods of the router.
https://github.com/symfony/symfony/blob/2.3/src/Symfony/Component/Routing/Router.php
For warming up the cache the RoutingCacheWarmer uses the router's warmUp() method (if it implements WarmableInterface).
Everything written by nifr is true, but doesn't really help you.
Symfony's built in router designed to support 'static' routes, so on the first cache warmup the routes will be generated and cached. If you check the mentioned cache files you will see that the routes saved in a static private variable.
There is no simple way to update a route (or change routes).
Using Symfony2 CMF
This a bit complex solution for your simple problem: http://symfony.com/doc/master/cmf/index.html
Clearing (invalidating) the cache
If you check the CacheClearCommand you will see that the implementation is quite complex. Some suggest to delete the whole cache dir, which I don't recommend, it is quite heavy and makes your site hang on until the cache is regenerated (even you can get exceptions of missing files / and logged out users if the sessions saved under the cache folder)
Calling the cache_warmer warmup has no effect if the cache already delegated.
If you just remove the two related cache file and then call the warmUp on the router would be a better solution, but also not nice one..
$cacheDir = $this->container->getParameter("kernel.cache_dir");
// Delete routing cache files
$filesystem = $this->container->get('filesystem');
$finder = new Finder();
/** #var File $file */
foreach ($finder->files()->depth('== 0')->in($cacheDir) as $file) {
if (preg_match('/UrlGenerator|UrlMatcher/', $file->getFilename()) == 1) {
$filesystem->remove($file->getRealpath());
}
}
$router = $this->container->get('router');
$router->warmUp($cacheDir);
Override the default Router class
As of 2.8 the Router class is defined with a parameter router.class
See here: https://github.com/symfony/framework-bundle/blob/v2.8.2/Resources/config/routing.xml#L63
Add something like this to your config.yml
parameters:
router.class: "My\Fancy\Router"
You can implement your own Router class, extending the original Router, and also you will need to extend the UrlMatcher and UrlGenerator classes to call the parent implementation and add your own routes to match against / generate with. This way you don't need to refresh the cache, because you can add your routes dynamically.
Note: I'm not sure if you can rely on this on long term, if you check master, the definition has changed, the parameter is not there anymore:
https://github.com/symfony/framework-bundle/blob/master/Resources/config/routing.xml#L54
I have a problem with doctrine. I like the caching, but if i update an Entity and flush, shouldn't doctrine2 be able to clear it's cache?
Otherwise the cache is of very little use to me since this project has a lot of interaction and i would literally always have to disable the cache for every query.
The users wouldn't see their interaction if the cache would always show them the old, cached version.
Is there a way arround it?
Are you talking about saving and fetching a new Entity within the same runtime (request)? If so then you need to refresh the entity.
$entity = new Entity();
$em->persist($entity);
$em->flush();
$em->refresh($entity);
If the entity is managed and you make changes, these will be applied to Entity object but only persisted to your database when calling $em->flush().
If your cache is returning an old dataset for a fresh request (despite it being updated successfully in the DB) then it sounds like you've discovered a bug. Which you can file here >> http://www.doctrine-project.org/jira/secure/Dashboard.jspa
Doctrine2 never has those delete methods such as deleteByPrefix, which was in Doctrine1 at some point (3 years ago) and was removed because it caused more trouble.
The page http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/caching.html#deleting is outdated (The next version of the doctrine2 document will see those methods removed). The only thing you can do now is manually managing the cache: find the id and delete it manually after each update.
More advanced doctrine caching is WIP: https://github.com/doctrine/doctrine2/pull/580.
This is according to the documentation on Doctrine2 on how to clear the cache. I'm not even sure this is what you want, but I guess it is something to try.
Doctrine2's cache driver has different levels of deleting cached entries.
You can delete by the direct id, using a regex, by suffix, by prefix and plain deleting all values in the cache
So to delete all you'd do:
$deleted = $cacheDriver->deleteAll();
And to delete by prefix, you'd do:
$deleted = $cacheDriver->deleteByPrefix('users_');
I'm not sure how Doctrine2 names their cache ids though, so you'd have to dig for that.
Information on deleting cache is found here: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/caching.html#deleting
To get the cache driver, you can do the following. It wasn't described in the docs, so I just traced through the code a little.
I'm assuming you have an entity manager instance in this example:
$config = $em->getConfiguration(); //Get an instance of the configuration
$queryCacheDriver = $config->getQueryCacheImpl(); //Gets Query Cache Driver
$metadataCacheDriver = $config->getMetadataCacheImpl(); //You probably don't need this one unless the schema changed
Alternatively, I guess you could save the cacheDriver instance in some kind of Registry class and retrieve it that way. But depends on your preference. Personally I try not to depend on Registries too much.
Another thing you can do is tell the query you're executing to not use the result cache. Again I don't think this is what you want, but just throwing it out there. Mainly it seems you might as well turn off the query cache altogether. That is unless it's only a few specific queries where you don't want to use the cache.
This example is from the docs: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/caching.html#result-cache
$query = $em->createQuery('select u from \Entities\User u');
$query->useResultCache(false); //Don't use query cache on this query
$results = $query->getResult();
I have a behavior which enables segregation of user data based on the user id stored in the session. In CakePHP 1.3 you could do this:
App::import('Component', 'Session');
$session = new SessionComponent();
$session->read('Auth.User.id');
But in CakePHP 2, you can't instantiate a component like that in a behavior because the Component __construct requires the Controller's ComponentCollection as a parameter.
Is it possible to access a session variable inside a behavior in CakePHP 2? What's the best way to do it?
If you look at the SessionComponent code, you will see that it is only a wrapper for the CakeSession class.
So you can do the following:
App::uses('CakeSession', 'Model/Datasource');
$user_id = CakeSession::read('Auth.User.id');
In CakePHP 2.0 you can also simply call the Session-methods via the static CakeSession::method() without having to load anything... ;-)