Reputation: 125
I'm trying to build multi-tenancy support in Symfony, doing this I created a group_id
column in my User, on top of that, all entities also have this same column. That way users only have access to their group's data.
I've managed to cut through this whole thing like butter with data access and display, but then came the challenge of EntityTypes
for the Symfony Forms.
The question is basically, how do I make the EntityType
display only the data that that particular group has inputted. Sorting it by the group_id
that both the user and contact has. Whats the best way to maybe pass this in so the user only has access to their data?
<?php
namespace ContactBundle\Form;
use ContactBundle\Entity\Contact;
use ContactBundle\Entity\Organization;
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\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class ContactType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('first_name', TextType::Class, [
'attr' => ['class'=>'u-full-width'],
'label' => 'First Name',])
->add('last_name', TextType::Class, [
'attr' => ['class'=>'u-full-width'],
'label'=>'Last Name',
])
->add('email', TextType::Class, [
'attr' => ['class'=>'u-full-width'],
'label'=>'Email Address'
])
->add('organization_id', EntityType::Class, [
'attr' => ['class'=>'u-full-width'],
'required' => false,
'class'=>'ContactBundle:Organization',
'choice_label'=>'name',
'choice_value'=>'id',
'label'=>'Organization'
])
->add('phone', TextType::Class, [
'attr' => ['class'=>'u-full-width'],
'label'=>'Phone',
])
->add('role', TextType::Class, [
'attr' => ['class'=>'u-full-width'],
'label'=>'Role',
])
->add('submit', SubmitType::class, [
'label'=>'Submit',
'attr' => [
'class'=>'button-primary',
'style'=>'margin-top:30px;',]]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class'=>Contact::class,]);
}
}
?>
It is worth noting that I am using FOSUserBundle
.
Upvotes: 1
Views: 3781
Reputation: 6480
You could do this in few different ways, one was to use combination of JavaScript and EntityType.
Basically just load all the entities and hide unwanted entities with JS depending on the previous list's selection. You can use data attribute to specify things needed to make that work.
Upvotes: 0
Reputation: 125
I got it!
So what I ended up doing was getting rid of the whole idea of using a EntityType on the form side. I then called the data from the controller and passed it in as an option. Like so:
$contactManager = $this->get('contact.contact_manager');
$contact = new Contact($contactManager->nextId($group = $this->getUser()->getGroupId()), $group);
$form = $this->createForm(ContactType::class, $contact, ['organizations' => $this->get('contact.organization_manager')->findDataCollection($this->getUser()->getGroupId())]);
$form->handleRequest($request);
Turned my EntityType into a choice type and passed the array of organizations into the 'choices' field as an option. Kept everything else the same like so:
->add('organization_id', ChoiceType::Class, [
'attr' => ['class'=>'u-full-width'],
'required' => false,
'choices'=> $options['organizations'],
'choice_label'=>'name',
'choice_value'=>'id',
'label'=>'Organization'
])
Then, of course, set it within the options so it can expect a variable.
$resolver->setDefaults(['data_class'=>Contact::class, 'organizations' => null]);
I appreciate all of the help and ideas!! :)
Upvotes: 0
Reputation: 2541
In Symfony it's very easy to inject whatever you need where you need it.
// ...
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class ContactType extends AbstractType
{
private $user;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->user = $tokenStorage->getToken()->getUser();
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// ...
->add('organization_id', EntityType::Class, [
'attr' => ['class'=>'u-full-width'],
'required' => false,
'class'=>'ContactBundle:Organization',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->where('u.group', '?0')
->setParameters([$this->user->getGroup()]);
},
'choice_label'=>'name',
'choice_value'=>'id',
'label'=>'Organization'
])
// ...
If you are not using autowire and autoconfigure (Symfony 3.3+), register your form as a service manually and tag it with form.type:
# config/services.yaml
services:
AppBundle\Form\ContactType:
arguments: ['@security.token_storage']
tags: [form.type]
Related reads
https://symfony.com/doc/current/form/form_dependencies.html#define-your-form-as-a-service
Upvotes: 5
Reputation: 2891
In controller when creating form
$form = $this->createForm(ContactType::class, $contact, ['group_id' => $groupId]);
In form configureOptions method
$resolver->setDefaults(['data_class'=>Contact::class, 'group_id' => null]);
In form buildForm you can get group_id by
$options['group_id']
Upvotes: 0