0

I've this function that returns a JSON response:

public function getUsersAction()
{
    $response = array();
    $em = $this->getDoctrine()->getManager();

    $entities = $em->getRepository("UserBundle:User")->findAll();

    $roles = array(
        "ROLE_PROFILE_ONE" => "Facturación y Entrega",
        "ROLE_PROFILE_TWO" => "Envío",
        "ROLE_ADMIN" => "Administrador",
        "ROLE_USER" => "No posee roles asignados"
    );

    $users = array();
    foreach ($entities as $entity)
    {
        $user = array();

        $user[] = $entity->getUsername();
        $user[] = $entity->getEmailCanonical();
        $user[] = $entity->getRoles();
        $user[] = $entity->getGroupNames() != NULL ? $entity->getGroupNames() : "-";
        $users[] = $user;
    }

    $response[ 'data' ] = $users;
    return new JsonResponse($response);

}

I access it through Twig template via Ajax call, that's work! Now since getRoles() from User (FOSUser) model return the DB value as per example: ROLE_PROFILE_ONE, ROLE_ADMIN, ROLE_USER, how do I format the output to display friendly names based on $roles defined array? I tried to do a foreach loop inside the foreach ($entities as $entity) ... and set a new array but the nested calls is to big since my Apache goes down. Any help?

Trying to be more clear with input/output example

Well, this is what we have as input (what the function returns, I made a ladybug_dump($entities) to get the output):

array(6)
   [0]: object(Tanane\UserBundle\Entity\User)
      >> Properties
      ...
      # [roles]: array(1)
         [0]: string (16) "ROLE_PROFILE_ONE"
      ...
   [1]: object(Tanane\UserBundle\Entity\User)
      >> Properties
      ...
      # [roles]: array(2)
         [0]: string (16) "ROLE_PROFILE_TWO"
         [1]: string (16) "ROLE_PROFILE_ONE"
      ...

When I access that on Twig, at template, I got:

User1 ROLE_PROFILE_ONE
User2 ROLE_PROFILE_TWO, ROLE_PROFILE_ONE

But I need this output instead:

User1 Facturación y Entrega
User2 Envío, Facturación y Entrega

It's more clear now?

4
  • Does Symfony return stdClass array object or PHP assoc array Commented Oct 10, 2014 at 3:04
  • @NoahMatisoff an assoc array: # [roles]: array(1) [0]: string (16) "ROLE_PROFILE_ONE" Commented Oct 10, 2014 at 3:11
  • Please edit the op with the current array formatted properly and the output you want after the problem is solved. Commented Oct 10, 2014 at 4:33
  • @NoahMatisoff done, take a look Commented Oct 10, 2014 at 4:46

2 Answers 2

1

If your roles are always going to transform to those specific string then you could add them to your user model and build a list of the roles in a getTransformedRoles() method like..

User.php

class User extends BaseUser implements UserInterface
{
    const ROLE_PROFILE_ONE = 'Facturación y Entrega';
    const ROLE_PROFILE_TWO = 'Envío';
    const ROLE_ADMIN       = 'Administrador';
    const ROLE_USER        = 'No posee roles asignados';
    ...
    public function getTransformedRoles()
    {
        $transformed = array();

        foreach ($this->getRoles() as $role) {
            $role = strtoupper($role);
            $const = sprintf('self::%s', $role);

            // Do not add if is $role === ROLE_USER
            if (FOS\UserBundle\Model\UserInterface::ROLE_DEFAULT === $role) {
                continue;
            }

            if (!defined($const)) {
                throw \Exception(sprintf('User does not have the role constant "%s" set', $role));
            }

            $transformed[] = constant($const);
        )

        // If no roles add self::ROLE_USER
        if (empty($transformed)) {
            $transformed[] = self::ROLE_USER;
        }

        return $transformed;
    }
    ....
}

This would then return the fully transformed array of roles (using $user->getTransformedRoles()) where ever you may want them as opposed to just the single use case.

Alternatively you could do it with a service that does the same kind of transformation but with a set of varying roles and transformations that you could set via the config.yml.

Update

To use this as a service with your role transformations specified in your app/config/config you could do the following..

Acme/SomethingBundle/DependencyInjection/Configuration

$rootNode
    ->children()
        ->arrayNode('role_transformations')
            ->defaultValue(array())
            ->useAttributeAsKey('name')
                ->prototype('scalar')->cannotBeEmpty()->end()
            ->end()
        ->end()
    ->end();

Acme/SomethingBundle/DependencyInjection/AcmeSomethingExtension

