Symfony2 - ExceptionController - NotFoundHttpException - symfony

I am trying to create a custom exception error page and I have been following Mike's tutorial. I have inserted the following code into config.yml, and created an ExceptionController in StoreBundle\Controller\ExceptionController.php. When I try to test my 404 error page by going local.store.com/fake-page, I get a NotFoundHttpException: No route found for "GET /fake-page" error. I thought my ExceptionController is suppose to redirect all users if a page is not found so I added a var_dump('testing') in it but it never dumped. I tried to remove the code I injected into config.yml and I get the default Symfony error page instead. Am I doing something wrong in config.yml?
Inserted into app/config/config.yml:
twig:
exception_controller: StoreBundle\Controller\ExceptionController::showAction
EDIT
I now think my problem is in my controller. This is what I have in StoreBundle\ControlleExceptionController.php. My error pages are in StoreBundle\Resources\views\Pages\Errors\404.html.twig and StoreBundle\Resources\views\Pages\Errors\500.html.twig
<?php
namespace StoreBundle\Controller;
use Symfony\Component\HttpKernel\Exception\FlattenException;
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\TwigBundle\Controller\ExceptionController as BaseExceptionController;
class ExceptionController extends BaseExceptionController
{
public function showAction(FlattenException $exception, DebugLoggerInterface $logger = null, $format = 'html')
{
$template = $this->container->get('kernel')->isDebug() ? 'exception' : 'error';
$code = $exception->getStatusCode();
return $this->container->get('templating')->renderResponse(
'StoreBundle:Exception:Pages/Errors:' . $code . '.html.twig', array(
'status_code' => $code,
'status_text' => Response::$statusTexts[$code],
'exception' => $exception,
'logger' => null,
'currentContent' => '',
));
}
}
}

Related

guzzlehttp and symfony with rest api problem

I want to make a post call with rest api using guzzlehttp in symfony ... I wrote this code but the response
/**
* #Route("/post/")
*/
public function postAction()
{
$client = new \GuzzleHttp\Client();
$response = $client->request('POST', $url, [
'form_params' => [
'username' => 'test',
'password' => 'test',
]
]);
return $this->render('esterne/post.html.twig', array(
'response'=>$response,
));
}
this is the twig file post.html.twig
{{response}}
the result is this:
{"status":"200","data":{"is_auth":true,"userToken":"194b873c004716acb3e0a5fba09fe405"}}
but if I put in html:
return $this->render('esterne/post.html.twig', array(
'response'=>$response->getBody(),
));
it results in error 500 internal server error
[2018-11-14 09:56:35] request.CRITICAL: Uncaught PHP Exception Twig_Error_Runtime: "An exception has been thrown during the rendering of a template ("Catchable Fatal Error: Object of class GuzzleHttp\Psr7\Response could not be converted to string")." at /app/app/Resources/views/esterne/post.html.twig line 1 {"exception":"[object] (Twig_Error_Runtime(code: 0): An exception has been thrown during the rendering of a template (\"Catchable Fatal Error: Object of class GuzzleHttp\Psr7\Response could not be converted to string\"). at /app/app/Resources/views/esterne/post.html.twig:1, ErrorException(code: 0): Catchable Fatal Error: Object of class GuzzleHttp\Psr7\Response could not be converted to string at /app/var/cache/prod/twig/47/478ca9f9b0a5c69caa7b0fed874bf831466230764635f396f057dc2c33868549.php:23)"} []
SOLUTION
use file
{{ response|json_encode()|raw }}
in twig and
return $this->render('esterne/post.html.twig', array(
'response'=>json_decode($response->getBody()->getContents(), FALSE),
));
You could try following response.
return $this->render('esterne/post.html.twig', array(
'response'=>$response->getBody()->getContent(),
));

Setup two different from_email in FOS_USER configuration

I'm using symfony 2.3 version and I want to configure two different from_email in fos_user configuration how is it possible and where to set my configuration.
I want to send welcome email after registration normal user using normaluser#gmail.com and send addition user welcome email using additionaluser#gmail.com
Plz suggest any solution.
You can do it by Using A Custom Mailer.
Create a custom service
Example:
<?php
namespace AppBundle\Mailer;
// implement all the needed methods
class CustomMailer implements MailerInterface
{
public function sendConfirmationEmailMessage(UserInterface $user)
{
$template = $this->parameters['confirmation.template'];
$url = $this->router->generate('fos_user_registration_confirm', array('token' => $user->getConfirmationToken()), UrlGeneratorInterface::ABSOLUTE_URL);
$rendered = $this->templating->render($template, array(
'user' => $user,
'confirmationUrl' => $url,
));
// implement the logic that decides which from_email to use
// change the from_email accordingly
$this->sendEmailMessage($rendered, $this->parameters['from_email']['confirmation'], (string) $user->getEmail());
}
}
and update the fos_user configuration to use your custom mailer
fos_user:
# ...
service:
mailer: app.custom_fos_user_mailer
Reference links:
http://symfony.com/doc/current/bundles/FOSUserBundle/emails.html#using-a-custom-mailer
https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Mailer/Mailer.php

