How can I use the a cache manager in Symfony? - symfony

I my contoller I try to clear the cache when updating a page
protected function mapDataToEntity(array $data, Project $entity): void{
$entity->setName($data['name']);
$cacheManager = $this->get('sulu_http_cache.cache_manager');
$cacheManager->invalidatePath($path, $headers);
}
I get the error message:
Attempted to call an undefined method named "get" of class
"App\Controller\Admin\ProjectController". Did you mean to call e.g.
"cgetAction", "getAction", "getLocale" or "getSecurityContext"?

You should autowire your cache manager instead of trying to access it from the container.
private CacheManager $cacheManager;
public function __construct(CacheManager $cacheManager)
{
$this->cacheManager = $cacheManager;
}
And use it in your method:
$this->cacheManager->invalidatePath($path, $headers);

Related

Symfony: redirecting to homepage after encountering an error

I've recently started learning Symfony, and I've been trying to make an app that will redirect user to the homepage after encountering an error (For the sake of the question, it can be error 404) However, I had problems with finding a way to do so.
Before, I used TwigErrorRenderer as described in Symfony documentation to handle my errors, but it only explains how to redirect to new error pages created by myself. Could somebody help me with this issue?
It is generally not a good idea to do this, because you want to tell the user that their request was not processed due to an error, or that they accessed non-existing page.
But if you really want to, you can achieve it with this Event Listener.
// src/EventListener/ExceptionListener.php
<?php
declare(strict_types=1);
namespace App\EventListener;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\Routing\RouterInterface;
final class ExceptionListener
{
private RouterInterface $router;
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
public function onKernelException(ExceptionEvent $event): void
{
// You should log the exception via Logger
// You can access exception object via $event->getThrowable();
$homepageRoute = $this->router->generate('homepage', [], RouterInterface::ABSOLUTE_URL);
$response = new RedirectResponse($homepageRoute);
$event->setResponse($response);
}
}
You also need to register the Event Listener in your services.yaml.
services:
App\EventListener\ExceptionListener:
tags:
- { name: kernel.event_listener, event: kernel.exception }
Please note the following:
The Event Listener assumes that your Homepage route is called homepage;
you really should log the exception or you will lose logs about all of them;
as stated at the top of this answer, this is not a good approach to deal with exceptions.

Have all undefined routes return 404 in symfony 4

I'm new to Symfony, and I'm working on a first project, using Symfony 4.1. I have a home page configured at '/', and that's working. The point of this project is to create a REST API, and I have a route defined at /api/argument/, and that's working fine, too.
However, when I navigate to /api, or any other route with something in the path (e.g. /car, /apple, /something/else), I get a 404 in the HTTP response, but the page displays a backtrace of the exception.
Sorry, the page you are looking for could not be found.
NotFoundHttpException
No route found for "GET /oijoij"
in RouterListener.php line 139
at RouterListener->onKernelRequest(object(GetResponseEvent), 'kernel.request', object(EventDispatcher))in EventDispatcher.php line 212
...
So then in .env I set
APP_ENV=test
APP_DEBUG=0
And now, instead of a pretty-printed HTML backtrace, I get just a plain-text error message
Fatal error: Uncaught Symfony\Component\Routing\Exception\ResourceNotFoundException in www\project\var\cache\test\srcTestProjectContainerUrlMatcher.php:50 Stack trace: ...
How do I configure this so that undefined routes return a 404 with the 404 template page, without a backtrace?
I'm also throwing a BadRequestHttpException in my API controllers, and that exception just gets dumped on the page too. Does this get solved by the same method?
As many people have pointed out, the error pages are generated for the dev and test environment only. In the prod environment, which you should use on your live system, will display a generic 404 page, which you can customize.
The system responsible for showing either the detailed error page in development or the regular 404 page in production is Symfony's event cycle, more specifically the kernel.exception event, which is listened to and then any uncaught errors and exceptions will be converted into an error page-response. Since you are writing an API you might want to register your own listener and return a JSON response instead of regular HTML.
An event subscriber for this could look something like this:
<?php declare(strict_types = 1);
namespace App\Api\Response;
use Exception;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\KernelEvents;
final class ExceptionToJsonResponseSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
KernelEvents::EXCEPTION => 'onKernelException',
];
}
public function onKernelException(GetResponseForExceptionEvent $event): void
{
// Skip if request is not an API-request
$request = $event->getRequest();
if (strpos($request->getPathInfo(), '/api') !== 0) {
return;
}
$exception = $event->getException();
$error = [
'type' => $this->getErrorTypeFromException($exception),
// Warning! Passing the exception message without checks is insecure.
// This will potentially leak sensitive information.
// Do not use this in production!
'message' => $exception->getMessage(),
];
$response = new JsonResponse($error, $this->getStatusCodeFromException($exception));
$event->setResponse($response);
}
private function getStatusCodeFromException(Exception $exception): int
{
if ($exception instanceof HttpException) {
return $exception->getStatusCode();
}
return 500;
}
private function getErrorTypeFromException(Exception $exception): string
{
$parts = explode('\\', get_class($exception));
return end($parts);
}
}
This will convert any exception into a JSON-response with a custom format similar to this:
{
"type": "NotFoundException",
"message": "Could not find argument with id x"
}
This listener will only do this for routes that start with /api so if you have both an API and a "regular" site it should not interfere with the default error handling.

