When I was verifying the operation with Symfony4, the following error appeared.
I want to get the staff who are logged in.
current() is not found in the code and the cause cannot be identified.
Do you have any ideas?
I changed from ChoiceList to ChoiceLoader, which may have an effect.
Error
Warning: current() expects parameter 1 to be array, null given
at vendor/symfony/form/Extension/Core/DataTransformer/ChoiceToValueTransformer.php:32
at Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer->transform(object(Staff))
(vendor/symfony/form/Form.php:1111)
at Symfony\Component\Form\Form->normToView(object(Staff))
(vendor/symfony/form/Form.php:350)
at Symfony\Component\Form\Form->setData(object(Staff))
(vendor/symfony/form/Extension/Core/DataMapper/PropertyPathMapper.php:49)
at Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper->mapDataToForms(object(Coordinate), object(RecursiveIteratorIterator))
(vendor/symfony/form/Form.php:383)
at Symfony\Component\Form\Form->setData(object(Coordinate))
(vendor/symfony/form/Form.php:487)
at Symfony\Component\Form\Form->initialize()
(vendor/symfony/form/FormBuilder.php:217)
at Symfony\Component\Form\FormBuilder->getForm()
(vendor/symfony/form/FormFactory.php:30)
at Symfony\Component\Form\FormFactory->create('Ahi\\Sp\\AdminBundle\\Form\\Type\\Article\\CoordinateType', object(Coordinate), array('method' => 'POST', 'action' => '/admin/sp/shop/coordinate/3860', 'login_staff' => object(Staff)))
(vendor/symfony/framework-bundle/Controller/ControllerTrait.php:312)
at Symfony\Bundle\FrameworkBundle\Controller\Controller->createForm('Ahi\\Sp\\AdminBundle\\Form\\Type\\Article\\CoordinateType', object(Coordinate), array('method' => 'POST', 'action' => '/admin/sp/shop/coordinate/3860', 'login_staff' => object(Staff)))
(src/Ahi/Sp/AdminBundle/Controller/BaseArticleController.php:288)
at Ahi\Sp\AdminBundle\Controller\BaseArticleController->editAndUpdate(object(Request), 'coordinate', '3860')
(src/Ahi/Sp/AdminBundle/Controller/Sp/Shop/ArticleController.php:113)
at Ahi\Sp\AdminBundle\Controller\Sp\Shop\ArticleController->editAction(object(Request), 'coordinate', '3860')
(vendor/symfony/http-kernel/HttpKernel.php:149)
ArticleController.php
protected function editAndUpdate(Request $request, $articleType, $id)
{
if ($articleType == 'coordinate'){
$form = $this->createForm(CoordinateType::class, $article, array(
"method" => "POST",
"action" => $this->generateUrl($this->updateRoute, array("articleType" => $articleType, "id" => $id)),
//Error part
"login_staff" => $this->getStaff(),
));
CoordinateType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
$wearerChoiceList = new StaffChoiceLoader($this->staffService, $options['login_staff']);
$builder->add("wearer", EntityType::class, array(
"required" => false,
"class" => "AhiSpCommonBundle:Staff",
"choice_loader" => $wearerChoiceList,
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setRequired(array(
"login_staff",
));
}
StaffChoiceLoader.php
class StaffChoiceLoader implements ChoiceLoaderInterface
{
public function __construct($staffService, $loginStaff)
{
$this->staffService = $staffService;
$this->loginStaff = $loginStaff;
}
public function setCurrentStaff($currentStaff)
{
$this->currentStaff = $currentStaff;
}
public function loadChoiceList($value = null)
{
// Get the same shop staff as the login staff
$staffs = $this->staffService->getStaffByShop($this->loginStaff->getShop());
// If the current staff is not included in the acquired staff (due to transfer etc.), add it to the end
if ($this->currentStaff && !array_search($this->currentStaff, $staffs)) {
$staffs[] = $this->currentStaff;
}
return new ChoiceList($staffs, $staffs);
}
ArticleType.php
$staff = $options['login_staff'];
if ($staff->getShop() && $staff->getShop()->getApprovalFlg()) {
// For shops that use the approval function
if ($staff->isManage()) {
// Shop administrator (save draft, wait for approval, post immediately)
$builder->add("draft", SubmitType::class);
$builder->add("approvalRequest", SubmitType::class);
$builder->add("publish", SubmitType::class);
} else {
// General staff (save draft, waiting for approval)
$builder->add("draft", SubmitType::class);
$builder->add("approvalRequest", SubmitType::class);
}
} else {
// For shops that do not use the headquarters management screen or approval function (save draft, post)
$builder->add("draft", SubmitType::class);
$builder->add("publish", SubmitType::class);
}
I solved the problem by the following method.
/**
*
* #Method("GET")
* #Route("/initialSetting")
*
* #Template("#AhiSpAdminBundle/Security/initialSetting.html.twig")
*/
public function initialSettingAction(Request $request)
{
return $this->initialSettingAndUpdateAction($request);
}
/**
*
* #Method("PUT")
* #Route("/initialSetting")
*
* #Template("#AhiSpAdminBundle/Security/initialSetting.html.twig")
*/
public function updateAction(Request $request)
{
return $this->initialSettingAndUpdateAction($request);
}
public function initialSettingAndUpdateAction(Request $request)
{
$key = $request->query->get("key");
$staff = $this->get("admin.staffService")->getStaffByUrlKey($key);
if (!$staff || $staff->getPassword()) {
throw new HttpException(
400,
The specified stuff prowl initialization URL does not exist or has expired.
"\n".
"Ask the administrator to reissue the URL."
);
}
$form = $this->createForm(InitialSettingType::class, $staff, array(
"method" => "PUT",
"action" => $this->generateUrl("ahi_sp_admin_security_update", array("key" => $key)),
));
if ($request->isMethod('PUT')) {
if ($form->handleRequest($request)->isSubmitted() && $form->handleRequest($request)->isValid()) {
$this->get("admin.staffService")->clearUrlKey($staff);
$this->get("admin.staffService")->save($staff);
$this->get('session')->getFlashBag()->add('success', 'Staff profile Initial settings have been completed.');
$this->autoLogin($staff);
return $this->redirect($this->generateUrl('ahi_sp_admin_default_index'));
} else {
$this->get('session')->getFlashBag()->add('error', 'Staff profile Initial settings could not be completed. Please check the input contents.');
}
}
return array(
'form' => $form->createView(),
);
}
Related
In Symfony3.4, when I corresponded to "" deleted in symfony3 or later, the following error occurred.
I don't think I'm using the "" anymore, but it seems to be recognized by symfony.
composer dump-autoload and php bin/console cache:clear have been executed.
Is there anything wrong with it?
https://.com/symfony/symfony/blob/2.7/UPGRADE-2.7.md
Error
The option "choices" with value Symfony\Component\Form\ChoiceList\
is expected to be of type "null" or "array" or "\Traversable", but is of type
"Symfony\Component\Form\ChoiceList\".
StaffChoiceList.php->StaffChoiceLoader.php
// use Symfony\Component\Form\ChoiceList\;
// use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList;
use AppBundle\Model\Service\StaffService;
use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory;
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
// class StaffChoiceList extends
class StaffChoiceLoader implements ChoiceLoaderInterface
{
private $loginStaff;
private $currentStaff;
/**
* #var StaffService
*/
private $staffService;
public function __construct(StaffService $staffService, $loginStaff)
{
$this->staffService = $staffService;
$this->loginStaff = $loginStaff;
}
public function setCurrentStaff($currentStaff)
{
$this->currentStaff = $currentStaff;
}
// Add"$value = null"
public function ($value = null)
{
// Get the same shop staff as the login staff
$staffs = $this->staffService->getStaffByShop($this->loginStaff->getShop());
// If the current staff is not included in the acquired staff (due to transfer etc.), add it to the end
if ($this->currentStaff && !array_search($this->currentStaff, $staffs)) {
$staffs[] = $this->currentStaff;
}
return new ChoiceList($staffs, $staffs);
}
//Add
public function loadChoicesForValues(array $values, $value = null)
{
// TODO: Implement loadChoicesForValues() method.
}
//Add
public function loadValuesForChoices(array $choices, $value = null)
{
// TODO: Implement loadValuesForChoices() method.
}
}
ArticleType.php
use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory;
use AppBundle\Form\ChoiceList\StaffChoiceLoader;
abstract class ArticleType extends AbstractType
{
// $authorChoiceList = new StaffChoiceList($this->staffService, $options['login_staff']);
//Add
$factory = new DefaultChoiceListFactory();
$authorChoiceList = $factory->createListFromLoader(new StaffChoiceLoader($this->staffService, $options['login_staff']));
$builder->add("author", EntityType::class, array(
"required" => true,
"class" => "AppBundle:Staff",
"choices" => $authorChoiceList,
"placeholder" => "Please select",
));
Tried Code
1. choices -> choice_loader
The following error
The option "choice_loader" with value Symfony\Component\Form\ChoiceList\ is expected to be of type "null" or "Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface", but is of type "Symfony\Component\Form\ChoiceList\".
Change $authorChoiceList
// $factory = new DefaultChoiceListFactory();
// $authorChoiceList = $factory->createListFromLoader(new StaffChoiceLoader($this->staffService, $options['login_staff']));
$authorChoiceList = new StaffChoiceLoader($this->staffService, $options['login_staff']);
The following error
Warning: current() expects parameter 1 to be array, null given
AbstractController->createForm('AppBundle\\Form\\Type\\Article\\BrandEventType', object(BrandEvent), array('method' => 'POST', 'action' => '/app_dev.php/admin/hq/brandevent/', 'login_staff' => object(Staff)))
in src/AppBundle/Controller/BaseArticleController.php (line 186)
));
} else if ($articleType == 'brandevent'){
$form = $this->createForm(BrandEventType::class, $article, array(
"method" => "POST",
"action" => $this->generateUrl($this->createRoute,
array("articleType" => $articleType)),
//Error Code
"login_staff" => $this->getStaff(),
));
}
Problem is that nothing is loaded in the municipality field, it goes undefined. In the AJAX code I get the value of the province well. But in the class addMunicipioField.php does not take the value of the $province, it is always nul
I am trying to make a registration form where part of the usual fields (name, nick, password, ...) I also add two dependent fields Municipality and Province.
The codec Controler:
class UserController extends Controller {
private $session;
public function __construct() {
$this->session = new Session();
}
public function registerAction(Request $request) {
if (is_object($this->getUser())) {
return $this->redirect('home');
}
$user = new DbUsuario();
$form = $this->createForm(RegistreUserType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery('SELECT u FROM BackendBundle:DbUsuario u WHERE u.email = :email OR u.nick = :nick')
->setParameter('email', $form->get("email")->getData())
->setParameter('nick', $form->get("nick")->getData());
$user_isset = $query->getResult();
if (count($user_isset) == 0) {
$factory = $this->get("security.encoder_factory");
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($form->get("password")->getData(), $user->getSalt());
$user->setPassword($password);
$user->setRole("ROLE_USER");
$user->setImagen(null);
$em->persist($user);
$flush = $em->flush();
if ($flush == null) {
$status = "Te has registrado correctamente";
$this->session->getFlashBag()->add("status", $status);
return $this->redirect("login");
} else {
$status = "No te has registrado correctamente";
}
} else {
$status = "Usuario ya esta registrado.";
}
} else {
$status = "No te has registrado correctamente.";
}
$this->session->getFlashBag()->add("status", $status);
}
return $this->render('AppBundle:User:register.html.twig', array(
"form" => $form->createView() # Genera el html del formulario.
));
}
The Entity that creates the form is DbUsuario, which has the idMunicipio field.
/** #var \BackendBundle\Entity\DbMunicipios */
private $idMunicipio;
/**
* Set idMunicipio
* #param \BackendBundle\Entity\DbMunicipio $idMunicipio
* #return DbUsuario
*/
public function setIdMunicipio(\BackendBundle\Entity\DbMunicipio $idMunicipio = null) {
$this->idMunicipio = $idMunicipio;
return $this;
}
/**
* Get idMunicipio
* #return \BackendBundle\Entity\DbMunicipio
*/
public function getIdMunicipio() {
return $this->idMunicipio;
}
Then the Entity Of DbMunicipio that connects with 'province' with :
/** #var \BackendBundle\Entity\DbProvincia */
private $provincia;
/**#param \BackendBundle\Entity\DbProvincia $provincia
* #return DbMunicipio
*/
public function setProvincia(\BackendBundle\Entity\DbProvincia $provincia = null){
$this->provincia = $provincia;
return $this;
}
// And implement this function.
public function __toString(){
return $this->getMunicipio();
}
/**#return \BackendBundle\Entity\DbProvincia */
public function getProvincia(){
return $this->provincia;
}
And the Entity DbProvincia that only has the fields (id (integer), slug (String) and province (String)).
I define the form as follows:
namespace AppBundle\Form;
use ....
class RegistreUserType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$factory = $builder->getFormFactory();
$builder->add('nombre', TextType::class, array('label' => 'Nombre',
'required' => 'required',
'attr' => array('class' => 'form-nombre form-control')
));
$builder->add('apellido', TextType::class, array('label' => 'Apellido',
'required' => 'required',
'attr' => array('class' => 'form-apellido form-control')
));
$builder->add('nick', TextType::class, array('label' => 'Nick',
'required' => 'required',
'attr' => array('class' => 'form-nick form-control nick-input')
));
$provinSubscriber = new AddProvinciaField($factory);
$builder->addEventSubscriber($provinSubscriber);
$muniSubscriber = new AddMunicipioField($factory);
$builder->addEventSubscriber($muniSubscriber);
$builder->add('email', EmailType::class, array('label' => 'Correo electrónico',
'required' => 'required',
'attr' => array('class' => 'form-email form-control')
));
$builder->add('password', PasswordType::class, array('label' => 'Password',
'required' => 'required',
'attr' => array('class' => 'form-password form-control')
));
$builder->add('Registrarse', SubmitType::class, array("attr" => array("class" => "form-submit btn btn-success")));
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => 'BackendBundle\Entity\DbUsuario'
));
}
public function getBlockPrefix() { return 'backendbundle_dbusuario'; }
}
I define within the AppBundle \ Form \ eventListener \ AddProvinciaField the classes called in the form:
namespace AppBundle\Form\EventListener;
use ....
use BackendBundle\Entity\DbProvincia;
class AddProvinciaField implements EventSubscriberInterface {
private $factory;
public function __construct(FormFactoryInterface $factory) {
$this->factory = $factory;
}
public static function getSubscribedEvents() {
return array(
FormEvents::PRE_SET_DATA => 'preSetData',
FormEvents::PRE_SUBMIT => 'preSubmit'
);
}
private function addProvinciaForm($form, $provincia) {
$form -> add('provincia', EntityType::class, array(
'class' => 'BackendBundle:DbProvincia',
'label' => 'Provincia',
'placeholder' => '_ Elegir _',
'auto_initialize' => false,
'mapped' => false,
'attr'=> array('class' => 'form-provincia form-control provincia-input'),
'query_builder' => function (EntityRepository $repository) {
$qb = $repository->createQueryBuilder('provincia');
return $qb;
}
));
}
public function preSetData(FormEvent $event){
$data = $event->getData();
$form = $event->getForm();
if (null === $data) {return;}
$provincia = ($data->getIdMunicipio()) ? $data->getIdMunicipio()->getProvincia() : null ;
$this->addProvinciaForm($form, $provincia);
}
public function preSubmit(FormEvent $event) {
$data = $event->getData();
$form = $event->getForm();
if (null === $data) { return;}
$provincia = array_key_exists('provincia-input', $data) ? $data['provincia-input'] : null;
$this->addProvinciaForm($form, $provincia);
}
}
And later I define AddMunicipioField.php:
namespace AppBundle\Form\EventListener;
use ....
use BackendBundle\Entity\DbProvincia;
class AddMunicipioField implements EventSubscriberInterface {
private $factory;
public function _construct(FormFactoryInterface $factory) {
$this->factory = $factory;
}
public static function getSubscribedEvents() {
return array(
FormEvents::PRE_SET_DATA => 'preSetData',
FormEvents::PRE_SUBMIT => 'preSubmit'
);
}
private function addMunicipioForm($form, $provincia) {
$form->add('idMunicipio', EntityType::class, array(
'class' => 'BackendBundle:DbMunicipio',
'label' => 'Municipio',
'placeholder' => '_ Elegir _',
'auto_initialize' => false,
'attr'=> array('class' => 'form-municipio form-control municipio-input'),
'query_builder' => function (EntityRepository $repository) use ($provincia) {
$qb = $repository->createQueryBuilder('idMunicipio')
->innerJoin('idMunicipio.provincia', 'provincia');
if ($provincia instanceof DbProvincia) {
$qb->where('idMunicipio.provincia = :provincia')
->setParameter('provincia', $provincia);
} elseif (is_numeric($provincia)) {
$qb->where('provincia.id = :provincia')
->setParameter('provincia', $provincia);
} else {
$qb->where('provincia.provincia = :provincia')
->setParameter('provincia', null);
}
return $qb;
}
));
}
public function preSetData(FormEvent $event){
$data = $event->getData();
$form = $event->getForm();
if (null === $data) { return; }
$provincia = ($data->getIdMunicipio()) ? $data->getIdMunicipio()->getProvincia() : null ;
$this->addMunicipioForm($form, $provincia);
}
public function preSubmit(FormEvent $event){
$data = $event->getData();
$form = $event->getForm();
if (null === $data) { return; }
$provincia = array_key_exists('provincia_input', $data) ? $data['provincia_input'] : null;
$this->addMunicipioForm($form, $provincia);
}
}
And finally the AJAX request:
$(document).ready(function(){
var $form = $(this).closest('form');
$(".provincia-input").change(function(){
var data = { idMunicipio: $(this).val() };
$.ajax({
type: 'POST',
url: $form.attr('action'),
data: data,
success: function(data) {
for (var i=0, total = data.length; i < total; i++) {
$('.municipio-input').append('<option value="' + data[i].id + '">' + data[i].municipio + '</option>');
}
}
});
});
});
I added var_dump and alert() in the code. This is the way output.
In this case, the value of province is always null.
addMunicipioField.php
public function preSetData(FormEvent $event){
$data = $event->getData();
$form = $event->getForm();
if (null === $data) {
return;
}
$provincia = ($data->getIdMunicipio()) ? $data->getIdMunicipio()->getProvincia() : null ;
var_dump('presetdata');
var_dump($provincia);
$this->addMunicipioForm($form, $provincia);
}
AJAX:
$(document).ready(function(){
var $form = $(this).closest('form');
$(".provincia-input").change(function(){
alert($('.provincia-input').val()); // THIS IS CORRECT VALUE, INTEGER.
var data = { idMunicipio: $(this).val() };
$.ajax({
type: 'POST',
url: $form.attr('action'),
data: data,
success: function(data) {
alert(data);
alert(data.length); // THIS IS INCORRECT.
for (var i=0, total = data.length; i < total; i++) {
$('.municipio-input').append('<option value="' + data[i].id + '">' + data[i].municipio + '</option>');
}
}
});
});
});
Another point of view
The entities are the same.
In this case it works but I must press the send button. How could I do it without pressing the button, that it was automatic change?
The class RegistreUserType extends AbstractType I add the following lines.
$builder -> add('provincia', EntityType::class, array(
'class' => 'BackendBundle:DbProvincia',
'label' => 'Provincia',
'placeholder' => '_ Elegir _',
'auto_initialize' => false,
'mapped' => false,
'attr'=> array('class' => 'form-provincia form-control provincia-input'),
'query_builder' => function (EntityRepository $repository) {
$qb = $repository->createQueryBuilder('provincia');
return $qb;
}
));
$builder->add('idMunicipio', EntityType::class, array(
'class' => 'BackendBundle:DbMunicipio',
'label' => 'Municipio',
'placeholder' => '_ Elegir _',
'auto_initialize' => false,
'mapped' => false,
'attr'=> array('class' => 'form-municipio form-control municipio-input')
));
$builder->addEventSubscriber(new AddMunicipioField());
The new class AddMunicpioField():
class AddMunicipioField implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return array(
FormEvents::PRE_SUBMIT => 'preSubmit',
FormEvents::PRE_SET_DATA => 'preSetData',
);
}
public function preSubmit(FormEvent $event){
$data = $event->getData();
$this->addField($event->getForm(), $data['provincia']);
}
protected function addField(Form $form, $provincia){
$form->add('idMunicipio', EntityType::class, array(
'class' => 'BackendBundle:DbMunicipio',
'label' => 'Municipio',
'placeholder' => '_ Elegir _',
'auto_initialize' => false,
'mapped' => false,
'attr'=> array('class' => 'form-municipio form-control municipio-input'),
'query_builder' => function(EntityRepository $er) use ($provincia){
$qb = $er->createQueryBuilder('idMunicipio')
->where('idMunicipio.provincia = :provincia')
->setParameter('provincia', $provincia);
return $qb;
}
));
}
Codec Ajax:
$(document).ready(function () {
$('.provincia-input').change(function () {
var $form = $(this).closest('form');
var data = $('.provincia-input').serialize();
$.ajax({
url: $form.attr('action'),
type: 'POST',
data: data,
success: function (data) {
$('.municipio-input').replaceWith($(html).find('.municipio-input'));
}
});
});
});
I did not notice a field or a property called 'select_provincia' in neither your entity, nor the main form, so I will try guessing, that it probably should be called simply 'provincia', as that is the name for both the property in municipality entity and in the form subscriber for municipality. Also in AddMunicipioField.php you should change this code:
if ($provincia instanceof DbProvincia) {
$qb->where('idMunicipio.provincia = :provincia')
>setParameter('provincia', $provincia);
}
to this:
if ($provincia instanceof DbProvincia) {
$qb->where('idMunicipio.provincia = :provincia')
>setParameter('provincia', $provincia->getId());
}
since when querying you will be comparing provincia to the ID of province.
Further more, make sure you have implemented the __toString() method in the municipality entity, so that symfony would know how to convert that object to a string in order to show it in the select list.
Hope this helps :)
Seeing that you have added new information i will update my answer:
Firstly, In the AddMunicipioField.php you still have basically the same error:
the array key is going to be called the same way you name your field, in this case not 'provincia_input', but 'provincia'. You can see the data that was posted to you by calling "dump($data); die;" just before you check if the array key exists (check for a key name "provincia", as you can see the name matches what you have specified when adding the field to the form (AddProvinciaField.php):
$form -> add('provincia', EntityType::class
Another thing I have noticed in the first js snippet you have posted is that in this part of code:
$(".provincia-input").change(function(){
var data = { idMunicipio: $(this).val() };
$.ajax({
type: 'POST',
url: $form.attr('action'),
data: data,
success: function(data) {
for (var i=0, total = data.length; i < total; i++) {
$('.municipio-input').append('<option value="' + data[i].id + '">' + data[i].municipio + '</option>');
}
}
});
});
you are taking the input from $(".provincia-input") and sending it as a value for a field called "idMunicipio", which in you situation I don't think makes any sense.
Lastly, I will discus the errors that were made in the last piece of the JS you've posted:
$(document).ready(function () {
$('.provincia-input').change(function () {
var $form = $(this).closest('form');
var data = $('.provincia-input').serialize();
$.ajax({
url: $form.attr('action'),
type: 'POST',
data: data,
success: function (data) {
$('.municipio-input').replaceWith($(html).find('.municipio-input'));
}
});
});
});
First of all, class names are not supposed to be used for identifying the fields that you are using. By definition they are supposed to be used multiple time in the document and describe only style, which might lead to some unexpected behaviour as your codebase grows. Please assign proper ID values to the inputs that you are going to be querying and especially replacing so that you could identify them correctly.
Secondly, please refer to the JS code posted in the official Symfony tutorial by following this link. As you can see the proper way to post data back to the server is not by sending a lone property like you are trying to do in this line:
var data = $('.provincia-input').serialize();
but rather by sending the property as a part of the forms data. So as in the tutorial I've posted, please first create an empty data object:
var data = {};
then add the province value to it:
data[$(this).attr('name')] = $(this).val();
Thirdly, this part of code is clearly incorrect:
success: function (data) {
$('.municipio-input').replaceWith($(html).find('.municipio-input'));
}
As you can see the html variable is undefined in that part of code. This of course is because the variable that you are supposed to be using in this case is called data (the response that you have gotten from the server). So please change it to this:
success: function (data) {
$('.municipio-input').replaceWith($(data).find('.municipio-input'));
}
Lastly, if you are still learning SF and web programming, I would like to suggest taking the bottom up approach to advance your programming knowledge instead, since this case is pretty complex and issues that prevented your code from working still require deeper understanding of the technologies you are using. I would personally suggest reading up on HTML attribute usage, Symfony form handling, read up on what data is available to you during each Symfony form event and maybe try using the dumper component of symfony more to debug your code, since var_dump is really a very inefficient way to debug SF code (would have solved many problems for you).
Solved!!
In my form I added the call to the two new classes:
$builder -> addEventSubscriber(new AddMunicipioFieldSubscriber('idMunicipio'));
$builder -> addEventSubscriber(new AddProvinceFieldSubscriber('idMunicipio'));
The firth select is province, this is the class:
class AddProvinceFieldSubscriber implements EventSubscriberInterface {
private $propertyPathToMunicipio;
public function __construct($propertyPathToMunicipio) {
$this->propertyPathToMunicipio = $propertyPathToMunicipio;
}
public static function getSubscribedEvents() {
return array(
FormEvents::PRE_SET_DATA => 'preSetData',
FormEvents::PRE_SUBMIT => 'preSubmit'
);
}
private function addProvinceForm($form, $Province = null) {
$formOptions = array(
'class' => 'BackendBundle:DbProvincia',
'mapped' => false,
'label' => 'Provincia',
'attr' => array(
'class' => 'class_select_provincia',
),
);
if ($Province) {
$formOptions['data'] = $Province;
}
$form->add('provincia', EntityType::class, $formOptions);
}
public function preSetData(FormEvent $event){
$data = $event->getData();
$form = $event->getForm();
if (null === $data) {
return;
}
$accessor = PropertyAccess::createPropertyAccessor();
$municipio = $accessor->getValue($data, $this->propertyPathToMunicipio);
$provincia = ($municipio) ? $municipio->getIdMunicipio()->getProvincia() : null;
$this->addProvinceForm($form, $provincia);
}
public function preSubmit(FormEvent $event){
$form = $event->getForm();
$this->addProvinceForm($form);
}
}
The second class is Municipi:
class AddMunicipioFieldSubscriber implements EventSubscriberInterface {
//put your code here
private $propertyPathToMunicipio;
public function __construct($propertyPathToMunicipio){
$this->propertyPathToMunicipio = $propertyPathToMunicipio;
}
public static function getSubscribedEvents(){
return array(
FormEvents::PRE_SET_DATA => 'preSetData',
FormEvents::PRE_SUBMIT => 'preSubmit'
);
}
private function addCityForm($form, $province_id){
$formOptions = array(
'class' => 'BackendBundle:DbMunicipio',
'label' => 'Municipio',
'attr' => array(
'class' => 'class_select_municipio',
),
'query_builder' => function (EntityRepository $repository) use ($province_id) {
$qb = $repository->createQueryBuilder('municipio')
->innerJoin('municipio.provincia', 'provincia')
->where('provincia.id = :provincia')
->setParameter('provincia', $province_id)
;
return $qb;
}
);
$form->add($this->propertyPathToMunicipio, EntityType::class, $formOptions);
}
public function preSetData(FormEvent $event){
$data = $event->getData();
$form = $event->getForm();
if (null === $data) {
return;
}
$accessor = PropertyAccess::createPropertyAccessor();
$municipio = $accessor->getValue($data, $this->propertyPathToMunicipio);
$province_id = ($municipio) ? $municipio->getIdMunicipio()->getProvincia()->getId() : null;
$this->addCityForm($form, $province_id);
}
public function preSubmit(FormEvent $event){
$data = $event->getData();
$form = $event->getForm();
$province_id = array_key_exists('provincia', $data) ? $data['provincia'] : null;
$this->addCityForm($form, $province_id);
}
}
The controled add this function:
public function municipioTestAction(Request $request){
$provincia_id = $request->get('provincia_id');
$em = $this->getDoctrine()->getManager();
$provincia = $em->getRepository('BackendBundle:DbMunicipio')->findByProvinceId($provincia_id);
return new JsonResponse($provincia);
}
Where the function findByProvinceId, I create it as a repository of the entity DbMunicipio.
class DbMunicipioRepository extends EntityRepository{
public function findByProvinceId($provincia_id){
$query = $this->getEntityManager()->createQuery("
SELECT muni
FROM BackendBundle:DbMunicipio muni
LEFT JOIN muni.provincia provin
WHERE provin.id = :provincia_id
")->setParameter('provincia_id', $provincia_id);
return $query->getArrayResult();
}
}
And de codec AJAX.
$(document).ready(function () {
$(".class_select_provincia").change(function(){
var data = {
provincia_id: $(this).val()
};
$.ajax({
type: 'POST',
url: URL+'/municipio-test',
data: data,
success: function(data) {
var $muni_selector = $('.class_select_municipio');
alert(data);
$muni_selector.html('<option>Ciudad</option>');
for (var i=0, total = data.length; i < total; i++) {
$muni_selector.append('<option value="' + data[i].id + '">' + data[i].municipio + '</option>');
}
}
});
});
});
I have a search form that works with the method POST, but the method POST doesn't display the requested data in the url.
With method POST the url look like this:
/search_flight
with the method GET no results found, the url look like this:
/search_flight?from=Cape+Town%2C+International+CPT&to=Johannesburg%2C+O.R.+Tambo+International+JNB&departuredate=2016%2F01%2F08&arrivaldate=2016%2F10%2F04&price=57.5%2C1000
I also noticed that with the method GET the data is reset in each input of the form.
routing.yml
searchFlight:
path: /search_flight
defaults: { _controller: FLYBookingsBundle:Post:searchtabflightResult }
requirements:
_method: GET|POST
controller
This method send the requested data to the method searchtabflightResultAction that will handle the query.
public function searchtabflightAction()
{
//$form = $this->createForm(new SearchflightType(),null, array('action' => $this->generateUrl('searchFlight'),'method' => 'GET',));
$form = $this->get('form.factory')->createNamed(null, new SearchflightType());
return $this->render('FLYBookingsBundle:Post:searchtabflight.html.twig', array(
'form' => $form->createView(),
));
}
.
<form action="{{ path ('searchFlight') }}" method="GET">
{# here I have my forms #}
</form>
.
public function searchtabflightResultAction(Request $request)
{
//$form = $this->createForm(new SearchflightType());
$form = $this->get('form.factory')->createNamed(null, new SearchflightType());
$form->handleRequest($request);
$em = $this->getDoctrine()->getManager();
$airport1 = $form["to"]->getData();
$airport = $form["from"]->getData();
$departureDateObj = $form["departuredate"]->getData();
$arrivalDateObj = $form["arrivaldate"]->getData();
$price = $form["price"]->getData();
$entities = $em->getRepository('FLYBookingsBundle:Post')->searchflight($airport1,$airport,$departureDateObj,$arrivalDateObj,$price);
return $this->render('FLYBookingsBundle:Post:searchtabflightResult.html.twig', array(
'entities' => $entities,
'form' => $form->createView(),
));
}
How can I make my search filter works with method get ?
Everything should be done within two actions, the basic concept is:
SearchFlightType has with/wo price option:
class SearchFlightType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('from', FormType\TextType::class)
->add('to', FormType\TextType::class)
->add('departuredate', FormType\TextType::class)
->add('arrivaldate', FormType\TextType::class);
if ($options['price']) {
$builder->add( 'price', FormType\TextType::class );
}
$builder
->add('submit', FormType\SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'price' => false,
));
}
}
Controller.php
class PostController extends Controller
{
/**
* #Route("/index", name="index")
*/
public function indexAction(Request $request)
{
$defaultData = array();
$form = $this->createForm(SearchFlightType::class, $defaultData, array(
// action is set to the specific route, so the form will
// redirect it's submission there
'action' => $this->generateUrl('search_flight_result'),
// method is set to desired GET, so the data will be send
//via URL params
'method' => 'GET',
));
return $this->render('Post/searchtabflight.html.twig', array(
'form' => $form->createView(),
));
}
/**
* #Route("/search_flight_result", name="search_flight_result")
*/
public function searchTabFlightResultAction(Request $request)
{
$defaultData = array();
$entities = null;
$form = $this->createForm(SearchFlightType::class, $defaultData, array(
// again GET method for data via URL params
'method' => 'GET',
// option for price form field present
'price' => true,
));
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// get data from form
$data = $form->getData();
// process the data and get result
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('FLYBookingsBundle:Post')->searchflight($data['from'], $data['to'], ...);
}
return $this->render('Post/searchtabflight.html.twig', array(
'form' => $form->createView(),
// present the result
'entities' => $entites,
));
}
}
Using Symfony2.3.4 and PHP5.6.3
People, I've been looking for this issue for a while now and yes, I've found some similar ones and even found this in the Cookbook.
Now you'd say, "This guy is pretty slow", bingo, I am. Please help me out because I can't seem to get this or any other example I've encountered to help me in my own problem.
What I need is to populate a select field when the user selects an item from another select field. All this happens in a standard-CRUDgenerated-Symfony2 form. Both selects stand for an entity collection each(Zone and UEB), being Zone the independent one.
Community: Stop talking and give me the code!
Me: OK, here is what I have so far:
//ReferenceController.php
public function newAction() {
$entity = new Reference();
$form = $this->createCreateForm($entity);
return $this->render('CCBundle:Reference:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
public function createAction(Request $request) {
$entity = new Reference();
$form = $this->createCreateForm($entity);
$form->bind($request);
/*
var_dump($form->get('UEB')->getData());
var_dump($form->get('UEB')->getNormData());
var_dump($form->get('UEB')->getViewData());
die();
*/
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('reference_show', array('id' => $entity->getId())));
}
return $this->render('CCBundle:Reference:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
private function createCreateForm(Reference $entity) {
$form = $this->createForm(new ReferenceType(), $entity, array(
'action' => $this->generateUrl('reference_create'),
'method' => 'POST',
));
return $form;
}
And
//ReferenceType.php
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('suffix')
->add('zone', null, array(
'required' => true,
))
;
//What follows is for populating UEB field accordingly,
//whether it's a "createForm" or an "editForm"
if ($options['data']->getId() !== null) {
$formModifier = function (FormInterface $form, Zone $zone = null) {
$UEBs = null === $zone ? array() : $zone->getUEBs();
$form->add('UEB', 'entity', array(
'required' => true,
'label' => 'UEB',
'class' => 'CCBundle:UEB',
// 'empty_value' => '',
'choices' => $UEBs,
));
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($formModifier) {
$data = $event->getData();
$formModifier($event->getForm(), $data->getZone());
});
} else {
$formModifier = function (FormInterface $form) {
$form->add('UEB', 'entity', array(
'required' => true,
'label' => 'UEB',
'class' => 'CCBundle:UEB',
'query_builder' =>
function(EntityRepository $er) {
return $er->createQueryBuilder('u')
->where('u.zone = :zone')
->setParameter('zone', $er->findFirstZone());
}
)
);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($formModifier) {
$formModifier($event->getForm());
});
}
And
//base.js
var goalURL = "" + window.location;
if (goalURL.slice(-13) === 'reference/new' || goalURL.match(/reference\/\d+\/edit$/))
{
//case new reference
goalURL = goalURL.replace('reference/new', 'reference/update_uebs/');
//case edit reference
goalURL = goalURL.replace(/reference\/\d+\/edit/, 'reference/update_uebs/');
//this is the function run every time the "new" or "edit" view is loaded
//and every time the Zone select field is changed
var runUpdateUEBs = function() {
$.getJSON(goalURL, {id: $('#cc_ccbundle_reference_zone').val()}, function(response) {
$('#cc_ccbundle_reference_UEB').children('option').remove();
var non_selected_options = [];
var index = 0;
$.each(response, function(key, val) {
var option = $('<option selected="selected"></option>');
option.text(val);
option.val(key);
option.prop('selected', 'selected');
option.appendTo($('#cc_ccbundle_reference_UEB'));
non_selected_options[index++] = $(option);
});
var amount = non_selected_options.length;
if (amount > 1)
$.each(non_selected_options, function(key, val) {
if (amount - 1 === key)
val.attr('selected', false);
});
});
};
runUpdateUEBs();
$('#cc_ccbundle_reference_zone').bind({
change: runUpdateUEBs
});
}
And
//ReferenceController.php
//this is where "goalURL" goes
function updateUEBsAction() {
$id = $this->getRequest()->get('id');
$em = $this->getDoctrine()->getManager();
$uebs = $em->getRepository('CCBundle:UEB')->findBy(array('zone' => $id));
$ids_and_names = array();
foreach ($uebs as $u) {
$ids_and_names[$u->getId()] = $u->getName();
}
return new \Symfony\Component\HttpFoundation\Response(json_encode($ids_and_names));
}
With this I can load the UEBs corresponding the Zone being shown at the moment and every time a new Zone is selected alright, but only visually, not internally, hence:
the select populates fine but when I submit the form it doesn't go through with it and outputs "This value is not valid" on the UEB field and the
var_dump($form->get('UEB')->getData());
var_dump($form->get('UEB')->getNormData());
var_dump($form->get('UEB')->getViewData());
die();
from above outputs
null
null
string <the_value_of_the_option_tag> (length=1)
I need to know how to populate the select AND the internal data to be submitted too.
Thanks for bearing with this simple explanation.
I'm listening(reading).
Here is the answer I was looking for, it looks a lot like the one in the cookbook but somehow I understood this one better and I was able to apply it to my own problem, it only needed a few tweaks in the ajax call and the corresponding action, but only regarding my own problem.
thanks to all who cared to read my question and special thanks to Joshua Thijssen for his post.
I have a form that will perform a search action with a drop down list of choices of category to search. The drop down list are subjects to search by. So:
Search by:
1) Invoice #
2) Tracking #
3) BL #
Then enter in the value and submit to search.
I have my form:
// src Bundle\Form\Type\SearchType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('kind', 'choice', array(
'choices' => array(
'invoice' => 'Invoice #',
'trackingno' => 'Tracking Number'
'blno' => 'BL #',
),
'label' => 'Search by: '
))
->add('value', 'text', array(
'label' => false
))
->add('Submit','submit');
}
With this function in the controller:
public function getForm() {
$form = $this->createForm(new SearchType(), array(
'action' => $this->generateUrl('search_view'),
'method' => 'POST'
) );
return $form;
}
With the action going to 'search_view' function:
/**
* #param Request $request
* #Route("/results/", name="search_view")
*/
public function searchAction(Request $request) {
$kind = $request->get('kind');
$value = $request->get('value');
$em = $this->getDoctrine()->getManager();
$findCfs = $em->getRepository("CFSBundle:Cfs")
->searchCfs($kind, $value);
return $this->render("CFSBundle:Search:index.html.twig", array(
'results' => $findCfs
));
}
My problem is I do not where to go to perform the database retrieval base on the category. This is what I have in the repository:
public function searchCfs($kind, $value) {
$query = $this->getEntityManager()
->createQuery('
SELECT
c.blno, m.ctrno, c.carrier, m.refno
FROM
CFSBundle:Cfs c
LEFT JOIN
cfs.refno m
WHERE
:kind LIKE :value
')->setParameter("kind", $kind)
->setParameter("value", $value);
try {
return $query->getResult();
} catch(\Doctrine\ORM\NoResultException $e) {
return null;
}
}
Of course this isn't working. I thought about creating different queries for each category and have the conditions submit according to its category, but I was wondering if there was one simple solution to this?
I'd advise using the QueryBuilder class when writing a query such as this, rather than writing DQL directly.
You could then do something like the following in your repository:
const KIND_INVOICE_NO = 'invoice';
const KIND_TRACKING_NO = 'tracking';
const KIND_BL_NO = 'blno';
public function searchCfs($kind, $value) {
$queryBuilder = $this->createQueryBuilder('c')
->select('c.blno, m.ctrno, c.carrier, m.refno')
->leftJoin('cfs.refno', 'm');
if ($kind === self::KIND_INVOICE_NO) {
$queryBuilder->where('c.invoiceno = :queryValue');
} elseif ($kind === self::KIND_TRACKING_NO) {
$queryBuilder->where('m.ctrno = :queryValue')
} elseif ($kind === self::KIND_BL_NO) {
$queryBuilder->where('c.blno = :queryValue')
}
$queryBuilder->setParameter('queryValue', $value);
$query = $queryBuilder->getQuery();
try {
return $query->getResult();
} catch(\Doctrine\ORM\NoResultException $e) {
return null;
}
}