$container->setParameter(
    'acme.something.role_transformations', 
    $config['role_transformations']
);

Then in your app/config/config.yml

// For an empty array
role_transformations: ~ // Or not even at all, it defaults to an empty array
// For transformation
role_transformations:
    ROLE_PROFILE_ONE: 'Facturación y Entrega'
    ROLE_PROFILE_TWO: 'Envío'
    ROLE_ADMIN: 'Administrador'
    ROLE_USER: 'No posee roles asignados'

Create your service and inject the role_transformations

parameters:
    acme.something.role_transformer.class: Acme/SomethingBundle/Transformer/RoleTransformer

services:
    acme.something.role_transformer:
        class: %acme.something.role_transformer.class%
        arguments:
            - %acme.something.role_transformations%

Then in your service file (Acme/SomethingBundle/Transformer/RoleTransformer)

class RoleTransformer implements RoleTransformerInterface
{
    const ROLE_DEFAULT = 'ROLE_USER';
    protected $rolesTransformations;

    public function __construct(array $roleTransformations)
    {
        $this->roleTransformations = $roleTransformations;
    }

    public function getTransformedRolesForUser($user)
    {
        if (!method_exists($user, 'getRoles')) {
            throw new \Exception('User object has no "getRoles" method');
            // Alternatively you could add an interface to you user object specifying 
            // the getRoles method or depend on the Symfony security bundle and 
            // type hint Symfony\Component\Security\Core\User\UserInterface
        }

        return $this->getTransformedRoles($user->getRoles();
    }

    public function getTransformedRoles(array $roles)
    {
        $transformedRoles = array()

        foreach ($roles as $role) {
            $role = strtoupper($role);

            if (null !== $transformedRole = $this->getTransformedRole($role)) {
                $transformedRoles[] = $transformedRole;
            }
        }

        return $transformedRoles;
    }

    public function getTransformedRole($role)
    {
        if (self::ROLE_USER === $role) {
            return null;
        }

        if (!array_key_exists($role, $this->roleTransformations)) {
            throw \Exception(sprintf(
                'Role "%s" not found in acme.something.role_transformations', $role)
            );
        }

        return $this->roleTransformations[$role];
    }
}

This could be then be injected into a service or controller and used like

$transformer = $this->container->get('acme.something.role_transformer');
// Or injected via the DI

$roles = $transformer->getTransformedRolesForUser($user);
// For all of a users roles
$roles = $transformer->getTransformedRoles($user->getRoles());
// For an array of roles
$role = $transformer->getTransformedRole('ROLE_PROFILE_ONE');
// For a single role, or null if ROLE_USER
Sign up to request clarification or add additional context in comments.

5 Comments

I like this solution and is what I'll use, now @Qoop I have a problem here and I forgot to mention in the main post, by default Symfony2 will always, or at least this is what I think, assign ROLE_USER so now in the transformed data I have strings like this Envío,No posee roles asignados, Envío,Facturación y Entrega,No posee roles asignados which is wrong since No posee roles asignados should appear only if no roles are assigned to user in which case the ROLE_USER should be use, did you have any idea in how to avoid this or fix this? BTW thanks for your answer
You could catch that special case in the getTransformedRoles method ("unless ROLE_USER is the only role, remove ROLE_USER from output"), or adjust the Symfony role hierarchy so that ROLE_USER is not auto-assigned to everyone.
@Qoop in your answer you said this Alternatively you could do it with a service that does the same kind of transformation but with a set of varying roles and transformations that you could set via the config.yml. could you improve your answer with this solution too in order to give a alternative? Will be nice for newbies!!
I will do if I find the time.
I've updated. Its' not really checked through though so it might not be bang on.
1

Hmm, you've tried to do a nested loop to create a new array, something like this?:

$users = array();
foreach ($entities as $entity)
{
    $user = array();

    $user[] = $entity->getUsername();
    $user[] = $entity->getEmailCanonical();

    $rolearray = [];
    foreach ($entity->getRoles() as $role)
    {
        $rolearray[] = $roles[$role];
    }
    $user[] = $rolearray;

    $user[] = $entity->getGroupNames() != NULL ? $entity->getGroupNames() : "-";
    $users[] = $user;
}

That's what I'd do. You could use array_map instead, but I don't see why that would differ significantly. It seems very unlikely this would be so resource intensive as to bring down the server, I would strongly suspect some other problem if that's happening.

1 Comment

Weird, is the same I did and it takes down my whole Apache server, anyway I'm trying @Qoop way but thanks to you too

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.