Symfony 2. How to catch Exception parameter in Twig template?

I'm throwing some exception in my controller.
For example:
throw new AccessDeniedHttpException('some_text');
How can i catch it's 'some_text' parameter in my Twig template?
I found the {{ status_code }} and {{ status_text }} variables, but can't find something similar that solve my problem.
P.S. I'm already use custom error page. I just want give users specific error explanations.
Thnx.
By default Symfony uses the showAction of Symfony\Bundle\TwigBundle\Controller\ExceptionController to render your error page. The Implementation in Symfony 2.3 is like:
public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null, $_format = 'html')
{
$currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1));
$code = $exception->getStatusCode();
return new Response($this->twig->render(
$this->findTemplate($request, $_format, $code, $this->debug),
array(
'status_code' => $code,
'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
'exception' => $exception,
'logger' => $logger,
'currentContent' => $currentContent,
)
));
}
From there you can see that there is 'exception' => $exception passed to your twig template. $exception is of type Symfony\Component\HttpKernel\Exception\FlattenException which is a wrapper for the original PHP Exception.
FlattenException::getMessage is probably what you want to access your error message. See FlattenException API for more Information.
Ok. The TWIG code is
{{ exception.message|nl2br }}

Access Service from Controller and/or Twig template

Disclaimer: I'm slowly starting to get into Symfony and still have some problems understanding how the architecture works.
Currently I set up different Bundles (Services, right?) that should deliver different output for different routes. So far I got around adding a simple Twig template that loads stylesheets and scripts via Assetics and Twig-blocks. Now I added another Bundle that queries data via Buzz from a remote location, which worked fine as a standalone script, but I don't get around printing output in a Twig template.
The architecture of the original script is like the following (names made more generic):
Vendors - abstract class that serves as base for all remote request Bundles.
ServiceABC - abstract class that extends Vendors and defines Error handling and output preparation for the ABC service.
ClientXYZ - final class that extends Service_ABC, defines output parsing and normalization of the returned data.
This Bundle got a services.yml file:
# ~/MyApp/Bundle/ServiceABCBundle/Resources/config/services.yml
parameters:
service_abc_manager.class: MyApp\Bundle\ServiceABCBundle\Models\Service_ABC
location_manager.class: MyApp\Bundle\ServiceABCBundle\Models\Clients\ClientLocation
monitor_manager.class: MyApp\Bundle\ServiceABCBundle\Models\Clients\ClientMonitor
services:
service_abc_manager:
abstract: true
location_manager:
class: %location_manager.class%
parent: service_abc_manager
monitor_manager:
class: %monitor_manager.class%
parent: service_abc_manager
Names changed for easier reference - Typos by accident possible.
Now my problem/question is, that I don't really get behind the Symfony2 concept of how to get the output into the template.
namespace MyApp\Bundle\ServiceABCBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use MyApp\Bundle\ServiceABCBundle\Models\Clients\ClientLocation;
class DefaultController extends Controller
{
public function indexAction()
{
$services = array();
$services[] = $this->container->has('service_abc_manager');
$services[] = $this->container->has('location_manager');
$services[] = $this->container->has('client_location');
$services[] = $this->container->has('ClientLocation');
var_dump( $services );
$client = new ClientLocation();
var_dump( $client );
$response = $this->render(
'Service_ABC:Default:index.html.twig'
);
# $response->setCharset( 'utf-8' );
# $response->headers->set( 'Content-Type', 'text/html' );
return $response;
}
}
The output of the first array() named $services is always false and the $client = new ClientLocation(); throws an Exception that the class name wasn't found.
How can I access those Services/Bundle(parts)/Classes? And how would I render the output to a template?
Update
After I added the complete tree definition to Configuration()->getConfigTreeBuilder(), I'm able to see the definitions in the CLI:
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root( 'myapp_service_abc' );
$rootNode
->children()
->scalarNode('service_abc_manager')->end()
->scalarNode('location_manager')->end()
->scalarNode('monitor_manager')->end()
->end()
;
return $treeBuilder;
}
}
The CLI command php app/console config:dump-reference myapp_service_abc now gives me the following output:
myapp_service_abc:
service_abc_manager: ~
location_manager: ~
monitor_manager: ~
I can as well see that the config data was loaded, when I var_dump( $loader ); inside MyAppServiceABCExtension right after $loader->load( 'services.yml' ); was called.
The output is the following:
object(Symfony\Component\DependencyInjection\Loader\YamlFileLoader)
protected 'container' =>
object(Symfony\Component\DependencyInjection\ContainerBuilder)
private 'definitions' =>
array
'service_abc_manager' =>
object(Symfony\Component\DependencyInjection\Definition)
'location_manager' =>
object(Symfony\Component\DependencyInjection\DefinitionDecorator)
private 'parent' => string 'service_abc_manager'
// etc.
The problem itself remains: There's still a FALSE return value inside DefaultController()->indexAction() when I var_dump( $this->container->has( 'service_abc_manager' );. I as well tried var_dump( $this->container->has( 'location_manager' ); and var_dump( $this->container->has( 'myapp.service_abc_manager' ); with the same result.
You should not call your services from the twig file, but from the controller.
The role of the controller is to :
validate your forms if there were a form posted
call your services to get some stuffs to display in a view
initialize forms if there is a form to display
return a Response that typically contains a rendered twig view
Do not call your services using something like $client = new ClientLocation();, but call it using the service container. This will allow you to take the whole power of the dependancy injection offered by Symfony2.
Your controller will look like :
<?php
namespace MyApp\Bundle\ServiceABCBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
public function indexAction()
{
$locationService = $this->container->get('location_manager');
$someStuffs = $locationService->someMethod();
$response = $this->render(
'ServiceABCBundle:Default:index.html.twig', array('stuffs' => $someStuffs)
);
return $response;
}
}
From your twig file, you'll be able to use the stuffs variable :
{{ stuffs }} if your variable is a terminal ( a string, a number... )
{{ stuffs.attribute }} if your variable is an object or an array
About your services file, I am a bit confused, because your architecture does not look to be the standard Symfony2's one :
# ~/MyApp/Bundle/ServiceABCBundle/Resources/config/services.yml
Why your services.yml file isn't in the src/MyApp/SomethingBundle/Resources/config/ directory?
If you didn't already read it, I suggest you to have a look to the Symfony2 : The Big Picture documentation, which is the best way to start with Symfony2.