Custom Repository in Doctrine

I have a controller:
public function getAllItemsAction()
{
$content = $this->getDoctrine()->getRepository(Item::class)->findAll();//<-(1)--THIS TO REPOSITORY
if ($content === NULL) {
return new View("Items not found", Response::HTTP_NOT_FOUND);
}
return new View($content,Response::HTTP_OK);
}
How can I move this line (1) to the repository and then use this method from the repository in the controller?
The line you highlighted is actually not related from Doctrine: you are getting a service from the Dependency Injection container, and then calling a method on it.
What may bothers you is that you are using an alias (getDoctrine()) and the registry from Doctrine, which is there for conveniency.
But actually you could also declare you repository as a service and do this: $this->get('item_repository)->findAll()`.

Symfony 3 : How to access my database inside my provider? (with doctrine)

I would like to access my database that contains all my user inside my provider with doctrine. I followed a tutorial (http://symfony.com/doc/current/security/custom_provider.html) to build my provider for my user, so I have an loadUserByUsername function :
public function loadUserByUsername($username)
{
// make a call to your webservice here
$player = new Player();
$player = $this->getDoctrine()
->getRepository('AppBundle:Player')
->findOneByPseudo($username);
// pretend it returns an array on success, false if there is no user
if ($player) {
return $player;
}
throw new UsernameNotFoundException(
sprintf('Username "%s" does not exist.', $username)
);
}
But of course my getDoctrine() function is undefined. So there is something I don't understand with the provider, I am trying to use it to be authenticated when I login so I need a provider, but why I can't search inside my database? How should I write this function? Thank for your help
EDIT :
When I add doctrine by service.yml (and after writting my constructor inside my provider), I have this error :
FatalThrowableError in PlayerProvider.php line 13:
Type error: Argument 1 passed to AppBundle\Security\PlayerProvider::__construct() must be an instance of Doctrine\Bundle\DoctrineBundle\Registry, instance of Doctrine\ORM\EntityManager given, called in /home/jean/PW6/SkA/SkeletonsOnlineV2/skeleton-online/var/cache/dev/appDevDebugProjectContainer.php on line 327
EDIT 2 : When I just put arguments: ['#doctrine'] inside my service.yml, I get an error that says that doctrine is undefined
EDIT 3 : It works now, I just made a dumb mistake
If you read further, it says the following (emphasis mine):
The real implementation of the user provider will probably have some dependencies or configuration options or other services. Add these as arguments in the service definition.
So in your case it would be something like
# app/config/services.yml
services:
app.webservice_user_provider:
class: AppBundle\Security\User\WebserviceUserProvider
arguments: ['#doctrine']
And your class needs a constructor
class WebserviceUserProvider implements UserProviderInterface
{
protected $doctrine;
public function __construct (\Doctrine\Bundle\DoctrineBundle\Registry $doctrine)
{
$this->doctrine = $doctrine;
}
// ...
}
Then in your method replace $this->getDoctrine() with just $this->doctine

Symfony2: Problems using logger in a service (passed as as parameter)

I'm developing a project using Symfony 2.1.
I have created a service that is being called from a controller, and it's working ok.
Now I need that the service generates log, and I'm trying to pass logger this way:
soap.client:
class: MyFirm\MyAppBundle\Service\SOAPClient
arguments:
logger: "#logger"
My service is defined this way:
namespace MyFirm\MyAppBundle\Service;
use \SoapClient as SoapClient;
use Monolog\Logger;
class SOAPClient
{
private $logger;
function __construct (Logger $logger)
{
$this->logger = $logger;
}
function sendMessage ($message, $wsdl_url)
{
$webServResult = "ERROR";
try{
$client = new SoapClient($wsdl_url, array("trace"=>true,
"exceptions"=>true));
$webServResult=$client->sendMessage($data);
}
catch(\Exception $ex){
$webServResult="ERROR";
$message=$ex->getMessage();
$log_text = print_r($ex, true)."\n".
$client->__getLastRequest()."\n".
$client->__getLastResponse();
$this->logger->err("ERROR: ".$log_text);
}
return $webServResult;
}
}
However, when I use the logger (if the wsdl doesn't exist, for example), the application hangs.
Am I doing anything wrong? Thanks a lot.
This is not a problzm from logger but from SoapClient that doesn't throw any Exception in case of unreachable WSDL... It waits until a Fatal error is thrown...
You have to check if WSDl exists before calling SoapClient ;)
Sorry, the problem wasn't at logger, but in the lines:
$log_text = print_r($ex, true)."\n".
$client->__getLastRequest()."\n".
$client->__getLastResponse();
If I remove these lines and log only the Exception message all goes right.
SoapClient does generate an Exception if the WSDL is not found:
SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://localhost/unknown.wsdl' :
failed to load external entity "http://localhost/unknown.wsdl"
Solved and fixed. Sorry for the spam.

Resources