I'm developing the Admin Panel for my website, and one part of it is managing users. I'm using FOSUserBundle for those tasks.
I'm using custom template (AdminLTE) for forms, and I cannot add a dropdown to select roles for user when I add a new one.
In UserType.php
$builder->add('roles', 'choice', array(
'label' => 'User Role',
'choices' => $this->roleHelper->getRolesForForm()
));
In WCB\SaleBundle\Helper\RoleHelper.php
...
public function getRolesForForm()
{
$roleList = self::flatArray($this->rolesHierarchy);
$roles = array();
foreach ($roleList as $roleId => $roleName) {
if ($roleId == 'ROLE_ADMIN') {
$roles[$roleName] = 'Admin';
}
if ($roleId == 'ROLE_USER') {
$roles[$roleName] = 'User';
}
}
return $roles;
}
...
Above getRolesForForm() function will return this array, which is correct format for using with Symfony's choice field type:
Array
(
[ROLE_ADMIN] => Admin
[ROLE_USER] => User
)
And the form's not working anymore, with this exception:
The value of type "array" cannot be converted to a valid array key.
When I add 'multiple' = true to form builder, it works, but it's not a dropdown. It's HTML select box which allow multiple selection.
$builder->add('roles', 'choice', array(
'label' => 'User Role',
'choices' => $this->roleHelper->getRolesForForm(),
'multiple' => true
));
I think, for role selection, it should be a dropdown, not a multiple-selection box. How can I achieve this? Anything wrong with my code? Thank you :)
I think it should be a multiple-selection box indeed.
Take into account that a user can (and usually will) have several roles. In your case, a user who has ROLE_ADMIN also has ROLE_USER (ROLE_ADMIN "includes" ROLE_USER if you are using FOSUserBundle).
Not really sure if this may be the problem since I don't have your full code and I am not sure what self::flatArray($this->rolesHierarchy) is returning but please notice that you are using $roleName, not $roleId as your array key. If $roleName is not a string then you will get this problem.
I faced same issue lately and this is my work around (I created DataTransformer):
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
...
$builder->get('roles')->addModelTransformer(new RolesTransformer());
}
}
And DataTransformer class:
use AppBundle\Entity\User;
use Symfony\Component\Form\DataTransformerInterface;
class RolesTransformer implements DataTransformerInterface
{
public function transform($roles)
{
foreach ($roles as $role) {
if ($role !== User::ROLE_DEFAULT) {
return $role;
}
}
return $roles[0];
}
public function reverseTransform($string)
{
return [
$string, User::ROLE_DEFAULT
];
}
}
The role attribute from UserModel of FOSUserBundle expected an array. If you use a select normal, they don't return an array.
Related
How can i access the id of a sonata admin entity. suppose i have an entity EndPoint which have a function getProject(), how can i get the id of that project. I tried the code bellow but this gave me the following error: Attempted to call an undefined method named "getProject" of class "ContainerAgbGotv\srcApp_KernelDevDebugContainer".
class EndPointAdmin extends AbstractAdmin{
protected function configureFormFields(FormMapper $form)
{ $container = $this->getConfigurationPool()->getContainer();
$em = $container->getProject();
$array = [];
foreach ($em as $ems) {
if (!empty($ems->getEnv())) {
$array[$ems->getEnv()] = $ems->getEnv();
}}
$result = array_unique($array);
$form
->add('name',ChoiceType::class, [
'choices'=> $result,
'placeholder' => 'Choose an option',
'required' => false
])
->add('ipAdress',TextType::class)
->add('port',IntegerType::class)
;
}
thanks for helping.
In an admin you will have access to
$this->getSubject()
which will return you the current entity for that admin. It's worth noting configureFormFields is used for both creating and editing an entity so getSubject() may return null.
I'm trying since several days to find out how to ask to easyadmin to store the author_id (or user_id to be specific), but I can't find by myself.
Basicly, by using the createListQueryBuilder function in a specific controller to override the easyadmin controller, I found the way to display for each logged user, the articles they created.
Now I'm locked since several days as i dont know how to tell to easyadmin to store the currently logged author_id (or user_id),the solution is to get the author_id with $this->getUser()->getId() that's ok, but where or how can i link this with my author_id before persisting it to the database
For now if i use this code below the author can add manually its author_id but that's not safe:
{ property: 'user', type: 'entity', type_options: { class: 'AppBundle\Entity\User' ,multiple: false,expanded: true} }
I would like to use an input hidden or any other way that could be more safe
Thank you any anybody could help me
EDIT:it was the hell to find the solution but i finaly found the way to display ONLY THE CURRENT LOGGED USER(who can be a basic admin or contributor if you prefer) or THE FULL LIST OF REGISTERED USERS IN DATABASE if the current logged user as a specific role (in this case that's the website main admin)
here is the solution:
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\User;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\Annotations\Route;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use AlterPHP\EasyAdminExtensionBundle\EasyAdminExtensionBundle;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AdminController as BaseAdminController;
/**
* Article controller.
*
* #Route("articles")
*/
class ArticlesController extends BaseAdminController
{
/**
* #Route("/", name="easyadmin")
*/
public function indexAction(Request $request)
{
return parent::indexAction($request);
}
public function createListQueryBuilder($entityClass, $sortDirection, $sortField = null, $dqlFilter = null)
{
if(!$this->getUser()->hasRole('ROLE_DIRECTION_ADMIN'))
{
$response = parent::createListQueryBuilder('Articles', $sortDirection, $sortField, $dqlFilter); // TODO: Change the autogenerated stub
$response->where('entity.userId = :userId')->setParameter('userId', $this->getUser()->getId());
return $response;
}
$response = parent::createListQueryBuilder('Articles', $sortDirection, $sortField, $dqlFilter); // TODO: Change the autogenerated stub
return $response;
}
public function createEntityFormBuilder($entity, $view)
{
$formBuilder = parent::createEntityFormBuilder($entity, $view);
// Here I overwrite field to be disabled
if(!$this->getUser()->hasRole('ROLE_DIRECTION_ADMIN'))
{
$formBuilder->add('user', EntityType::class, array(
'class' => 'AppBundle:User',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->where('u.id = :id')->setParameter('id',$this->getUser()->getId());
},
));
return $formBuilder;
}
$formBuilder->add('user', EntityType::class, array(
'class' => 'AppBundle:User',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->orderBy('u.username', 'asc');
},
));
return $formBuilder;
}
}
1/i created a custom ArticlesController and added 1 method to display the logged user article list if they have a low level role,and display all the website articles if the user role is high
2/ i created a second method which add the currently logged user_id field to the easyadmin formbuilder if the user has a low level role(a contributor for example) OR a full list of users in case the currently logged user is the website main admin
I'm using Media in User Entity(avatar).
At first I used sonata_media_type. It worked good.
The problem is I'm using ManyToOne - so the admin can select one from the list. To achieve this I have to use sonata_type_model_list - this has list,new,delete actions. I removed delete by 'btn_delete' => ''. Here the list action works good(up to now).
The real PROBLEM is at new action. The new action window load from ajax - and it has File/Reference, Category (Two fields).
Here I need to remove Category field entirely(list,new,delete). Why do we need this? Because it is useless!.
LIST - only display the 'context' => 'profile' from
'link_parameters'. So here the LIST action is useless.
NEW - New action can create new context, but it will not display in the
LIST(right now). So I don't need this. If I need I'll create from
ClassificationBundle.
DELETE - Delete action has no effect(right now - here).
MY-RESEARCH:
I tried to modify the TEMPLATE - but I can't find the correct twig file. It points to parent() - which is pointing to admin bundle!
To validation File/Reference - I created my own ImageProvider(as per doc) - It works(validate) good.
I tried to remove Category field(check image) - but failed.
My code is:
class ImageProvider extends BaseProvider{...}
public function buildCreateForm(FormMapper $formMapper) {
// This works - changed File/Reference to ok
$formMapper->add('binaryContent', 'file', array('label' => 'ok',
'constraints' => array(
new NotBlank(),
),
));
// This works - added a new text field
$formMapper->add('context', 'text', ['attr' => ['class' => 'fz_rocks']]);
// This not working - also ->add('category') - has no effect even when attr=hide and so on..
$formMapper->remove('category');
}
-
To remove category field from media popup
You need to override media admin's class by overriding class parameter sonata.media.admin.media.class
Create you own admin class and extend it with sonata media's base admin class.
Override configureFormFields() method by defining in your admin class
Remove category field from $formMapper
Override Sonata media class
parameters:
sonata.media.admin.media.class: Your\MediaBundle\Admin\ORM\MediaAdmin
Media Admin Class
namespace Your\MediaBundle\Admin\ORM;
use Sonata\MediaBundle\Admin\ORM\MediaAdmin as Admin;
// .. Other use statements
class MediaAdmin extends Admin {
/**
* {#inheritdoc}
*/
protected function configureFormFields( FormMapper $formMapper ) {
$media = $this->getSubject();
if ( ! $media ) {
$media = $this->getNewInstance();
}
if ( ! $media || ! $media->getProviderName() ) {
return;
}
$formMapper->add( 'providerName', 'hidden' );
$formMapper->getFormBuilder()->addModelTransformer( new ProviderDataTransformer( $this->pool, $this->getClass() ), true );
$provider = $this->pool->getProvider( $media->getProviderName() );
if ( $media->getId() ) {
$provider->buildEditForm( $formMapper );
} else {
$provider->buildCreateForm( $formMapper );
}
}
}
I solved by hiding the categoy field. If i removed completely it cause problem sometimes. Safe is to hide.
To achieve this i use custom providers, as per sonata-media doc creating_a_provider_class.rst
namespace Application\Sonata\MediaBundle\Provider;
class ImageProvider extends BaseProvider {
public function buildCreateForm(FormMapper $formMapper) {
$formMapper->add('binaryContent', 'file', ['label' => 'Upload a new file', 'constraints' => [new NotBlank(), new NotNull()]])->end();
$formMapper->with('General', ['class' => 'hidden'])->add('category');
}
public function buildEditForm(FormMapper $formMapper) {
parent::buildEditForm($formMapper);
$formMapper->add('binaryContent', 'file', ['label' => 'Upload a new file', 'required' => FALSE])->end();
$formMapper->with('General', ['class' => 'hidden'])->add('category');
}
}
Trying to validate a choice field (multiple checkboxes) Im having this problem:
"Notice: Array to string conversion "
My validation file looks like this one:
Cgboard\AppBundle\Forms\UploadImageEntity:
properties:
image:
...
cgnetworks:
- Choice:
choices: [flickr, tumblr] //<--- this is giving me problems!!!
My form entity class (Im not going to save this to db for now):
class UploadImageEntity {
public $image;
public $cgnetworks;
}
And my form class:
class UploadImageForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('image', 'file')
->add('cgnetworks', 'choice', [
'choices' => $this->getCgNetworks(),
'multiple' => TRUE,
'expanded' => TRUE
]
);
}
public function getCgNetworks()
{
return [
'tumblr' => 'Tumblr',
'flickr' => 'Flickr'
];
}
}
Any idea?
perhaps you need to specify multiple in your validation
cgnetworks:
- Choice:
choices: [flickr, tumblr] //<--- this is giving me problems!!!
multiple: true
Check your Entity field getter. If you have something else instead of
public function getValue(){
return $this->value;
}
You can reach this error.
Form builder uses get and set entity methods, that's why you need to return an allowable value.
I have some form and if user isn't logged in yet i want to show some additional fields (like email, username) in this form and validate it.
Now i do it in that way:
Create new FormType:
class QuickRegisterType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email')
->add('username')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\UserBundle\Entity\User',
'validation_groups' => array(
'QuickRegister'
),
));
}
public function getName()
{
return 'quick_register';
}
}
In Controller:
$user = $this->getUser();
$formBuilder = $this->createFormBuilder($message, array(
'cascade_validation' => true
));
$formBuilder->add('body', 'textarea');
if (!$user) {
$formBuilder->add('quick_register', new QuickRegisterType(), array(
'property_path' => 'sender'
));
}
In template:
{% if form.quick_register is defined %}
{# render this fields #}
{% endif %}
And just after $form->bind($request); i get valid user in $message->getSender(); Everything is good! But i need to perform some other actions just after form validation:
if ($form->isValid()) {
$sender = $message->getSender();
if (!$sender->getId()) {
// perform some work with just registered user
// like sending email, generate passwords, so on
}
}
And this actions will be generally the same for such form from all project, so i want to optimize it.
At first i think i can create new service in DI container and just call it like
$this->container->get('my.user_service')->afterQuickRegister($sender);
But now i read about Form events in symfony2 on page http://symfony.com/doc/current/cookbook/form/dynamic_form_generation.html
I don't understand right now, can i use it for my purposes? Can i create such form subscriber in DI container? And generally, can i somehow change User just after creation? What is right form event for this? Thanks!
I implement it with FormEvents.
form.type.quick_register:
class: Acme\UserBundle\Form\Type\QuickRegisterType
arguments: [ #form.type.quick_register.subscriber ]
tags:
- { name: form.type, alias: quick_register }
And pass subscriber by form.type.quick_register.subscriber service. In subscriber i check form validation in POST_BIND event and do my stuff. Great!