I want to cache results of some HTTP requests to my Symfony 3.4 application using Symfony HttpCache.
Following their docs I now have those lines in app_dev.php
$kernel = new AppKernel('dev', true);
$kernel = new AppCache($kernel);
This is my controller code
$overview = $this->channelRepository->getOverview();
$response = new JsonResponse($overview);
$response->setSharedMaxAge(3600);
return $response;
And this is my config.yml
framework:
cache:
app: cache.adapter.redis
default_redis_provider: snc_redis.default
#...
snc_redis:
clients:
default:
type: phpredis
alias: default
dsn: redis://localhost
logging: false
However, I don't see new entries appear in my redis server. Instead, those entries appear in filesystem cache (var/cache/dev/http/cache). How do I fix my application to use Redis HTTP cache?
I dug a bit deeper and found out that AppCache that I use extends Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache which only accepts filesystem store. Does that mean that I should somehow use Symfony\Component\HttpKernel\HttpCache\HttpCache, which accepts any Store in its constructor?
Related
I am working on upgrade Symfony 3.3 to 4.4 and almost done with it, but I have one last issue.
I enabled autowiring with default config, but whole project uses lots of #ParamConverter conversions without annotation.
Problem: ParamConverter with auto_convert tries to convert services and classes that are mentioned to controller by typehinting for autowiring (not entities).
Controller:
/**
* #Route("/report", name="report_page")
*/
public function report(
Request $request,
FileManager $fileManager
): Response {
// code
}
Error:
The class 'App\Service\FileManager' was not found in the chain configured namespaces App\Entity.
Service 'App\Service\FileManager' exists and works correctly.
ParamConverter config (by default 'request: {converters: true, auto_convert: true}'):
sensio_framework_extra:
router:
annotations: false
Doctrine config:
doctrine:
dbal:
default_connection: default
connections:
default:
# config
orm:
auto_generate_proxy_classes: '%kernel.debug%'
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
mappings:
Base:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity'
prefix: App\Entity
Controller setting (services.yaml):
services:
_defaults:
autowire: true
autoconfigure: true
public: false
App\:
resource: '../src/*'
exclude: '../src/{DataFixtures,Entity,Objects,Repository,Utils}'
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
I understand that I can set 'sensio_framework_extra.request.auto_convert' to false and use #ParamConverter annotation everywhere in controllers, but there are too many places with this conversion and I try to get default behavior, so additional #ParamConverter annotations would be extra.
Just in case: I tried to disable auto_convert and everything worked correctly (services were autowired and entities were passed to controllers).
Also I checked that I installed last available versions of packages and bundles.
Need some hints on it. Probably I missed something. I tried to find exact way how ParamConverter works by default and how it checks that class is entity or not, but it is too complicated and I decided that I dug too deep.
EDIT:
Probably I have some issue with autowiring's config and not paramconverter. But I checked php bin/console debug:autowiring and got all needed services as available via autowiring.
It looks like symfony is trying to convert the fileManager like an entity.
Try moving the injected service(s) into the __construct method of the controller (and add the property to the controller) like:
private $fileManager;
...
public function __construct(FileManager $fileManager) {
$this->fileManager = $fileManager;
}
and then access it in the report method:
$this->fileManager
Is there any way to set _locale parameter of routing configuration as a function call or an expression result? I have multiple hosts running on the same symfony app, and there is an i18n turned on. Everything is working fine, but now i need to have another locales set for a specified host.
Right now my routing config looks like
app:
resource: '#AppBundle/Controller/'
...
requirements:
_locale: '%route_locales%'
...
and i have something like this in parameters:
...
route_locales: en|de|fr
...
That would be perfect if i can use something like
"#=service('AppBundle\\\\...\\\\LocalesConfigurator').getLocales()"
as a _locale: value to get this value based on a function call result. Or maybe there are some other options to get another _locale set for a specified host?
You can do it like #Jakumi suggested (via env params passed from server)
or using only application logic:
In route_locales put all possible locales that can be used in any host.
Validate incoming _locale inside "request listener" depending on request host - if some _locale is not allowed for host then return 404 response or show 404 error page etc.
Seems like i've found at least one solutuin, thanks to Jakumi for the idea. I've created a parameter in my config file, which receives an environment varaible. Then i use this parameter in my routing file. The main idea is that i can control this environment variable value using bootstrap file. So it looks like
bootstrap.php (in the example it's just a static value, but in a real life this value will depend on host).
bootstrap.php
...
$_SERVER['SYMFONY__ROUTE__LOCALES'] = 'en|es';
...
config.yml
parameters:
route_locales: '%route.locales%'
routing.yml
app:
resource: '#AppBundle/Controller/'
...
requirements:
_locale: '%route_locales%'
...
I don't realy like this solution, and will be thankful for any better solutions, but at least it does what i want.
I was in the same issue some months ago and I was able to found a solution that worked correctly, but there may also be some better ones.
As I needed different route names to be loaded depending on the locale (i.e. /contact in English and /contacto in Spanish) what I did was creating the methods in the controller and creating an individual link to them in routes.yaml
contact-en:
path: /contact/
controller: App\Controller\ContactController::index
host: domain1.tld
contact-es:
path: /contacto/
controller: App\Controller\ContactController::index
host: domain2.tld
Then I created an EventListener called LocaleSwitcher:
class LocaleSwitcher
{
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
$host = $request->getHost();
switch($host) {
case 'domain1.tld': {
$request->setLocale('en');
break;
}
case 'domain2.tld': {
$request->setLocale('es');
break;
}
}
}
}
And then adding it in services.yaml for event kernel.request and priority 20:
App\EventListener\LocaleSwitcher:
tags:
- { name: kernel.event_listener, event: kernel.request, priority: 20 }
I've been trying to figure out why I'm getting CORS issues with my Symfony 4 API application I've just deployed to my Apache server and can't make any sense of the issue.
config/packages/nelmio_cors.yaml
nelmio_cors:
defaults:
origin_regex: true
allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
allow_headers: ['Content-Type', 'Authorization']
max_age: 3600
paths:
'^/': ~
.env
...
CORS_ALLOW_ORIGIN=/*/
...
All responses from requests I make from my localhost front-end application to that API contain no Access-Control-Allow-Origin header, and I get the standard error;
Access to XMLHttpRequest at 'http://my-api.com/foo' from origin
'http://localhost:4200' has been blocked by CORS policy: Response to
preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
No special headers are being sent and for now I've set the allowed origin regex to "all" so I can't work out what is causing issue here. I've even checked within the cache to ensure the origin is being correctly pulled from the env variables, which it is. If other context/file content is required to assist please let me know!
I always try to be a bit more specific for allowing CORS like:
CORS_ALLOW_ORIGIN=^http://(.*:8080|localhost:4200)$
what you could try if you really want to enable all origins would be something like:
CORS_ALLOW_ORIGIN=^.*$
Your problem is that you've opted to use a regular expression (origin_regex: true) but not provided valid pattern.
If you want to use origin_regex: true then you should specify a valid pattern such as .* or ^.*$.
If you don't want to use a regular expression then omit the origin_regex setting (or set it to false) and just use * as your CORS_ALLOW_ORIGIN value.
I've resolved the issue, and although on the surface it appeared to be related to the CORS configuration, it was actually misconfiguration of the project on the server.
TL;DR is that the project was missing a .htaccess file which I didn't require in development due to using Valet - following the instructions here resolved the issue.
Why do you need nelmio?
You can have simple event listener(on kernel.event_subscriber) adding these headers.
namespace App\EventListener\HttpKernel;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
class CorsSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
KernelEvents::RESPONSE => 'onResponse'
];
}
public function onResponse(FilterResponseEvent $filterResponseEvent)
{
$response = $filterResponseEvent->getResponse();
$response->headers->set('Access-Control-Allow-Origin', '*');
}
}
Register it as kernel.event_subscriber
app.http_kernel.cors_subscriber:
class: App\EventListener\HttpKernel\CorsSubscriber
tags:
- { name: kernel.event_subscriber }
I'm using RabbitMQ Bundle for the Symfony2 web framework. My question is, how can I avoid creating multiple connections (to prevent overloading the broker) after running many workers in terminal? In example below, I've run two workers and ended up having two connections/channel.
config.yml
old_sound_rabbit_mq:
connections:
default:
host: 127.0.0.1
port: 5672
user: guest
password: guest
vhost: /
lazy: true
producers:
order_create_bmw:
connection: default
exchange_options: { name: order_create_ex, type: direct }
queue_options:
name: order_create_bmw_qu
routing_keys:
- bmw
consumers:
order_create_bmw:
connection: default
exchange_options: { name: order_create_ex, type: direct }
queue_options:
name: order_create_bmw_qu
routing_keys:
- bmw
callback: application_frontend.consumer.order_create_bmw
services.yml
services:
application_frontend.producer.order_create_bmw:
class: Application\FrontendBundle\Producer\OrderCreateBmwProducer
arguments:
- #old_sound_rabbit_mq.order_create_bmw_producer
Producer
namespace Application\FrontendBundle\Producer;
use Application\FrontendBundle\Entity\Order;
use OldSound\RabbitMqBundle\RabbitMq\ProducerInterface;
class OrderCreateBmwProducer
{
private $producer;
public function __construct(ProducerInterface $producer)
{
$this->producer = $producer;
}
public function add(Order $order)
{
$message = [
'order_id' => $order->getId(),
'car_model' => $order->getCarModel(),
'timestamp' => date('Y-m-d H:i:s')
];
$this->producer->publish(json_encode($message), 'bmw');
}
}
Running workers
$ app/console rabbitmq:consumer order_create_bmw
$ app/console rabbitmq:consumer order_create_bmw
RabbitMQ Management
Every client (regardless if publisher or subscriber) that connects to rabbitmq will create a connection. Aside from using less clients, I can't think of any other way to achive this. I also can't think of a reason to do so :) If it's performance, than actually having more subscribers will help to "empty" the exchanges (and queues).
I use fosuserbundle for auth.
Database during tests is set to be in-memory:
#config_test.yml
doctrine:
dbal:
driver: pdo_sqlite
path: :memory:
memory: true
orm:
auto_generate_proxy_classes: true
auto_mapping: true
Test file looks like that (just playground until i solve that):
Pasted
And when I run tests I get 302 redirect to the login, which should not happen.
What could be wrong with that?
When I change config to use development mysql connection - it works good - response is without redirect and the body is correct.
Ok, i managed to find out why is it happening.
I digged into Client class and its doRequest method.
protected function doRequest($request)
{
// avoid shutting down the Kernel if no request has been performed yet
// WebTestCase::createClient() boots the Kernel but do not handle a request
if ($this->hasPerformedRequest) {
$this->kernel->shutdown();
} else {
$this->hasPerformedRequest = true;
}
if ($this->profiler) {
$this->profiler = false;
$this->kernel->boot();
$this->kernel->getContainer()->get('profiler')->enable();
}
return parent::doRequest($request);
}
So kernel is shutting down every request and database is deleted from memory. That's my 99% guess.
I can't do anything for that except using other client library