Symfony2 + SonataAdminBundle: Error on updating User's roles (many-to-many) - symfony

Entities:
User:
class User implements AdvancedUserInterface, \Serializable
{
...
/**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="users")
* #ORM\JoinTable(name="users_roles")
*
*/
private $roles;
...
}
Role:
class Role implements RoleInterface
{
...
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="roles")
*/
private $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
public function __toString()
{
return $this->getName();
}
...
}
Admin classes:
UsersAdmin:
<?php
namespace Lan\ConsoleBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Knp\Menu\ItemInterface as MenuItemInterface;
class UsersAdmin extends Admin
{
protected function configureShowField(ShowMapper $showMapper)
{
$showMapper
->add('id', null, array('label' => 'ID'))
->add('username', null, array('label' => 'Name'))
->add('password', null, array('label' => 'Password'))
->add('email', null, array('label' => 'Mail'))
->add('is_active', null, array('label' => 'Active', 'required' => false))
->add('roles');
}
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->with('General')
->add('username', null, array('label' => 'Name'))
->add('password', null, array('label' => 'Password'))
->add('email', null, array('label' => 'Mail'))
->add('is_active', 'checkbox', array('label' => 'Active', 'required' => false))
->end()
->with('Roles')
->add('roles', 'sonata_type_model',array('expanded' => true, 'compound' => true, 'multiple' => true))
->end();
}
}
RolesAdmin:
<?php
namespace Lan\ConsoleBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Knp\Menu\ItemInterface as MenuItemInterface;
class RolesAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name', null, array('label' => 'Заголовок'))
->add('role', null, array('label' => 'Роль'));
}
}
Screenshot:
http://img577.imageshack.us/img577/3565/jyte.png
After updating User i get this error message:
FatalErrorException: Error: Call to a member function add() on a
non-object in
\vendor\sonata-project\doctrine-orm-admin-bundle\Sonata\DoctrineORMAdminBundle\Model\ModelManager.php line 560
I think this error occurs because the object instead of 'Role' value is passed to the function Role-> __toString (). How can I solve this problem?

Try something arround this code:
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name', null, array('label' => 'Заголовок'))
->add('Product' , 'entity' , array(
'class' => 'LanConsoleBundle:Role' ,
'property' => 'name' ,
'expanded' => true ,
'multiple' => true , ))
}

Related

SonataAdmin how to set default value for ModelAutocompleteType

for example: I have an article belongs to category,
class ArticleAdmin extends AbstractAdmin{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('category', ModelAutocompleteType::class, [
'property' => 'title'
])
;
}
}
It is using ModelAutocompleteType form type, But it is unable to set default value by data option.
The default-value in a symfony form is provided by the empty_data option.
More information about the option can be found in the documentation chapter:
How to Configure empty Data for a Form Class
$formMapper
->add('category', ModelAutocompleteType::class, [
'property' => 'title',
'class' => Category::class
'empty_data' => function() {
$category = new Category();
$category->setTitle('This is a title');
return $blogPost;
}),
])
... or with a repository ...
/** #var CategoryRepository */
protected $repository;
public function __construct(CategoryRepository $repository)
{
$this->repository = $repository;
}
protected function configureFormFields(FormMapper $formMapper)
{
$repository = $this->repository;
$formMapper
->add('category', ModelAutocompleteType::class, [
'property' => 'title',
'class' => Category::class
'empty_data' => function() use ($repository) {
return $repository->findOneById(1);
}),
])
;
}

Custom Variables in custom FormType

Got some question about passing custom variables to a custom FormType
I have a custom FormType named KontoType:
I pass some custom variables to it and this works like as expected, if i override the method buildForm and dump the passed $options array, the mandant exists and is a entity.
But how the heck can i now pass this custom variable to the function getChoices() which loads the choices based on the mandant in this custom FormType?
Even if i did reset the $options in the override buildForm function like $options['choices'] = $this->getChoices($options['mandant']) the select box is empty if i render this form.
<?php
namespace App\Form\Type;
use App\Entity\Core\Finanzen\Konto;
use App\Entity\Core\Organisation\Mandant;
use App\Services\LocaleService;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Translation\TranslatorInterface;
class KontoType extends AbstractType
{
/**
* #var ObjectManager
*/
private $manager;
/**
* #var TranslatorInterface
*/
private $translator;
/**
* #var LocaleService
*/
private $localeService;
public function __construct(ObjectManager $manager, TranslatorInterface $translator, LocaleService $localeService)
{
$this->manager = $manager;
$this->translator = $translator;
$this->localeService = $localeService;
}
private function getChoices(Mandant $mandant=null)
{
return $this->manager->getRepository(Konto::class)->findBuchbar(true, $mandant);
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'label' => 'konto.name',
'help' => 'konto.help',
'choices' => null,
'attr' => array(
'class' => 'bs-select',
'aria-hidden' => 'true',
'ref' => 'input',
'multiple' => false,
'tabindex' => 1,
'data-live-search' => true,
'data-size' => 6
),
'choice_label' => function ($choiceValue, $key, $value) {
return $choiceValue->getKonto()." ".$this->localeService->doTranslate($choiceValue);
},
'choice_value' => function(Konto $konto = null) {
return $konto ? $konto->getId() : '' ;
},
'required' => true,
'multiple' => false,
'empty_data' => null,
'label_attr' => array(
'class' => 'control-label'
),
'placeholder' => 'message.bitte wählen',
'mandant' => null
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$options['choices'] = $this->getChoices($options['mandant']);
parent::buildForm($builder, $options); // TODO: Change the autogenerated stub
}
public function getParent() {
return ChoiceType::class;
}
}

