[HowTo] Sonata User Bundle installieren

Diese Anleitung für die Installation des Sonata User Bundle unter Symfony2 basiert größtenteils auf der Installationsanleitung des Projektes. Ich habe jedoch festgestellt, dass die dortige Anleitung doch recht oberflächlich ist und es einige Stolpersteine (Fehlermeldungen) gibt, wenn man nur dieser Anleitung folgt.

Deshalb möchte ich hier die Installationsanleitung vervollständigen und Lösungen für die Fallstricke anbieten.

Grundinstallation

Zuerst das Bundle via Composer herunterladen:

php composer.phar require sonata-project/user-bundle

Dann die beiden benötigten Bundles in der AppKernel.php anmelden:

<?php
 
// app/AppKernel.php
 
public function registerbundles()
{
    return array(
        // ..
 
        // If you have already installed the Sonata Admin Bundle,
        // these bundles are already registered.
        new Sonata\CoreBundle\SonataCoreBundle(),
        new Sonata\BlockBundle\SonataBlockBundle(),
 
        // The required bundles for Sonata User.
        new FOS\UserBundle\FOSUserBundle(),
        new Sonata\UserBundle\SonataUserBundle('FOSUserBundle'),
 
        // ...
    );
}

Konfiguration

Je nachdem welche Sonata Bundles bereits installiert wurden (z.B. das Sonata Admin Bundle) kann es sein, dass bei dir schon einzelne Bestandteile bereits in deiner Konfiguration vorhanden sind.

app/config/config.yml

fos_user:
    db_driver:      orm
    firewall_name:  main
    user_class:     Application\Sonata\UserBundle\Entity\User
    group:
        group_class:   Application\Sonata\UserBundle\Entity\Group
        group_manager: sonata.user.orm.group_manager
    service:
        user_manager: sonata.user.orm.user_manager

doctrine:
    dbal:
        # Your database configuration ...
        
        types:
            json: Sonata\Doctrine\Types\JsonType

    orm:
        # And these in the config mapping definition (or enable auto_mapping):
        # auto_mapping: true
        
        entity_managers:
            default:
                mappings:
                    FOSUserBundle: ~  # This mapping is important (and missing in the original install instruction)
                    ApplicationSonataUserBundle: ~
                    SonataUserBundle: ~

sonata_user:
    security_acl: true
    manager_type: orm
    
sonata_block:
    blocks:
        #...
        sonata.user.block.menu:    # used to display the menu in profile pages
        sonata.user.block.account: # used to display menu option (login option)

app/config/security.yml

security:
    # [...]
 
    # The encoders are important and missing in the original install instruction.
    encoders:
        FOS\UserBundle\Model\UserInterface: bcrypt
    
    role_hierarchy:
        ROLE_ADMIN:       [ROLE_USER, ROLE_SONATA_ADMIN]
        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
        SONATA:
            - ROLE_SONATA_PAGE_ADMIN_PAGE_EDIT  # if you are using acl then this line must be commented

    providers:
        fos_userbundle:
            id: fos_user.user_manager

    firewalls:
        # Disabling the security for the web debug toolbar, the profiler and Assetic.
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false
 
        # -> custom firewall for the admin area of the URL
        admin:
            pattern:            /admin(.*)
            context:            user
            form_login:
                provider:       fos_userbundle
                login_path:     /admin/login
                use_forward:    false
                check_path:     /admin/login_check
                failure_path:   null
            logout:
                path:           /admin/logout
            anonymous:          true
 
        # -> end custom configuration
 
        # default login area for standard users
 
        # This firewall is used to handle the public login area
        # This part is handled by the FOS User Bundle
        main:
            pattern:             .*
            context:             user
            form_login:
                provider:       fos_userbundle
                login_path:     /login
                use_forward:    false
                check_path:     /login_check
                failure_path:   null
            logout:             true
            anonymous:          true

    acl:
        connection: default
        
    access_control:
        # URL of FOSUserBundle which need to be available to anonymous users
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
 
        # Admin login page needs to be access without credential
        - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY }
 
        # Secured part of the site
        # This config requires being logged for the whole site and having the admin role for the admin part.
        # Change these rules to adapt them to your needs
        - { path: ^/admin/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
        - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }

Routes

app/config/routing.yml

# ...

sonata_user:
    resource: '@SonataUserBundle/Resources/config/routing/admin_security.xml'
    prefix: /admin

sonata_user_security:
    resource: "@SonataUserBundle/Resources/config/routing/sonata_security_1.xml"

sonata_user_resetting:
    resource: "@SonataUserBundle/Resources/config/routing/sonata_resetting_1.xml"
    prefix: /resetting

sonata_user_profile:
    resource: "@SonataUserBundle/Resources/config/routing/sonata_profile_1.xml"
    prefix: /profile

sonata_user_register:
    resource: "@SonataUserBundle/Resources/config/routing/sonata_registration_1.xml"
    prefix: /register

sonata_user_change_password:
    resource: "@SonataUserBundle/Resources/config/routing/sonata_change_password_1.xml"
    prefix: /profile
 
# ...

Bundle generieren mit EasyExtends

Laut Anleitung soll jetzt mittels EasyExtends ein Bundle generiert werden. Bei mir kam jedoch bei diesem Schritt immer die Fehlermeldung: Bundle "ApplicationSonataUserBundle" does not exist or it is not enabled.

Um diese Fehlermeldung zu umgehen, müssen für diesen Schritt ein paar Sachen in der Konfiguration kurzzeitig auskommentiert bzw. verändert werden:

  • In der config.yml das bei den neu eingetragenen Mapping, die Zeile ApplicationSonataUserBundle: ~ auskommentieren.
  • In der selben Datei, den Klassenpfad Application\Sonata\UserBundle\Entity\User zu Sonata\UserBundle\Entity\BaseUser ändern.
  • Ebenfalls in der config.yml den Klassenpfad Application\Sonata\UserBundle\Entity\Group ändern zu Sonata\UserBundle\Entity\BaseGroup.

Jetzt kann mit Sonata EasyExtends unser Application Bundle für SonataUser erstellt werden:

php app/console sonata:easy-extends:generate SonataUserBundle --dest=src

Nachdem wir jetzt das Bundle generieren konnten, müssen die Sachen die wir gerade angepasst haben, wieder zurückgeändert werden:

Jetzt noch unser neu erstelltes/generiertes Bundle in der AppKernel.php anmelden.

<?php
 
// AppKernel.php
 
class AppKernel {
    public function registerbundles()
    {
        return array(
            // Application Bundles
            // ...
            new Application\Sonata\UserBundle\ApplicationSonataUserBundle(),
            // ...
 
        )
    }
}

Datenbankschema updaten

Hier endet jetzt die Original-Anleitung von Sonata und eigentlich ist die Implementierung des User Bundles jetzt auch abgeschlossen. Um jetzt jedoch auch unseren ersten User anlegen zu können, müssen noch die entsprechenden Datenbanktabellen angelegt werden:

php app/console doctrine:schema:update --force

Benutzer anlegen

Nettwerweise gibt es auch einen Befehl für das Terminal, um einen User Eintrag in der Datenbank anzulegen:

php app/console fos:user:create

Und für einen User mit Admin-Berechtigung:

php app/console fos:user:create --super-admin

Fehlermeldungen und ihre Lösung

"No encoder has been configured for account"

Wenn diese Fehlermeldung auftritt, müssen in der security.yml (app/config/security.yml) folgende Zeilen ergänzt werden:

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: sha512

Die Datei sollte dann so aussehen:

security:
    encoders:
        Symfony\Component\Security\Core\User\User: plaintext
        FOS\UserBundle\Model\UserInterface: sha512

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
 
# ...

Ein anderer Grund für diese Fehlermeldung kann sein, dass die Reihenfolge der Bundles in der AppKernel.php nicht richtig ist. Es ist wichtig, dass das SecurityBundle vor dem FOSUserBundle geladen wird.

new Symfony\Bundle\SecurityBundle\SecurityBundle(),
# ...
new FOS\UserBundle\FOSUserBundle(),

Meldung "Unrecognized field: usernameCanonical" bei Login

Wenn diese Meldung bei einem Loginversuch erscheint, nachdem man sich einen User angelegt hat, liegt dies daran, dass die Datenbanktabelle fos_user_user nicht vollständig erstellt wurde. Dort fehlen nämlich die sonst vorhanden Felder für Benutzername, Passwort etc.

Der Grund ist, dass nur das Schema für das Model vom SonataUserBundle von Doctrine angelegt wurde. Um das Problem zu beheben, muss in der config.yml (app/config/config.yml) ein zusätzliches Mapping eingetragen werden:

doctrine:
    dbal:
        orm:
            entity_managers:
                default:
                    mappings:
                        FOSUserBundle: ~

Danach muss noch das Schema bzw. die Datenbank mittels Doctrine aktualisiert werden und natürlich ein neuer (dann vollständiger) User angelegt werden:

php composer.phar doctrine:schema:update --force