Custom error pages for PROD environment only?

How can I setup custom error pages for PROD environment only? I want to show custom ones for production but ordinary ones with exceptions for dev environment.
Any ideas?
I had the same issue and solution was pretty easy. You have to modify parameter twig.exception_listener.contoller to redirect rendering of error page to your own controller, which may extend original Twig exception controller.
Example (YourBundle/Resources/config/services.xml):
<parameter key="twig.exception_listener.controller">YourBundle\Controller\ExceptionController::showAction</parameter>
Then you have to create your own ExceptionController with method showAction, check for environment and do what you want to do or pass request to parent::showAction().
namespace YourBundle\Controller;
use Symfony\Bundle\TwigBundle\Controller\ExceptionController as BaseExceptionController;
use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference;
use Symfony\Component\HttpKernel\Exception\FlattenException;
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
use Symfony\Component\HttpFoundation\Response;
class ExceptionController extends BaseExceptionController {
public function showAction(FlattenException $exception, DebugLoggerInterface $logger = null, $format = 'html') {
$kernel = $this->container->get('kernel');
if ($kernel->getEnvironment() == 'prod') {
$request = $this->container->get('request');
$request->setRequestFormat($format);
$templating = $this->container->get('templating');
$code = $exception->getStatusCode();
$template = new TemplateReference('YourBundle', 'Exception', 'errorpage', $format, 'twig');
if ($templating->exists($template)) {
$response = $templating->renderResponse($template, array(
'status_code' => $code,
'message_code' => 'error_' . $code,
'status_text' => Response::$statusTexts[$code],
'requested_url' => $request->getUri(),
));
$response->setStatusCode($code);
$response->headers->replace($exception->getHeaders());
return $response;
}
}
return parent::showAction($exception, $logger, $format);
}
}
Beware of errors in errorpage.html.twig, because exceptions in twig processing are not handled as usual.
If you don't want to override the exception controller :
You can first copy the entire folder (or specific layout file) at
\vendor\symfony\symfony\src\Symfony\Bundle\TwigBundle\Resources\views\
to
\app\Resources\TwigBundle\views
Then customise the view in each layout file to match your design.
Then in the layout file, customise the message for each environment as follow
{% if app.environment == 'prod' %}
// message for prod
{% else %}
// message for dev
{% endif %}

Resources