2

I want to add a CSS class to the body when the customer is logged in or not. I thought this would work:

<body>
    <customer_logged_in>
        <attribute name="class" value="customer-logged-in"/>
    </customer_logged_in>
    <customer_logged_out>
        <attribute name="class" value="customer-logged-out"/>
    </customer_logged_out>

But that doesn't seem to work. Is there any way to do this via the layout?

4

This can be easily done by hooking into the page renderer. After all, that's the class responsible for rendering the <body>-tag.

It always starts easy...

If you look at \Magento\Framework\View\Result\Page::render(), you'll see that the argument bodyAttributes is build like this:

'bodyAttributes' => $this->pageConfigRenderer->renderElementAttributes($config::ELEMENT_TYPE_BODY),

Magento\Framework\View\Page\Config\Renderer::renderElementAttributes() is a public method which we can hook into:

etc/frontend/di.xml:

<type name="Magento\Framework\View\Page\Config\Renderer">
    <plugin name="add_class_to_body"
            type="Vendor\Module\Plugin\Magento\Framework\View\Page\Config\Renderer"/>
</type>

And Renderer.php:

/**
 * @var Session
 */
protected $customerSession;

/**
 * Renderer constructor.
 * @param Session $customerSession
 */
public function __construct(
    Session $customerSession
)
{
    $this->customerSession = $customerSession;
}

/**
 * @param \Magento\Framework\View\Page\Config\Renderer $subject
 * @param callable $proceed
 * @param string $elementType
 * @return string
 */
public function aroundRenderElementAttributes(
    \Magento\Framework\View\Page\Config\Renderer $subject,
    callable $proceed,
    string $elementType
)
{
    $result = $proceed($elementType);

    if ($elementType === \Magento\Framework\View\Page\Config::ELEMENT_TYPE_BODY) {
        // Prepend CSS class:
        if ($this->customerSession->isLoggedIn()) {
            $result = str_replace(
                'class="', 
                'class="logged-in ', 
                $result
            );
        }
    }

    return $result;
}

Now, this should do the trick.

Well, as long as you're not using full page cache...

Full Page Cache and customer sessions

Full Page cache has a very interesting way of working that you should be aware of. That is: if all elements in the page qualify for caching, the customer session is 'depersonalized'. This is done to make sure that no customer details accidentally end up in the cached pages. After all, caching is your best friend and your worst enemeny.

The method responsible for this is Magento\PageCache\Model\DepersonalizeChecker::checkIfDepersonalize().

What this means for us, is that whenever a page is qualified for full page caching, our customer session is depersonalized. So we can no longer check $this->customerSession->isLoggedIn() to determine whether or not to add the class to the body.

To fix this you have to write a plugin for the depersonalizer as well:

di.xml:

<type name="Magento\PageCache\Model\DepersonalizeChecker">
    <plugin name="check_logged_in"
            type="Vendor\Module\Plugin\Magento\PageCache\Model\DepersonalizeChecker"/>
</type>

DepersonalizeChecker.php:

/**
 * @var Session
 */
protected $customerSession;

/**
 * DepersonalizeChecker constructor.
 * @param Session $customerSession
 */
public function __construct(
    Session $customerSession
)
{
    $this->customerSession = $customerSession;
}

/**
 * @param \Magento\PageCache\Model\DepersonalizeChecker $subject
 * @param bool $result
 * @return bool
 */
public function afterCheckIfDepersonalize(
    \Magento\PageCache\Model\DepersonalizeChecker $subject,
    bool $result
)
{
    if ($result === true) {
        return $this->customerSession->isLoggedIn() ? false : $result;
    }

    return $result;
}

BEWARE OF WHAT YOU ARE DOING HERE! Because basically you are disabling full-page cache entirely for every logged in customer. So if you are going to mess with the outcome of the depersonalizer, you'd be best of by adding some extra additions:

  • Perhaps there are only some customer groups that require a body class?
  • Perhaps it's only needed on certain pages?
  • Perhaps it's only needed according to some other parameters?

In conclusion

The combination of the two plugins should get you going into implementing this feature. It's funny how a simple task like 'adding a class to the body if condition x == y' can introduce a lot more complexity than - for example - adding a new shipping carrier or something.

It's just important that you know what you're doing.

1

I think the best approach would be to manually add handles customer-logged-in and customer-logged-out since they are not anymore present by default in Magento 2, and to utilize class attribute to add a particular class for these handles.

eg.

<body>
    <attribute name="class" value="customer-logged-in"/>

...

Here is the simple module which adds customer-logged-in and customer-logged-out handles: http://frankclark.xyz/modules/magento-2-get-customer_logged_in-and-customer_logged_out-layout-handles

1

The approach I use is to add these classes with JavaScript, it has the advantage of not messing around with any of M2's backend logic, Full Page Cache and is less likely to cause any issues.

var htmlBody = $("body[data-container='body']");
if ($(".customer-welcome").length) {
    htmlBody.addClass("customer-logged-in");
} else {
    htmlBody.addClass("customer-logged-out");
}

It adds the correct CSS class to the <body> tag by checking the document body to see if the .customer-welcome element is present, which for my theme is only in the document body if the customer is logged in (and I believe Magento default themes too).

0

You can directly do that by using below code. Its not best way but works fine. Put below code in any common file like footer.phtml

<?php 
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$customerSession = $objectManager->get('Magento\Customer\Model\Session');
if($customerSession->isLoggedIn()) { ?>
<script type="text/javascript">


require(['jquery'],function($){
        $( window ).load(function()  {
            $('body').addClass('customer-logged-in');
        });
    });


</script>
<?php } ?>
  • 1
    I would never instantiate objects directly into your template. You should always keep your template as dump as possible. But besides that, this solution is going to give side-effects: If you look at my answer, this is going to give problems when full page cache is enabled since customer session will always get depersonalized if the page qualifies for caching. Also: JavaScript will be executed further in the chain, so your styles will be applied seconds after the page is already rendered. – Giel Berkers Feb 24 '17 at 15:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.