How to create a form to edit user roles with FriendsOfSymfony UserBundle

I'm trying to create a controller where I can edit the roles of a user (just that, nothing else) and I'm king of stuck.
I've created a form type:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'roles', 'choice', [
'choices' => ['ROLE_ADMIN', 'ROLE_USER', 'ROLE_CUSTOMER'],
'expanded' => true,
'multiple' => true,
]
)
->add('send', 'submit');
}
First, what would be the best way to retrieve the roles? Is there any way to associate a label to them?
In the controller I have this:
/**
* User role edition
*
* #Route(
* path="/edit-roles",
* name = "backoffice_user_edit_roles",
* requirements = {
* "id_user" = "\d*",
* },
* methods = {"GET"}
* )
*
* #Security("has_role('ROLE_ADMIN')")
*
* #Template
*/
public function editRolesAction($id_user)
{
$user = $this->user_repository->findOneById($id_user);
$form = $this->form_factory->create('dirital_user_roles_form_type', $user);
return [
'form' => $form->createView(),
'user' => $user
];
}
Problems that I have:
The form doesn't get populate with the current user roles, how should I do that?
When receiving the form, how can I update the user?
Thanks a lot
Actually it was easier than I thought – this is the form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'roles', 'choice', [
'choices' => ['ROLE_ADMIN' => 'ROLE_ADMIN', 'ROLE_USER' => 'ROLE_USER', 'ROLE_CUSTOMER' => 'ROLE_CUSTOMER'],
'expanded' => true,
'multiple' => true,
]
)
->add('save', 'submit', ['label' => 'ui.button.save']);
}
And the controller:
public function editRolesAction(Request $request, $id_user)
{
$user = $this->user_repository->findOneById($id_user);
$form = $this->form_factory->create('dirital_user_roles_form_type', $user);
$form->handleRequest($request);
if($form->isValid())
{
$this->addFlash('success', 'section.backoffice.users.edit_roles.confirmation');
$this->em->persist($user);
$this->em->flush();
$this->redirectToRoute('backoffice_user_edit_roles', ['id_user' => $user->getId()]);
}
return [
'form' => $form->createView(),
'user' => $user
];
}
The only part that remains to do is grabbing the form choices from the config instead of hardcoding them.
To rehuse easily in other controllers or actions, I like more this approach:
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\CallbackTransformer;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add(
'roles', 'choice', [
'choices' => ['ROLE_ADMIN' => 'ROLE_ADMIN', 'ROLE_USER' => 'ROLE_USER', 'ROLE_CUSTOMER' => 'ROLE_CUSTOMER'],
'expanded' => true,
'multiple' => true,
]
)
->add('save', 'submit', ['label' => 'ui.button.save']);
;
// Data transformer
$builder->get('roles')
->addModelTransformer(new CallbackTransformer(
function ($rolesArray) {
// transform the array to a string
return count($rolesArray)? $rolesArray[0]: null;
},
function ($rolesString) {
// transform the string back to an array
return [$rolesString];
}
));
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
without touching the controller.
I added the whole file to see the clases you need to import

Add roles field to FOSUserBundle group form

