I had a lot of stuggle with this, but I think I came out with a practicable workaround. Hope this saved someone time in the future.
My case is: I have multiple Company
entities with 1
to n
ContactPerson
entities. When I create a new Job
, I have to select the Company
and then I want to filter the list of ContactPerson
s by the selected company. As you can see, its working. Depending on the selected Company
the matching list if ContactPerson
s is loaded:

So get this working, I just disabled the mapping
flag for Customer
and ContactPerson
to map the data by myself in postPersist
and postUpdate
in my admin class (extends BaseAdmin
).
Then I use a sonata_type_choice_field_mask
for all my Company
entities. I build a data-array by myself. After that, I add a prefiltered (by passing a matching filter) ContactPerson
dropdown using the type sonata_type_model
.
This is working very well - but of course, it will fail if you have thousands of companies and customers. In my case this will not happed so this is a practicable solution.
Code (removed unnecessary stuff):
<?php
namespace AppBundle\Admin;
use AppBundle\Entity\Customer;
use AppBundle\Entity\Job;
use AppBundle\Form\Types;
use AppBundle\Repository\CustomerRepository;
use AppBundle\Service\EntityManager;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Route\RouteCollection;
use Sonata\AdminBundle\Show\ShowMapper;
class JobAdmin extends BaseAdmin
{
const CONTACT_PERSON_DROPDOWN_PREFIX = 'contactPersons';
/**
* @param Job $job
* @return null|string
*/
protected function getPreselectedCustomerId(Job $job)
{
if ($job) {
$customer = $job->getCustomer();
if ($customer) {
$customerId = $customer->getId();
return $customerId;
}
}
return null;
}
/**
* @return \Doctrine\ORM\EntityRepository
*/
protected function getEnabledCustomers()
{
$customerRepository = $this->getCustomerRepository();
$customers = $customerRepository->findByEnabled();
return $customers;
}
/**
* @param $enabledCustomers
* @return array
*/
protected function getFilterMap($enabledCustomers)
{
$filterMap = [];
/**
* @var $customer Customer
*/
foreach ($enabledCustomers as $customer) {
$customerId = $customer->getId();
$fieldName = self::CONTACT_PERSON_DROPDOWN_PREFIX . $customerId;
$filterMap[$customerId] = [$fieldName];
}
return $filterMap;
}
/**
* @param $enabledCustomers
* @return array
*/
protected function getCustomerDropDown($enabledCustomers)
{
$customerDropDown = [];
/**
* @var $customer Customer
*/
foreach ($enabledCustomers as $customer) {
$customerId = $customer->getId();
$name = $customer->getName();
$customerDropDown[$name] = $customerId;
}
return $customerDropDown;
}
/**
* @param Customer $customer
* @return \Doctrine\ORM\QueryBuilder
*/
protected function getFilterQueryForCustomer(Customer $customer)
{
$entityManager = $this->getEntityManager();
$contactPersonQuery = $entityManager->createQueryBuilder('cp')
->select('cp')
->from('AppBundle:ContactPerson', 'cp')
->where('cp.customer = :customer')
->setParameter('customer', $customer)
->orderBy('cp.name', 'ASC')
;
return $contactPersonQuery;
}
/**
* @param FormMapper $formMapper
*/
protected function configureFormFields(FormMapper $formMapper)
{
$job = $this->getSubject();
$enabledCustomers = $this->getEnabledCustomers();
$filterMap = $this->getFilterMap($enabledCustomers);
$customerDropDown = $this->getCustomerDropDown($enabledCustomers);
$preselectedId = $this->getPreselectedCustomerId($job);
$formMapper
->tab('BaseData')
->with('JobData')
->add('customer', 'sonata_type_choice_field_mask', array(
'choices' => $customerDropDown,
'data' => $preselectedId,
'map' => $filterMap,
'by_reference' => false,
'required' => true,
'mapped' => false,
)
)
->end()
->end()
;
/**
* @var $customer Customer
*/
foreach ($enabledCustomers as $customer) {
$customerId = $customer->getId();
$fieldName = self::CONTACT_PERSON_DROPDOWN_PREFIX . $customerId;
$filterQuery = $this->getFilterQueryForCustomer($customer);
$formMapper
->tab('BaseData')
->with('JobData')
->add($fieldName, 'sonata_type_model', array(
'class' => 'AppBundle\Entity\ContactPerson',
'query' => $filterQuery,
'mapped' => false,
'label' => 'Contact Person'
))
->end()
->end()
;
}
$formMapper
->tab('BaseData')
->with('JobData')
->add('name')
// Add more fields
->end()
->end()
;
}
/**
* @param EntityManager $entityManager
* @return \Doctrine\ORM\EntityRepository
*/
protected function getCustomerRepository(EntityManager $entityManager = null)
{
if (!$entityManager) {
$entityManager = $this->getEntityManager();
}
$customerRepository = $entityManager->getRepository(Customer::class);
return $customerRepository;
}
/**
* @return EntityManager
*/
protected function getEntityManager()
{
$entityManager = $this->getConfigurationPool()->getContainer()->get('doctrine')->getManager();
return $entityManager;
}
/**
* @param Job $job
*/
protected function mapUnmappedFields (Job $job)
{
$customerId = $this->getForm()->get('customer')->getData();
if ($customerId)
{
$entityManager = $this->getEntityManager();
$contactPerson = $this->getForm()->get(self::CONTACT_PERSON_DROPDOWN_PREFIX.$customerId)->getData();
$customerRepository = $this->getCustomerRepository($entityManager);
$customer = $customerRepository->findById($customerId);
if ($contactPerson) {
$job->setContactPerson($contactPerson);
}
if ($customer) {
$job->setCustomer($customer);
}
$entityManager->persist($job);
$entityManager->flush($job);
}
}
/**
* @param mixed $object
*/
public function postPersist($object)
{
$this->mapUnmappedFields($object);
parent::postPersist($object);
}
/**
* @param mixed $object
*/
public function postUpdate($object)
{
$this->mapUnmappedFields($object);
parent::postUpdate($object);
}
}