Symfony - Cache (Memcache) in a controller - symfony

How do you using caching directly in a controller without queries (orm cache drivers)?
http://symfony.com/doc/current/reference/configuration/doctrine.html#caching-drivers
The documents only mention caching with doctrine but there are times it's needed without doctrine. They don't appear to mention anything on this or is it one of the view things Symfony doesn't have a wrapper for?

Install and use DoctrineCacheBundle:
Add this bundle to your project as a composer dependency:
composer require doctrine/doctrine-cache-bundle
Add this bundle in your application kernel:
// app/AppKernel.php
public function registerBundles()
{
// ...
$bundles[] = new \Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle();
return $bundles;
}
Configure it:
# app/config/config.yml
doctrine_cache:
providers:
my_apc_metadata_cache:
type: apc
namespace: metadata_cache_ns
my_apc_query_cache:
namespace: query_cache_ns
apc: ~
memcache:
servers:
memcache01: 11211
Use it:
$apcCache = $this->container->get('doctrine_cache.providers.my_apc_cache');
$memcacheCache = $this->container->get('doctrine_cache.providers.memcache');
Read more: https://symfony.com/doc/current/bundles/DoctrineCacheBundle/usage.html
Full providers reference: http://symfony.com/doc/current/bundles/DoctrineCacheBundle/reference.html

Related

Kayue\WordpressBundle with Symfony 4.4.1: The class XXX was not found in the chain configured namespaces App\Entity

I'm trying to work with kayue/KayueWordpressBundle installed with composer as composer require kayue/kayue-wordpress-bundle in my Symfony 4.4.1 project but I'm unable to.
This is what I'm trying to do:
<?php
namespace App\Service\WordPress;
use Doctrine\ORM\EntityManagerInterface;
use Kayue\WordpressBundle\Entity\Post;
class PostCollection
{
protected $postRepository;
public function __construct(EntityManagerInterface $entityManager)
{
$this->postRepository = $entityManager->getRepository(Post::class);
}
}
The error I get:
The class 'Kayue\WordpressBundle\Entity\Post' was not found in the chain configured namespaces App\Entity
At first I blamed my dual-database configuration (Symfony is on a different DB from Wordpress) but then I put the DBs together and the issue persists:
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
# Only needed for MySQL (ignored otherwise)
charset: utf8mb4
default_table_options:
collate: utf8mb4_unicode_ci
orm:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
mappings:
App:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
I've been fiddling for the past 2hrs, but now I'm fresh out of ideas. I wonder if ANYONE actually got this to work with Symfony 4.
Thanks!
Edit: other tries:
Direct post injection:
use Kayue\WordpressBundle\Entity\Post;
public function index(Post $post){}
Result:
Cannot autowire argument $post of "App\Controller\IndexController::index()": it references class "Kayue\WordpressBundle\Entity\Post" but no such service exists.
As per documentation: outdated Symfony 2 way
$repo = $this->get('kayue_wordpress')->getManager()->getRepository('KayueWordpressBundle:Post');
Result:
Service "kayue_wordpress" not found: even though it exists in the app's container, the container inside "App\Controller\IndexController" is a smaller service locator that only knows about the "doctrine", "form.factory", "http_kernel", "parameter_bag", "request_stack", "router", "security.authorization_checker", "security.csrf.token_manager", "security.token_storage", "serializer", "session" and "twig" services. Try using dependency injection instead.
The "best way" to do this would actually be:
public function index(EntityManagerInterface $entityManager)
{
$entityManager->getRepository('KayueWordpressBundle:Post');
}
Result:
The class 'Kayue\WordpressBundle\Entity\Post' was not found in the chain configured namespaces App\Entity
Although you found a solution to this, there is a chain of issues I would like to explain.
The error
The class 'Kayue\WordpressBundle\Entity\Post' was not found in the chain configured namespaces App\Entity
means that in the entity manager provided, whose config is defined at:
orm:
...
mappings:
App:
...
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
the entity type Kayue\WordpressBundle\Entity\Post was not found.
Usually this type of error is solved by:
include the path Kayue\WordpressBundle\Entity in the entity manager or
use another entity manager which includes this path
In your case the default entity manager is autowired, based on the service alias of Doctrine\ORM\EntityManagerInterface as explained here. The alias is defined in the doctrine bundle `s config which points to the default doctrine entity manager.
You want to use Kayue\WordpressBundle `s entity manager, not the default one.
Solution
To solve this you can
1) Bind Arguments By type, as you find out, creating an alias of Kayue\WordpressBundle\Wordpress\ManagerRegistry to the service kayue_wordpress, as:
services:
# pass this service for any ManagerRegistry type-hint for any
# service that's defined in this file
Kayue\WordpressBundle\Wordpress\ManagerRegistry: '#kayue_wordpress'
or
2) use Binding Arguments by Name, in this case the "$wpManagerRegistry", as:
services:
# default configuration for services in *this* file
_defaults:
...
bind:
$wpManagerRegistry: '#kayue_wordpress'
and then
public function index($wpManagerRegistry)
{
$postRepository = $wpManagerRegistry->getManager()->getRepository('KayueWordpressBundle:Post');
so that any argument with name "$wpManagerRegistry" is autowired to this service.
References
The Symfony 3.3 DI Container Changes Explained (autowiring, _defaults, etc)
My collegue found a solution. You must configure the autowire like this:
// config/packages/kayue_wordpress.yaml
services:
Kayue\WordpressBundle\Wordpress\ManagerRegistry: '#kayue_wordpress'
After that, you can autowire:
use Kayue\WordpressBundle\Wordpress\ManagerRegistry;
public function __construct(ManagerRegistry $wpManagerRegistry)
{
$this->wpPostRepository = $wpManagerRegistry->getManager()->getRepository('KayueWordpressBundle:Post');
}
public function getPosts()
{
$post = $this->wpPostRepository->findOneBy(array(
'slug' => 'hello-world',
'type' => 'post',
'status' => 'publish',
));
}

How to disable Nelmio UI in production?

Is there a way to turn off all of the Nelmio Swagger UI docs? In production I want the outside world to see nothing at the following URLs, but in dev they should display useful docs & sandbox as normal:
http://ourserver.com/api/doc
http://ourserver.com/api-docs
http://ourserver.com/api-docs/*
Seems like there should be an easy switch for this in the Nelmio config, but I haven't found it. My company is using the Nelmio API Doc bundle in Symfony to develop APIs for a non-public API. The API server is exposed to the public, but we're not interested in publishing its usage to the world.
#gp_sflover's comment got me on the right track, but there's more to it than just disabling NelmioApiDocBundle on prod in AppKernel.php. Configs & routes that refer to Nelmio will generate errors until you move them into dev-specific files. The following change in app/AppKernel.php was the first step:
public function registerBundles()
{
$bundles = [
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
...
// new Nelmio\ApiDocBundle\NelmioApiDocBundle(), // <-- REMOVED FROM HERE
new Nelmio\CorsBundle\NelmioCorsBundle(),
new AppBundle\AppBundle(),
];
if (in_array($this->getEnvironment(), ['dev', 'test'], true)) {
$bundles[] = new Nelmio\ApiDocBundle\NelmioApiDocBundle(); // <-- ADDED HERE
$bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
...
To eliminate the config errors, I had to move the following stuff out of app/config/config.yml and into config_dev.yml:
# nelmio Configuration
nelmio_api_doc:
sandbox:
enabled: true
name: 'DLAP API Bridge'
swagger:
...
cache:
enabled: false
Likewise, the following stuff came out of app/config/routing.yml and moved to routing_dev.yml:
NelmioApiDocBundle:
resource: "#NelmioApiDocBundle/Resources/config/routing.yml"
prefix: /api/doc
nelmio_api_swagger:
resource: "#NelmioApiDocBundle/Resources/config/swagger_routing.yml"
resource: null
prefix: /api-docs
Now with symfony4 and flex you can install the bundle normally
composer require nelmio/api-doc-bundle
(So it won't be set as a require-dev depandency in your composer.json)
Now you change the setting in the project/config/bundles.php to:
Nelmio\ApiDocBundle\NelmioApiDocBundle::class => ['dev' => true]
So you don't get errors (eg from annotations), but it wont be loaded from Kernel.php
For disabling the Nelmio api-doc-bundle on specific environment, after some investigation, I ended up doing the following:
only enable the bundle on your target environment, i.e. on project/config/bundles.php have:
Nelmio\ApiDocBundle\NelmioApiDocBundle::class => ['dev' => true]
put the config annotation on env specific (put nelmio_api_doc.yaml inside ./config/packages/dev/)
put the route file nelmio_api_doc.yaml inside ./config/routes/dev (instead of being in the ./config/routes), otherwise, if you call the route for another environment which doens't have the bundle loaded, instead of 404, you will see 500)
not loading the bundle in config/bundles.php throws an exception in Symfony 4.3 with NelmioApiDocBundle Version 3:
There is no extension able to load the configuration for "nelmio_api_doc"
I ended up disabling the route for the api docs with a redirect in the environment, which needed to be disabled (prod):
#config/routes/prod/nelmio_api_doc.yaml
app.swagger:
path: /api/doc.json
methods: GET
defaults:
_controller: FrameworkBundle:Redirect:urlRedirect
path: /
permanent: true
app.swagger_ui:
path: /api/doc
methods: GET
defaults:
_controller: FrameworkBundle:Redirect:urlRedirect
path: /
permanent: true

JMS Serializer ignored my YML Entity exclusions

My config is
jms_serializer:
metadata:
auto_detection: true
directories:
NameOfBundle:
namespace_prefix: ""
path: "#VendorNameOfBundle/Resources/config/serializer"
My YML file named Entity.Project.yml contains
Vendor\NameOfBundle\Entity\Project:
exclusion_policy: ALL
properties:
id:
expose: true
I am loading the serializer like so from within a Controller
$serializer = SerializerBuilder::create()
->configureListeners(function(EventDispatcher $dispatcher) {
$dispatcher->addSubscriber(new ProjectSubscriber($this->container));
})
->addDefaultListeners()
->build();
This completely ignored my YML file and exposes all fields from the Project. I have cleared the cache.
But if I use this instead without the custom subscriber, then the exclusions work
$serializer = $this->get("jms_serializer");
Even explicitly adding a dir does not work either
$serializer = SerializerBuilder::create()
->configureListeners(function(EventDispatcher $dispatcher) {
$dispatcher->addSubscriber(new ProjectSubscriber($this->container));
})
->addDefaultListeners()
->addMetadataDir(realpath($this->get('kernel')->getRootDir()."/../") . '/src/Vendor/NameOfBundle/Resources/config/serializer')
->build();
The docs are not clear on how this path should befined. The above method does not error, but does not pull in the YML files. The below method errors and says the directory does not exist;
$serializer = SerializerBuilder::create()
->configureListeners(function(EventDispatcher $dispatcher) {
$dispatcher->addSubscriber(new ProjectSubscriber($this->container));
})
->addDefaultListeners()
->addMetadataDir('#VendorNameOfBundle/Resources/config/serializer')
->build();
How do I make the JMS Serializer look at my YML file in order to exclude the fields and also use the Subscriber?
As i see from documentation you need to setup your Yaml files:
it is necessary to configure a metadata directory where those files are located:
$serializer =
JMS\Serializer\SerializerBuilder::create()
->addMetadataDir($someDir)
->build();
For more information read manual.
This was helpful Using JMSSerialize to serialize Doctrine2 Entities that follow SimplifiedYamlDriver convention
It would appear that the file names needs to be completely different if you do not specify a namespace. I never thought to specify a namespace as this is not mentioned in the main docs.
If there is no namespace then the addMetaDir usage is fine but you also need to make sure your file names look like this
Vendor.NameOfBundle.Entity.Project.yml

Add configuration when run cache:clear

I look for the way to inject more parameters into symfony configuration cache. Currently, I use kernel.cache_warmer hook to my class in services.yml to generate another yml file in a directory. Then, it will be include in the symfony configuration cache, are there any possible way to inject a variable into generated config cache without need to create the Yml file?
Basically, I would like to make cache key changed everytime when run app/console cache:clear. Here is my service,
services.yml
imports:
- { resource: version.yml }
services:
cacheManager:
class: "%cacheManager.class%"
calls:
- [ setCachePrefix, ["%memcache.deploymentPrefix%"]]
memcacheDataVersioning:
class: WarmUpListener
tags:
- { name: kernel.cache_warmer, priority: 0}
WarmUpListener.php
class WarmUpListener implements CacheWarmerInterface{
public function warmUp($dir)
{
$array = ['parameters' => ['memcache.deploymentPrefix' => date('Ymd')]];
$dumper = new Dumper();
$yaml = $dumper->dump($array);
file_put_contents(__DIR__ . '/../Resources/config/version.yml', $yaml);
}
public function isOptional()
{
return false;
}
}
I have added to the DependencyInjection/*Extension class as below
DependencyInjection/somethingExtension.php
$container->setParameter('memcache.deploymentPrefix', date('Ymd') );
This will help to inject the variable in the configuration cached without need to make the Yml file and can removed all warmUp hookup on the question.

How to fix "This repository can be attached only to ORM sortable listener" error in Gedmo sortable?

When usieing StofDoctrineExtensions (which is a Symfony2 port of Gedmo Doctrine Extensions) Sortable behaviour I kept on getting this error:
This repository can be attached only to ORM sortable listener
Since I could not easily find the answer in official docs I'm leaving an answer here for future reference.
You need to enable any listeners you are using. In this case, Sortable.
stof_doctrine_extensions:
default_locale: en_US
orm:
default:
sortable: true
For Symfony 4, add this configuration in /config/packages/stof_doctrine_extensions.yaml. For older versions of Symfony, add it to config.yml.
In order to use Sortable behaviour you need to add an event listener to your bundle's boot method
<?php
namespace Acme\DemoBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class AcmeDemoBundle extends Bundle
{
public function boot()
{
// get the doctrine 2 entity manager
$em = $this->container->get('doctrine.orm.default_entity_manager');
// get the event manager
$evm = $em->getEventManager();
$evm->addEventSubscriber(new \Gedmo\Sortable\SortableListener);
}
}

Resources