When activating group( base on its documentation) in FOSUserBundle, the group roles are not embedded on edit and update form!I already override GroupFormType and GroupController but I can't pass roles from controller to form class.
my question is how can I add roles to form to let administrator change or assign role to groups?
Solving my problem by adding the role field to override GroupController
public function editAction(Request $request, $groupName)
{
...
/** #var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
$formFactory = $this->get('fos_user.group.form.factory');
$form = $formFactory->createForm();
$form->add('roles', 'choice', array(
'choices' => $this->getExistingRoles(),
'data' => $group->getRoles(),
'label' => 'Roles',
'expanded' => true,
'multiple' => true,
'mapped' => true,
));
...
}
public function getExistingRoles()
{
$roleHierarchy = $this->container->getParameter('security.role_hierarchy.roles');
$roles = array_keys($roleHierarchy);
foreach ($roles as $role) {
$theRoles[$role] = $role;
}
return $theRoles;
}
For my part I decided to inerite the GroupFormType class. Here is inherited class :
namespace UserBundle\Form;
use FOS\UserBundle\Form\Type\GroupFormType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
class GroupType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$permissions = array(
'Utilisateur' => 'ROLE_USER',
'Administrateur' => 'ROLE_ADMIN'
);
$builder
->add('name', null, array('label' => 'form.group_name', 'translation_domain' => 'FOSUserBundle'))
->add('role', ChoiceType::class, array(
'label' => 'Rôle',
'choices' => $permissions,
'multiple' => true,
'expanded' => true
))
;
}
public function getParent()
{
return GroupFormType::class;
}
}
Don't forget to precise your new class in the config.yml
fos_user:
group:
form:
type: UserBundle\Form\GroupType

Symfony2: Class not found error

Im getting a Class not found error on this line:
new \Placas\FrontendBundle\Form\Type\ChoiceOrTextType(),
I have the class at Placas/FrontendBundle/Form/Type.
Here are the codes, the instantation:
namespace Placas\FrontendBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Route\RouteCollection;
use Placas\FrontendBundle\Form\Type\ChoiceOrTextType;
class CategoriaAdmin extends Admin
{
public $last_position = 0;
private $container;
private $positionService;
public function setContainer(\Symfony\Component\DependencyInjection\ContainerInterface $container)
{
$this->container = $container;
}
public function setPositionService(\Pix\SortableBehaviorBundle\Services\PositionHandler $positionHandler)
{
$this->positionService = $positionHandler;
}
protected $datagridValues = array(
'_page' => 1,
'_sort_order' => 'ASC',
'_sort_by' => 'position',
);
protected function configureFormFields(FormMapper $formMapper)
{
$options = array('required' => false, 'label' => 'Imagen');
if ($this->getSubject()->getImageName()) {
$path = $this->getSubject()->getImageName();
$options['help'] = '<img style="width: 300px; border: 1px solid #ccc" src="/images/' . $path . '" />';
}
$formMapper
->add('nombre', 'text', array('label' => 'Nombre', 'required' => false ))
//->add('image', 'file', $options)
->add('activado', 'checkbox', array('label' => 'Activado', 'required' => false ))
->add('menu', new ChoiceOrTextType(), array(
'choices' => array('Option 1' => 'Option 1', 'Option 2' => 'Option 2'),
'required' => false,))
;
}
// Fields to be shown on lists
protected function configureListFields(ListMapper $listMapper)
{
$this->last_position = $this->positionService->getLastPosition($this->getRoot()->getClass());
$listMapper
->addIdentifier('nombre')
->add('activado')
->add('position', array(), array('label' => 'Posición'))
->add('_action', 'actions', array(
'label' => 'Cambio orden',
'actions' => array(
'move' => array('template' => 'PixSortableBehaviorBundle:Default:_sort.html.twig'),
)));
}
protected function configureRoutes(RouteCollection $collection)
{
$collection->add('move', $this->getRouterIdParameter() . '/move/{position}');
}
}
The class:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Placas\FrontendBundle\DataTransformer\ValueToChoiceOrTextTransformer;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
class ChoiceOrTextType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('choice', 'choice', array(
'choices' => $options['choices'] + array('Other' => 'Other'),
'required' => false,
))
->add('text', 'text', array(
'required' => false,
))
->addModelTransformer(new ValueToChoiceOrTextTransformer($options['choices']))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setRequired(array('choices'));
$resolver->setAllowedTypes(array('choices' => 'array'));
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
}
public function finishView(FormView $view, FormInterface $form, array $options)
{
}
public function getParent()
{
return 'form';
}
public function getName()
{
return 'tags';
}
}
$ pwd
/home/tirengarfio/workspace/mareva/src/Placas/FrontendBundle/Form/Type
$ ls
ChoiceOrTextType.php
Im just trying to follow the answer here.
Your ChoiceOrTextType is missing the namespace statement:
<?php
namespace Placas\FrontendBundle\Form\Type;
class ChoiceOrTextType
{
....
}

Resources