# Data collection

Learn how to collect information for personalization.

This guide explains how to associate the visitor with a user from your application, enrich their profile, and detect their locale, so you can deliver personalized experiences.

Identity and locale are resolved on the server. Profile and session attributes are collected in the browser, which you trigger from your templates with the [`croct`](/reference/sdk/symfony/api/twig/croct-filter) Twig filter.

## Identity \[#identity]

When your application has authenticated users, you can link the visitor session with the logged-in user so you can recognize them when they return.

### Automatic reconciliation

When [Symfony Security](https://symfony.com/doc/current/security.html) is configured, the bundle reconciles the visitor with the authenticated user on every request, using the user identifier as the [user ID](/reference/cql/context#user). This is enabled by default and requires no code, controlled by the [`identity.enabled`](/reference/sdk/symfony/api/configuration#identity-prop) option.

You can then recognize identified users in your queries using the [`user`](/reference/cql/context#user) variable:

```cql
user is identified
```

### Custom identifier

By default, the bundle uses the Symfony user identifier, such as the email or username. To reconcile with a different identifier, such as a database ID, implement the [identity resolver](/reference/sdk/php/api/resolvers/identity-resolver) interface:

**src/Croct/AuthIdentityResolver.php**

```php
<?php

namespace App\Croct;

use App\Entity\User;
use Croct\Plug\IdentityResolver;
use Symfony\Bundle\SecurityBundle\Security;

final class AuthIdentityResolver implements IdentityResolver
{
    private Security $security;

    public function __construct(Security $security)
    {
        $this->security = $security;
    }

    public function getUserId(): ?string
    {
        $user = $this->security->getUser();

        return $user instanceof User ? (string) $user->getId() : null;
    }
}
```

Then bind it to the interface so the bundle uses it instead of the default:

**config/services.yaml**

```yaml
services:
    Croct\Plug\IdentityResolver: '@App\Croct\AuthIdentityResolver'
```

### Manual control

If you do not use Symfony Security, disable automatic reconciliation and manage identity yourself:

**config/packages/croct.yaml**

```yaml
croct:
    identity:
        enabled: false
```

Then call [`identify`](/reference/sdk/php/api/core/plug/identify) when the user logs in and [`anonymize`](/reference/sdk/php/api/core/plug/anonymize) when they log out, using the injected [`Plug`](/reference/sdk/php/api/core/plug) service:

**src/Controller/SessionController.php**

```php
<?php

namespace App\Controller;

use Croct\Plug\Plug;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

final class SessionController extends AbstractController
{
    #[Route('/login', name: 'login', methods: ['POST'])]
    public function login(Request $request, Plug $croct): Response
    {
        $user = $this->authenticate($request);

        $croct->identify($user->getId());

        return $this->redirectToRoute('dashboard');
    }

    #[Route('/logout', name: 'logout')]
    public function logout(Plug $croct): Response
    {
        $croct->anonymize();

        return $this->redirectToRoute('home');
    }
}
```

## Profile and session attributes \[#attributes]

Besides identity, you can enrich the visitor's profile and session with information relevant to your business. These calls run in the browser, so you trigger them from your templates with the [`croct`](/reference/sdk/symfony/api/twig/croct-filter) Twig filter, passing server-side values through Twig.

### Profile data

Profile attributes describe the user across visits, such as their name, email, or interests. For example, to sync the signed-in user's details into their profile:

**templates/base.html.twig**

```twig
<!DOCTYPE html>
<html>
    <head>
        <title>My application</title>
    </head>
    <body>
        {% block body %}{% endblock %}

        {% if app.user %}
            {% apply croct %}
                croct.user.edit()
                    .set('name', {{ app.user.fullName|json_encode|raw }})
                    .set('email', {{ app.user.email|json_encode|raw }})
                    .save();
            {% endapply %}
        {% endif %}
    </body>
</html>
```

### Session data

Session attributes describe the current visit, such as the plan a user is considering on the pricing page:

**templates/pricing/index.html.twig**

```twig
{% extends 'base.html.twig' %}

{% block body %}
    <select id="plan-selector">
        <option value="basic">Basic</option>
        <option value="premium">Premium</option>
        <option value="enterprise">Enterprise</option>
    </select>

    {% apply croct %}
        const selector = document.getElementById('plan-selector');

        selector.addEventListener('change', () => {
            croct.session.edit()
                .set('plan', selector.value)
                .save();
        });
    {% endapply %}
{% endblock %}
```

See [Data collection](/reference/sdk/javascript/data-collection) for the full client-side API.

## Locale detection \[#locale]

The bundle detects the visitor locale from the [request](https://symfony.com/doc/current/translation/locale.html) automatically and forwards it as the preferred locale, so content is returned in the matching language out of the box. This is enabled by default, controlled by the [`locale.enabled`](/reference/sdk/symfony/api/configuration#locale-prop) option.

To pin a fixed locale or set a fallback when detection is disabled, use the [`locale.default`](/reference/sdk/symfony/api/configuration#locale-prop) option:

**config/packages/croct.yaml**

```yaml
croct:
    locale:
        default: en-us
```

For full control, implement the [locale resolver](/reference/sdk/php/api/resolvers/locale-resolver) interface and bind it the same way as a custom identity resolver:

**config/services.yaml**

```yaml
services:
    Croct\Plug\LocaleResolver: '@App\Croct\AccountLocaleResolver'
```

## Explore

- [Audiences](/explanation/audience): Understand how to deliver content to specific groups of users.
- [CQL](/reference/cql/syntax): Get the basics on how to write conditions to define audiences.
