If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
What about logging out? Symfony has some magic for that too!
Look at the logout part of security.yml:
# app/config/security.yml
# ...
firewalls:
secured_area:
# ...
logout:
path: _demo_logout
target: _demo
path is the name of your logout route. Set it to logout - we’ll create that route in a second. target is where you want to redirect the user after logging out. We already have a route called event, which is our event list page. Use that for target:
# app/config/security.yml
# ...
firewalls:
secured_area:
# ...
logout:
path: logout # a route called logout
target: event # a route called event
To make the logout route, let’s add another method inside SecurityController and use the @Route annotation:
// ...
// src/Yoda/UserBundle/Controller/SecurityController.php
/**
* @Route("/logout", name="logout")
*/
public function logoutAction()
{
}
Just like with the loginCheckAction, the code here won’t actually get hit. Instead, Symfony intercepts the request and processes the logout for us.
Try it out by going to /logout manually. Great! As you can see by the web debug toolbar, we’re anonymous once again.
If we fail login, we see a “Bad Credentials” message. When Symfony handles the login, it saves this error to the session under a special key, and we’re just fetching it out in loginAction.
Actually, we have more code than we need here. Remove the if statement and just leave the second part:
// src/Yoda/UserBundle/Controller/SecurityController.php
// ...
public function loginAction(Request $request)
{
$session = $request->getSession();
// get the login error if there is one
$error = $session->get(SecurityContextInterface::AUTHENTICATION_ERROR);
$session->remove(SecurityContextInterface::AUTHENTICATION_ERROR);
return array(
// last username entered by the user
'last_username' => $session->get(SecurityContextInterface::LAST_USERNAME),
'error' => $error,
);
}
The first part isn’t used unless you reconfigure how Symfony sends you to the login page.
Note
The configuration I’m talking about here is the use_forward, which causes Symfony to forward to the login page, instead of redirecting.
I know I know, the login page is embarrassing looking. So I made a login.css file to fix things - find it in the resources/episode2 directory of the code download.
Let’s move it into a Resources/public/css directory in the UserBundle.
/* src/Yoda/UserBundle/Resources/public/css/login.css */
.login {
width: 500px;
margin: 100px auto;
}
/* for the rest of login.css, see the code download */
Just like in episode 1, run app/console assets:install and add the --symlink option, unless you’re on Windows:
php app/console assets:install --symlink
This creates a symbolic link from web/bundles/user to the Resources/public directory in UserBundle. Since web/ is our application’s document root, this makes our new CSS file accessible in a browser by going to /bundles/user/css/login.css.
So how can we add this CSS file to only this page? First, open up the base template. Here, we have a bunch of blocks, including one called stylesheets. All of our global CSS link tags live inside of it:
# app/Resources/views/base.html.twig
# ...
{% block stylesheets %}
{% stylesheets
'bundles/event/css/event.css'
'bundles/event/css/events.css'
'bundles/event/css/main.css'
filter='cssrewrite'
%}
<link rel="stylesheet" href="{{ asset_url }}" />
{% endstylesheets %}
{% endblock %}
Let’s override this block in login.html.twig and add the new link tag to login.css:
{# src/Yoda/UserBundle/Resources/views/Security/login.html.twig #}
{% block stylesheets %}
<link rel="stylesheet" href="{{ asset('bundles/user/css/login.css') }}" />
{% endblock %}
Cool, but do you see the problem? This would entirely replace the block, but we want to add to it. The trick is the Twig parent() function. By including this, all the parent block’s content is included first:
{# src/Yoda/UserBundle/Resources/views/Security/login.html.twig #}
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="{{ asset('bundles/user/css/login.css') }}" />
{% endblock %}
Refresh now. Much less embarrassing looking. When you need to add CSS or JS to just one page, this is how you do it.
And by adding a little error class, it looks even better:
{# src/Yoda/UserBundle/Resources/views/Security/login.html.twig #}
{# ... #}
{% if error %}
<div class="error">{{ error.message }}</div>
{% endif %}
And while we’re making things look better, let’s open up base.html.twig and add a link tag to the Bootstrap CSS file. Just use a CDN URL for simplicity:
{# app/Resources/views/base.html.twig #}
{# ... #}
{% block stylesheets %}
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css"/>
...
{% block stylesheets %}
Back in login.html.twig, I’ll tweak the submit button so things look nicer:
{# src/Yoda/UserBundle/Resources/views/Security/login.html.twig #}
{# ... #}
<hr/>
<button type="submit" class="btn btn-primary pull-right">login</button>
Refresh! Ah, much better. I’m a programmer, but I don’t want the site to look totally embarrassing!
While we’re here, let’s change that “Bad Credentials” message, it’s a little, “programmery”. The message comes from deep inside Symfony. So to customize it, we’ll use the translator.
First, use the Twig trans filter on the message:
{# src/Yoda/UserBundle/Resources/views/Security/login.html.twig #}
{# ... #}
{% if error %}
<div class="error">{{ error.message|trans }}</div>
{% endif %}
Next, create a translation file in app/Resources/translations/messages.en.yml. This file is just a simple key-value pair of translations:
# app/Resources/translations/messages.en.yml
"Bad credentials": "Wrong password bro!"
Now, we just need to activate the translation engine in app/config.yml:
framework:
# ...
translator: { fallback: %locale% }
Ok now, try it! Again, so much better!
"Bad credentials": "Wrong password bro!" - actually won't work, there should be: "Bad credentials.": "Wrong password bro!" - a difference is a dot. I don't know if this is typo or they changed this string in newer version of Symfony2.
Hey Luken!
Very nice catch! And yes, the dot makes all the difference! This change (yes, the dot!) was introduced in Symfony 2.6 - 2.5 and before do not have the dot :) https://github.com/symfony/...
Cheers!
Hi all :)
I'm having problem setting the logout page. My security.yml file is a lot simpler than the one shown in the video. It only has the providers and firewalls sections; the firewalls section has only two sections called dev and main. I assume that this file is automatically generated. Why is mine so different? (I haven't changed anything in that file.) Is there a step that I missed? I know I can solve the problem by just copying the security.yml file in the video but I want to understand what's wrong/what I'm doing. Thanks!!
Hey Lily
Which version of Symfony did you install ? anyways I don't think your security file is wrong, actually it comes very light because it's on you the big security decisions ;)
What error are you experimenting ? I believe you get lost in setting up a login / logout path
Have a nice day!
Hi Diego,
I'm using symfony 2.3, but I think I fixed the problem now. Now there's another problem. After logging in, I'm taken to the default symfony welcome page instead of the event page. I think it's because the / route is set to that page, so I'm wondering if changing that route in routing.yml to indexAction of EventBundle and deleting the default AppBundle would fix the problem.
Thanks!
Lily
Oh yes, you can put in there whatever route you want, I usually point to my homepage, and it's competely ok if you remove that default page
When you get more confidence with Symfony, consider upgrading to 2.8 or even better if you upgrade version 3
Cheers!
Hey Roman!
What message is printing out? "Bad Credentials."? If so, the m(obviously) make sure that's exactly the string you have in your translation file. And then, try two other things:
1) Make sure the translation system is activated by uncommenting out the framework.translator key in config.yml (it's that *last* code block on this page)
2) Clear your cache: sometimes when adding your very first translation file in a directory, Symfony won't see it until you clear your cache.
Let me know if that helps!
Hej, Ryan
I figured out what's going on. I was using filter in login template with this "error.message" from symfony.com, This filter using default security messages. Now everything is ok
// composer.json
{
"require": {
"php": ">=5.3.3",
"symfony/symfony": "~2.4", // v2.4.2
"doctrine/orm": "~2.2,>=2.2.3", // v2.4.2
"doctrine/doctrine-bundle": "~1.2", // v1.2.0
"twig/extensions": "~1.0", // v1.0.1
"symfony/assetic-bundle": "~2.3", // v2.3.0
"symfony/swiftmailer-bundle": "~2.3", // v2.3.5
"symfony/monolog-bundle": "~2.4", // v2.5.0
"sensio/distribution-bundle": "~2.3", // v2.3.4
"sensio/framework-extra-bundle": "~3.0", // v3.0.0
"sensio/generator-bundle": "~2.3", // v2.3.4
"incenteev/composer-parameter-handler": "~2.0", // v2.1.0
"doctrine/doctrine-fixtures-bundle": "~2.2.0", // v2.2.0
"ircmaxell/password-compat": "~1.0.3", // 1.0.3
"phpunit/phpunit": "~4.1" // 4.1.0
}
}
Hey there!
I just replied on your previous comment, but you're right! If you're using Symfony 3, then some parts of this tutorial are out-of-date. The Symfony 3 version of this tutorial is here: http://knpuniversity.com/sc...
But, I need to make sure you're happy and have exactly what you need :). So, I'll send you an email shortly so we can make sure you're taken care of.
Cheers!