Sonata Admin Bundle: Refresh multiple sonata_model_type inputs of the same entity type?

1.4k views Asked by At

I have two entities Location and Person. Person has four many to one relationships to the Location entity.

A Person has one of each of the following locations:

  • Home Town
  • List item
  • Current Town
  • Departure Arrival

Like this:

class Person {

/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="notes", type="string", length=255, nullable=false)
 */
private $name;

/**
 * @ORM\ManyToOne(targetEntity="Location", inversedBy="locationHomeTowns")
 * @ORM\JoinColumn(name="home_town_location_id", referencedColumnName="id", onDelete="SET NULL")
 */
protected $locationHomeTown;

/**
 * @ORM\ManyToOne(targetEntity="Location", inversedBy="locationCurrentTowns")
 * @ORM\JoinColumn(name="current_town_location_id", referencedColumnName="id", onDelete="SET NULL")
 */
protected $locationCurrentTown;

/**
 * @ORM\ManyToOne(targetEntity="Location", inversedBy="locationDepartures")
 * @ORM\JoinColumn(name="departure_location_id", referencedColumnName="id", onDelete="SET NULL")
 */
protected $locationDeparture;

/**
 * @ORM\ManyToOne(targetEntity="Location", inversedBy="locationArrivals")
 * @ORM\JoinColumn(name="arrival_location_id", referencedColumnName="id", onDelete="SET NULL")
 */
protected $locationArrival;
…
}

class Location {

/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="latitude", type="decimal", scale=12, precision=18, nullable=false)
 */
private $latitude;

/**
 * @var string
 *
 * @ORM\Column(name="longitude", type="decimal", scale=12, precision=18, nullable=false)
 */
private $longitude;

/**
 * @var string
 *
 * @ORM\Column(name="notes", type="string", length=255, nullable=false)
 */
private $name;

/**
 * @ORM\OneToMany(targetEntity="Person", mappedBy="locationHomeTown", cascade={"persist"}, orphanRemoval=false)
 */
protected $locationHomeTowns;

/**
 * @ORM\OneToMany(targetEntity="Person", mappedBy="locationCurrentTown", cascade={"persist"}, orphanRemoval=false)
 */
protected $locationCurrentTowns;

/**
 * @ORM\OneToMany(targetEntity="Person", mappedBy="locationDeparture", cascade={"persist"}, orphanRemoval=false)
 */
protected $locationDepartures;

/**
 * @ORM\OneToMany(targetEntity="Person", mappedBy="locationArrival", cascade={"persist"}, orphanRemoval=false)
 */
protected $locationArrivals;
…
}

I have setup the admin classes both entities as follows:

class LocationAdmin extends Admin {
…
/**
 * @param FormMapper $formMapper
 */
protected function configureFormFields(FormMapper $formMapper) {
    $formMapper
            ->add('longitude', null, array('attr' => array(
                    'placeholder' => 'decimal degrees e.g. 51.5072',
        )))
            ->add('latitude', null, array('attr' => array(
                    'placeholder' => 'decimal degrees e.g. -0.1275',
        )))
            ->add('name')
        ;
    }
…
}

class ExileAdmin extends Admin {
…
/**
 * @param FormMapper $formMapper
 */
protected function configureFormFields(FormMapper $formMapper) {
    $formMapper
            ->add('name')
            ->add('locationHomeTown', 'sonata_type_model', array(
                'required' => false
           ))
           ->add('locationCurrentTown', 'sonata_type_model', array(
                'required' => false
           ))
           ->add('locationDeparture', 'sonata_type_model', array(
                'required' => false
           ))
           ->add('locationArrival', 'sonata_type_model', array(
                'required' => false
           ))
           ;
    }
…
}

I have set the locations as sonata_type_model in the person form to allow me to add new locations whilst editing the person entity. This works as expected for each input, however only that list of locations updates. This means I have to save the whole Person form if I need to use this new location in one of the other location fields on the form.

Is there a way to have all of the location input widgets update when I add a new location to one of the other inputs?

For example, if I currently have London, Paris and Berlin stored in the database and I add Rome to Home Town I would like the form input widgets for Current Town, Departure and Arrival to update too. If I then add Dublin to Current Town I would like Home Town, Departure and Arrival to update too, and so on for Departure and Arrival.

1

There are 1 answers

0
Ugur On BEST ANSWER

What you need to do is catch the sonata-admin-append-form-element javascript event when the new Location presisted and sync all other select element's options with the triggered one.

First override Sonata's form admin fields; this will generate the event handling javascript block for each element in your form.

I know this is not the most elegant way to use "select" selectors and filter appropriate elements in the for loop but this may guide you.

YourBundle/resources/views/Form/form_admin_fields.html.twig

{% extends 'SonataAdminBundle:Form:form_admin_fields.html.twig' %}

{% block sonata_admin_orm_many_to_one_widget %}

    {% include 'SonataDoctrineORMAdminBundle:CRUD:edit_orm_many_to_one.html.twig' %}

    <script type="text/javascript"> 

    /*The event fires twice after appending Location element, 
      so I remove the listener at first*/
    $('div[id$={{form.vars['id']}}]').off('sonata-admin-append-form-element');

    $('div[id$={{form.vars['id']}}]').on('sonata-admin-append-form-element', function(event) {
        //I loop every children of parent form
        {% for element in form.parent.children %}
            if({{element.vars['id']}}!={{form.vars['id']}}) {
                /*Copy the options from the current select 
                element with the most update values*/
                var opts = $('#{{form.vars['id']}} > option').clone();
                //console.log($('select[id$={{element.vars["id"]}}]'));
                /* I add the most updated options 
                to other select elements.
                */
                $('select[id$={{element.vars["id"]}}]').empty();
                $('select[id$={{element.vars["id"]}}]').append(opts);


            }
        {% endfor %}

    });

    </script>

{% endblock %}

Then override your ExileAdmin getFormTheme method;

  public function getFormTheme()
  {
      return array_merge(
          parent::getFormTheme(),
          array('YourBundle:Form:form_admin_fields.html.twig')
      );
  }

I guess that's all now when you persist a Location all select elements are synced with the most updated one.