Buy
Buy

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

We know how to get the user in a template:

... line 1
<html lang="en">
... lines 3 - 15
<body>
<nav class="navbar navbar-expand-lg navbar-dark navbar-bg mb-5">
... lines 18 - 21
<div class="collapse navbar-collapse" id="navbarNavDropdown">
... lines 23 - 34
<ul class="navbar-nav ml-auto">
{% if is_granted('ROLE_USER') %}
<li class="nav-item dropdown" style="margin-right: 75px;">
<a class="nav-link dropdown-toggle" href="http://example.com" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img class="nav-profile-img rounded-circle" src="{{ app.user.avatarUrl(100) }}">
</a>
... lines 41 - 47
</li>
... lines 49 - 52
{% endif %}
</ul>
</div>
</nav>
... lines 57 - 74
</body>
</html>

And... we know how to get the user from a controller with $this->getUser():

... lines 1 - 11
class AccountController extends BaseController
{
... lines 14 - 16
public function index(LoggerInterface $logger)
{
$logger->debug('Checking account page for '.$this->getUser()->getEmail());
... lines 20 - 22
}
}

But... what about from inside a service? Because this nice $this->getUser() shortcut will only work in controllers.

To show you what I mean, I need to remind you of a feature we built a long time ago, like 3 screencasts ago. Click on any article. Then, click anywhere on the web debug toolbar to open the profiler. Find the "Logs" section and click on "Info & Errors". There it is!

They are talking about bacon again!

This is a super-informative log message that we added from inside our markdown service: src/Service/MarkdownHelper.php:

... lines 1 - 8
class MarkdownHelper
{
... lines 11 - 23
public function parse(string $source): string
{
if (stripos($source, 'bacon') !== false) {
$this->logger->info('They are talking about bacon again!');
}
// skip caching entirely in debug
if ($this->isDebug) {
return $this->markdown->transform($source);
}
$item = $this->cache->getItem('markdown_'.md5($source));
if (!$item->isHit()) {
$item->set($this->markdown->transform($source));
$this->cache->save($item);
}
return $item->get();
}
}

This code parses the article content through markdown and caches it. But also, if it sees the word "bacon" in the content ... which every article has in our fixtures, it logs this message.

So here's our challenge: I want to add information about who is currently logged in to this message. To do that, we need to answer one question: how can we access the current User object from inside a service?

The Security Service

The answer is... of course - by using another service. The name of the service that gives you access to the User object is easy to remember. Add another argument: Security $security:

... lines 1 - 7
use Symfony\Component\Security\Core\Security;
class MarkdownHelper
{
... lines 12 - 18
public function __construct(AdapterInterface $cache, MarkdownInterface $markdown, LoggerInterface $markdownLogger, bool $isDebug, Security $security)
{
... lines 21 - 25
}
... lines 27 - 48
}

I'll hit Alt+Enter and click "Initialize Fields" to create that property and set it:

... lines 1 - 7
use Symfony\Component\Security\Core\Security;
class MarkdownHelper
{
... lines 12 - 16
private $security;
public function __construct(AdapterInterface $cache, MarkdownInterface $markdown, LoggerInterface $markdownLogger, bool $isDebug, Security $security)
{
... lines 21 - 24
$this->security = $security;
}
... lines 27 - 48
}

So how can we use this service? Well... let's just look inside! Hold Command or Control and click to open the Security class. It has just two important methods: getUser() and isGranted(). Hey! That makes a lot of sense! Remember, once you set up authentication, there are only two things you can do with security: get the user object or figure out whether or not the user should have access to something, like a role. That's what isGranted() does.

Close that and move down to the log message. Ok, we could get the user object, maybe call getEmail() on it, and concatenate that onto the end of the log string. But! There's a cooler way. Add a 2nd argument to info: an array. Give it a user key - I'm just making that up - and set it to the user object: $this->security->getUser():

... lines 1 - 9
class MarkdownHelper
{
... lines 12 - 27
public function parse(string $source): string
{
if (stripos($source, 'bacon') !== false) {
$this->logger->info('They are talking about bacon again!', [
'user' => $this->security->getUser()
]);
}
... lines 35 - 47
}
}

Unrelated to security, every method on the logger, like info(), debug() or alert(), has two arguments. The first is the message string. The second is an optional array called a "context". This is just an array of any extra info that you want to include with the log message. I invented a user key and set it to the User object.

Let's go see what it looks like! Refresh! Then, click back into the profiler, find logs, and check out "Info & Errors". The message looks the same, but now we have a "Show Context" link. Click that! Nice! There is our entire User object in all of its glory. That's pretty sweet. And now, you know how to get the User object from anywhere.

Next, we get to talk about a feature called "role hierarchy". A little feature that will make you love working with roles, especially if you have complex access rules.

Leave a comment!

  • 2018-10-20 Serge Boyko

    Actually, I somehow missed that tip. Thanks!

  • 2018-10-19 Diego Aguiar

    Hey Serge Boyko

    > Doesn't it mean that it only shows the User object of a user, who is currently logged in?

    Well, yes, the `Security::getUser()` method returns the logged in user object or null in case you are anonymous

    Were you logged in? You could dump what `Security::getUser()` returns and see what it's actually returning.

    Cheers!

  • 2018-10-19 Diego Aguiar

    I know you already know this :) but just in case someone else reads it. Check the TIP on this episode: https://symfonycasts.com/sc...

  • 2018-10-19 Serge Boyko

    But how does it log all information about a User?

    In markdown.log there's only this information:
    [2018-10-19 13:49:37] markdown.INFO: They are talking about bacon again! {"user":"[object] (App\\Entity\\User: {})"} []

    There's no specific id of the user.
    Doesn't it mean that it only shows the User object of a user, who is currently logged in?

  • 2018-10-19 Serge Boyko

    The "Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait::getUser()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "App\Controller\BaseController".

    What about that deprecation message, should we ignore it? Is there another way to get user autocompletion from PhpStorm?

  • 2018-10-15 Diego Aguiar

    Hey cybernet2u

    That's a good question, what you can do is create a form type specific for updating a user, in that form type you can add a "UserPassword" field type, that field type has all the logic you are looking for. You can find more detailed info about it in the docs: https://symfony.com/doc/cur...

    Cheers!

  • 2018-10-10 cybernet2u

    care to teach us how to create a form to update the user profile ?

    password field is a must, therefore we cant create a form to update profile without password :(