Skip to content

Instantly share code, notes, and snippets.

@jaytaph
Last active October 3, 2019 15:17
Show Gist options
  • Save jaytaph/9640066 to your computer and use it in GitHub Desktop.
Save jaytaph/9640066 to your computer and use it in GitHub Desktop.
<?php
namespace NoxLogic\DemoBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Account
*
* @ORM\Table()
* @ORM\Entity()
*/
class Account
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* @ORM\ManyToOne(targetEntity="City")
*/
protected $city;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
* @return Account
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set city
*
* @param \NoxLogic\DemoBundle\Entity\City $city
* @return Account
*/
public function setCity(\NoxLogic\DemoBundle\Entity\City $city = null)
{
$this->city = $city;
return $this;
}
/**
* Get city
*
* @return \NoxLogic\DemoBundle\Entity\City
*/
public function getCity()
{
return $this->city;
}
}
<?php
namespace NoxLogic\DemoBundle\Form\Type;
use Doctrine\ORM\EntityManager;
use NoxLogic\DemoBundle\Entity\Province;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class AccountType extends AbstractType {
protected $em;
function __construct(EntityManager $em)
{
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Name of the user
$builder->add('name', 'text');
/* Add additional fields... */
$builder->add('save', 'submit');
// Add listeners
$builder->addEventListener(FormEvents::PRE_SET_DATA, array($this, 'onPreSetData'));
$builder->addEventListener(FormEvents::PRE_SUBMIT, array($this, 'onPreSubmit'));
}
protected function addElements(FormInterface $form, Province $province = null) {
// Remove the submit button, we will place this at the end of the form later
$submit = $form->get('save');
$form->remove('save');
// Add the province element
$form->add('province', 'entity', array(
'data' => $province,
'empty_value' => '-- Choose --',
'class' => 'NoxLogicDemoBundle:Province',
'mapped' => false)
);
// Cities are empty, unless we actually supplied a province
$cities = array();
if ($province) {
// Fetch the cities from specified province
$repo = $this->em->getRepository('NoxLogicDemoBundle:City');
$cities = $repo->findByProvince($province, array('name' => 'asc'));
}
// Add the city element
$form->add('city', 'entity', array(
'empty_value' => '-- Select a province first --',
'class' => 'NoxLogicDemoBundle:City',
'choices' => $cities,
));
// Add submit button again, this time, it's back at the end of the form
$form->add($submit);
}
function onPreSubmit(FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
// Note that the data is not yet hydrated into the entity.
$province = $this->em->getRepository('NoxLogicDemoBundle:Province')->find($data['province']);
$this->addElements($form, $province);
}
function onPreSetData(FormEvent $event) {
$account = $event->getData();
$form = $event->getForm();
// We might have an empty account (when we insert a new account, for instance)
$province = $account->getCity() ? $account->getCity()->getProvince() : null;
$this->addElements($form, $province);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'NoxLogic\DemoBundle\Entity\Account'
));
}
public function getName()
{
return "account_type";
}
}
<?php
namespace NoxLogic\DemoBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* City
*
* @ORM\Table()
* @ORM\Entity()
*/
class City
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* @ORM\ManyToOne(targetEntity="Province", inversedBy="cities")
*/
protected $province;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
* @return City
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set province
*
* @param \NoxLogic\DemoBundle\Entity\Province $province
* @return City
*/
public function setProvince(\NoxLogic\DemoBundle\Entity\Province $province = null)
{
$this->province = $province;
return $this;
}
/**
* Get province
*
* @return \NoxLogic\DemoBundle\Entity\Province
*/
public function getProvince()
{
return $this->province;
}
function __toString() {
return $this->getName();
}
}
<?php
namespace NoxLogic\DemoBundle\Controller;
use NoxLogic\DemoBundle\Entity\Account;
use NoxLogic\DemoBundle\Form\Type\AccountType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class DefaultController extends Controller
{
public function ajaxAction(Request $request) {
if (! $request->isXmlHttpRequest()) {
throw new NotFoundHttpException();
}
// Get the province ID
$id = $request->query->get('province_id');
$result = array();
// Return a list of cities, based on the selected province
$repo = $this->getDoctrine()->getManager()->getRepository('NoxLogicDemoBundle:City');
$cities = $repo->findByProvince($id, array('name' => 'asc'));
foreach ($cities as $city) {
$result[$city->getName()] = $city->getId();
}
return new JsonResponse($result);
}
public function createAction(Request $request)
{
$account = new Account();
// You probably want to use a service and inject it automatically. For simplicity,
// I'm just adding it to the constructor.
$form = $this->createForm(new AccountType($this->getDoctrine()->getManager()), $account);
$form->handleRequest($request);
if ($form->isValid()) {
/* Do your stuff here */
$this->getDoctrine()->getManager()->persist($account);
$this->getDoctrine()->getManager()->flush();
}
return $this->render('NoxLogicDemoBundle:Default:index.html.twig', array('form' => $form->createView()));
}
}
{# Display the form #}
{{ form(form) }}
{# Add ajax thingie that will update the city select box #}
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#account_type_province').change(function(){
var val = $(this).val();
$.ajax({
type: "POST",
url: "{{ url('province_ajax_call') }}?province_id=" + val,
success: function(data) {
// Remove current options
$('#account_type_city').html('');
$.each(data, function(k, v) {
$('#account_type_city').append('<option value="' + v + '">' + k + '</option>');
});
}
});
return false;
});
});
</script>
<?php
namespace NoxLogic\DemoBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Province
*
* @ORM\Table()
* @ORM\Entity()
*/
class Province
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* @ORM\OneToMany(targetEntity="City", mappedBy="province")
*/
protected $cities;
/**
* Constructor
*/
public function __construct()
{
$this->cities = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
* @return Province
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Add cities
*
* @param \NoxLogic\DemoBundle\Entity\City $cities
* @return Province
*/
public function addCity(\NoxLogic\DemoBundle\Entity\City $cities)
{
$this->cities[] = $cities;
return $this;
}
/**
* Remove cities
*
* @param \NoxLogic\DemoBundle\Entity\City $cities
*/
public function removeCity(\NoxLogic\DemoBundle\Entity\City $cities)
{
$this->cities->removeElement($cities);
}
/**
* Get cities
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getCities()
{
return $this->cities;
}
function __toString() {
return $this->getName();
}
}
Copy link

ghost commented Apr 24, 2014

First of all thank you and props on your work. It's very clear and neat to understand, and implement.

Secondly, I have a case when I have to show a user an undefined "depth" of categories, where Category has self referencing relationships of parent - children. Do you have any idea or what could be done in order to make a "pick" a category form?

Case : Select Categories of Level 1 -> Add Level 2 Categories -> Select Level 2 Categories -> Add Level 3 Categories and so on.

Of course, a new level is added if the selected category has children.

@carlosequiz
Copy link

I have spent much time trying to find a clean solution to implement this.
Now everything is clearer.
Thank you so much!!!

@smaug1985
Copy link

Great work! You saved my day :)

@esteemed
Copy link

esteemed commented Nov 9, 2014

Can you show the findByProvince function you're using?

Figured it out, this is the findByProvince function I'm using and it works! Thanks for the tutorial!

Hope this helps others, the findByProvince function is used in the AccountType file.

public function findByProvince($province)
{
return $this
->createQueryBuilder('city')
->join('city.province', 'province')
->andWhere('province.id = :province')
->setParameter('province', $province)
->getQuery()
->getResult();
}

@massil31
Copy link

Awasome solution!!!!!!!!!
Thank you for your solution, It's help me a lot!!!!!!!!

@captainwass
Copy link

Bonjour,
en fait ce formulaire fonctionne a merveille.
mais je trouve un problème lorsque je veux l'imbriquer dans un autre formulaire.
le probleme est dans cette ligne $province = $account->getCity() ? $account->getCity()->getProvince() : null; de accountType.

l'erreur est: Error: Call to a member function getCity() on a non-object

pouvez vous m'aider?

@mtchuenten
Copy link

Awesome. Thanks a million.

@MathLG
Copy link

MathLG commented Oct 20, 2016

Hello,

I'm trying to use this solution but since few days I have a persitent error:

Catchable Fatal Error: Argument 1 passed to QuizBundle\Form\AssignType::__construct() must be an instance of Doctrine\ORM\EntityManager, none given, called in D:\qcm\Dev\qcm\vendor\symfony\symfony\src\Symfony\Component\Form\FormRegistry.php on line 85 and defined

And on every forums I went they use the "__construct" function in a controller with:
$this->getDoctrine()->getEntityManager();

So I don't know what to do to fix this error after trying many things.

Can someone help me?

Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment