-
-
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(); | |
} | |
} |
I have spent much time trying to find a clean solution to implement this.
Now everything is clearer.
Thank you so much!!!
Great work! You saved my day :)
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();
}
Awasome solution!!!!!!!!!
Thank you for your solution, It's help me a lot!!!!!!!!
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?
Awesome. Thanks a million.
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.
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.