FOSUserBundle Override Roles - Property “roles” in “Acme\DemoBundle\Entity\User” was already declared, but it must be declared only once

前端 未结 1 1483
花落未央
花落未央 2020-12-15 02:44

I like most people are trying to override the FOSUserBundle roles so I can map them ManyToMany to a Role Entity.

Unfortunately for some reason due to the mapping of

相关标签:
1条回答
  • 2020-12-15 03:22

    I had the same Issue, using Annotations also.

    Note: As some readers had issues putting all together I created a gitHub repo with my UserBundle. If you find that there's something missing in this HowTo, let me know so I add it.

    This post covers three aspects, DB based Roles with Tree structure implementation, framework configuration to also support RoleHierarchy (getReachableRoles) for DB roles. Without which it'd be useless to have roles in DB after all. And Doctrine Subscribers to create Roles upon certain Entity being persisted.

    The changes FOS had to make were deep, and well documented, but I must say that a single HowTo Use sample code would have prevented me from reading a lot, (not complaining, at least I know a little on compiler passes now.)

    Roles to DB

    I'm using Sf 2.4, but this should work since 2.3. Here is my solution's involved files, consider one step per file:

    ./:
    composer.json
    
    src/Application/UsuarioBundle/:
    ApplicationUsuarioBundle.php
    
    src/Application/UsuarioBundle/Resources/config/doctrine/model/:
    User.orm.xml
    
    src/Application/UsuarioBundle/Entity/:
    Role.php  Usuario.php
    

    In the copmoser.json I upgraded doctrine-bundle so it includes required files:

    "require": {
    ...
        "doctrine/doctrine-bundle": "~1.3@dev",
    ...
    }
    

    In the Bundle.php file you must register a compiler pass

    namespace Application\UsuarioBundle;
    
    use Symfony\Component\DependencyInjection\ContainerBuilder;
    use Symfony\Component\HttpKernel\Bundle\Bundle;
    use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass;
    
    class ApplicationUsuarioBundle extends Bundle
    {
    
        public function build(ContainerBuilder $container)
        {
            parent::build($container);
            $mappings = array(
                realpath(__DIR__ . '/Resources/config/doctrine/model') => 'FOS\UserBundle\Model',
                realpath(__DIR__ . '/Resources/config/doctrine/model') => 'FOS\UserBundle\Entity', 
            );
    
            $container->addCompilerPass(
                DoctrineOrmMappingsPass::createXmlMappingDriver(
                    $mappings, array('fos_user.model_manager_name'), false
                )
            );
    }
    

    This is the dependency imported by the new version of doctrine-bundle:

    `\Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass`. 
    

    I assume that this mapping info is added later than FOSUSerBundle's because I just repeated the process (simplified for only ORM) I saw in FOSUerBundle.php hoping it'd take precedence and it did.

    The mappings in User.orm.xml are the exact copy of ./vendor/friendsofsymfony/user-bundle/FOS/UserBundle/Resources/config/doctrine/model/User.orm.xml with line #35 commented out. This deletes the conflicting mapping on roles in the mapped super class.

    <!--<field name="roles" column="roles" type="array" />-->
    

    From now on, you just do what you wanted to do in 1st place, implement your idea of Roles. Here's mine: The User Class that extends FOS\UserBundle\Model\User, but now with the mappings you used in your bundle.

    src/Application/UsuarioBundle/Entity/Role.php

    And the Role Class:

    src/Application/UsuarioBundle/Entity/Usuario.php

    After doing it you- can see that the right SQL changes are dumped by schema update --dump-sql.

    $ php app/console doctrine:schema:update --dump-sql --complete
    CREATE TABLE fos_usuario_role (usuario_id INT NOT NULL, role_id INT NOT NULL, INDEX IDX_6DEF6B87DB38439E (usuario_id), INDEX IDX_6DEF6B87D60322AC (role_id), PRIMARY KEY(usuario_id, role_id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
    CREATE TABLE fos_role (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(30) NOT NULL, role VARCHAR(20) NOT NULL, UNIQUE INDEX UNIQ_4F80385A57698A6A (role), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
    ALTER TABLE fos_usuario_role ADD CONSTRAINT FK_6DEF6B87DB38439E FOREIGN KEY (usuario_id) REFERENCES fos_user (id) ON DELETE CASCADE;
    ALTER TABLE fos_usuario_role ADD CONSTRAINT FK_6DEF6B87D60322AC FOREIGN KEY (role_id) REFERENCES fos_role (id) ON DELETE CASCADE;
    ALTER TABLE fos_user DROP roles;
    

    Still, I'm not representing Role hierarchies, which I need.

    Hope it's useful for someone. I'm sure you already solved this, or lost your job :p.

    Documentation I followed:

    https://github.com/symfony/symfony/pull/7599
    https://github.com/FriendsOfSymfony/FOSUserBundle/pull/1081
    http://symfony.com/doc/2.4/cookbook/doctrine/mapping_model_classes.html
    http://symfony.com/doc/current/cookbook/service_container/compiler_passes.html
    

    RoleHierarchy Implementation

    Files involved in the Solution:

    // The Magician, for I just re-instantiated RoleHierarchyVoter & ExpressionVoter 
    // classes as ApplicationUsuarioBundle services; passing my RoleHierarchy 
    // implementation. 
    src/Application/UsuarioBundle/Role/RoleHierarchy.php
    
    // duplicating security.access.expression_voter && 
    // application_usuario.access.role_hierarchy_voter BUT WITH NEW 
    // RoleHierarchy ARGUMENT
    src/Application/UsuarioBundle/Resources/config/services.xml
    
    // Entities, important methods are collection related
    src/Application/UsuarioBundle/Entity/Role.php
    src/Application/UsuarioBundle/Entity/Usuario.php
    
    // Edited, commented out regular hardcoded roleHierarchy
    app/config/security.yml
    
    // CRUD related, sample files will add dependencies to lexik/form-filter-bundle; 
    // docdigital/filter-type-guesser; white-october/pagerfanta-bundle
    src/Application/UsuarioBundle/Controller/RoleController.php
    src/Application/UsuarioBundle/Form/RoleType.php
    src/Application/UsuarioBundle/Resources/views/Role/edit.html.twig
    src/Application/UsuarioBundle/Resources/views/Role/index.html.twig
    src/Application/UsuarioBundle/Resources/views/Role/new.html.twig
    src/Application/UsuarioBundle/Resources/views/Role/show.html.twig
    

    you can see files in this gist

    Or access each file direcctly, (as Gist doesn't preserve the listing order).

    src/Application/UsuarioBundle/Role/RoleHierarchy.php

    src/Application/UsuarioBundle/Resources/config/services.xml

    src/Application/UsuarioBundle/Entity/Role.php

    src/Application/UsuarioBundle/Entity/Usuario.php

    app/config/security.yml

    src/Application/UsuarioBundle/Controller/RoleController.php

    src/Application/UsuarioBundle/Form/RoleType.php

    src/Application/UsuarioBundle/Resources/views/Role/edit.html.twig

    Documentation followed:

    cookbook: security voters

    Components: Security

    reference: Service tags priorities

    Souce: RoleHierarchyInterface

    Doctrine Subscribers

    You'll get this far to realize there's something missing...

    The main reason I ported Roles to DB is because I'm dealing with a dynamic (from a Structural perspective) Application that enables the user to configure a workflow. When I add a new Area, with a new Process, with a new Activity (or update either the name or the parent-Child relationship, or remove any), I need new Roles to be generated automatically.

    Then you think of the Doctrine Subscribers for LyfeCycleEvents, but adding new entities in the PrePersist/PreUpdate will demand a nested flush, which in my case messes things up, its easier when you just need to update some fields on already "computedChanges" entities.

    So what I used to hook and create/edit/delete roles is the onFlush, at which point the computChangeSet() works fine for adding new entities..

    I'll leave the ProcessRolesSubscriber Gist as an example.

    0 讨论(0)
提交回复
热议问题