I'm working on an e-commerce project and I got stuck at the cart update. Here I have to present a form using the contents of the current cart, with input fields containing the current quantities.
I checked the documentation and the forums, but I didn't find anything useful. The problem is that i cannot declare the exact form fields in my form class because I don't know how many fields will be there. I tried this:
class CartForm extends sfForm {
public function configure()
{
$cart = sfContext::getInstance()->getUser()->getShoppingCart();
foreach ($cart->getItems() as $item) {
$widgetName = $item->getId().'_quantity';
$this->widgetSchema[$widgetName] = new sfWidgetFormInput(
array(),
array(
'class' => 'quantity-input',
'id' => null,
'name' => $widgetName
)
);
$this->widgetSchema->setDefault($widgetName, $item->getQuantity());
$this->validatorSchema[$widgetName] = new sfValidatorInteger(array(
'required' => true,
'min' => 1
),
array());
}
unset($cart);
$this->getWidgetSchema()->getFormFormatter()->setRowFormat('%field%%error%%hidden_fields%');
}
}
but I got some errors:
Fatal error: Cannot use object of type sfShoppingCart as array in /home/sfprojects/mdmall/lib/vendor/symfony/lib/form/sfForm.class.php on line 784
so this is not the right way. I tried to use raw fields without any form classes (and validators) but something very odd happens, instead of getting the $_POST values i get a 404 error because when I submit the form it doesn't trigger this:
cart_update:
url: /cart/update.:sf_format
class: sfRequestRoute
param: { module: cart, action: update, sf_format: html }
requirements: { sf_method: post }
If I remove the requirement, cart/update runs, but I dont have the $_POST data in the request object. Do you have any ideas?
These will help you with regard to dynamically adding form fields and working with validation of those fields:
Dynamic Emedded forms (but the process is similar for fields)
Dynamic Form Fields
Custom Validation
Related
I'm creating a form in moodle using moodleform class. I have many fields in the form. What I'm wanting to do is when the user fills in the first field, I want to get the data that he/she entered, search the relevant DB table for a field that matches that input, then populate the other fields for that record.
Please note that the user hasn't pressed any submit button yet. I've been trying to find a function that gets the entered data but all efforts have been in vain. What I found was a get_data() method but I don't even know how to use that correctly. I've been reading the moodle docs but nothing is helping. I'm not a beginner to coding but neither am I an expert.
Here's a code snippet:
class requestcourse_form extends moodleform
{
function definition()
{
global $CFG, $currentsess, $DB, $USER, $currentrecord;
$mform =& $this->_form; // Don't forget the underscore!
// Form header
$mform->addElement('header', 'mainheader','<span style="font-size:22px">'.get_string('courserequestform','block_usp_mcrs'). '</span>');
// Course Code field.
$coursecodearray = array();
$coursecodearray[0] = get_string('choosecoursecode', 'block_usp_mcrs');
$allcoursecodes = $DB->get_records_select('block_usp_mcrs_courses', 'id > 0', array(), 'id', 'id, course_code');
foreach ($allcoursecodes as $id => $coursecodeobject) {
$coursecodearray[$id] = $coursecodeobject->course_code;
}
$coursecode = $mform->addElement('select', 'coursecode', get_string('coursecode', 'block_usp_mcrs'), $coursecodearray);
$mform->addRule('coursecode', get_string('required'), 'required', null, 'client');
$mform->setType('coursecode', PARAM_RAW);
// Course Name field. TODO: Course Name to pick automatically after entering Course Code
$coursenamearray = array();
$coursenamearray[0] = get_string('choosecoursename', 'block_usp_mcrs');
$allcoursenames = $DB->get_records_select('block_usp_mcrs_courses', 'id > 0', array(), 'id', 'id, course_name');
foreach ($allcoursenames as $id => $coursenameobject) {
$coursenamearray[$id] = $coursenameobject->course_name;
}
$mform->addElement('select', 'coursename', get_string('coursename', 'block_usp_mcrs'), $coursenamearray);
$mform->addRule('coursename', get_string('required'), 'required', null, 'client');
$mform->setType('coursename', PARAM_RAW);
Any help would be appreciated.
You have to achieve this using javascript, as Moodle has no functionality to fill data in a nested way.
Add AMD module js file where you are calling your mform for display purpose.
In the file where you render your mform
$mform->render();
add below line to call you amd js file.
$PAGE->requires->js_call_amd('local_acestructure/registration', 'init');
In your amd js file make httprequest / ajax call to fetch data based on your course_code select drop down change.
Thank you.
Long story short, in Symfony 2.8 I've got Movie entity with actors field, which is ArrayCollection of entity Actor (ManyToMany) and I wanted the field to be ajax-loaded Select2.
When I don't use Ajax, the form is:
->add('actors', EntityType::class, array(
'class' => Actor::class,
'label' => "Actors of the work",
'multiple' => true,
'attr' => array(
'class' => "select2-select",
),
))
And it works.
I tried to put there an empty Select field:
->add('actors', ChoiceType::class, array(
'mapped' => false,
'multiple' => true,
'attr'=>array(
'class' => "select2-ajax",
'data-entity'=>"actor"
)
))
The Select2 Ajax works, everything in DOM looks the same as in previous example, but on form submit I get errors in the profiler: This value is not valid.:
Symfony\Component\Validator\ConstraintViolation
Object(Symfony\Component\Form\Form).children[actors] = [0 => 20, 1 => 21]
Caused by:
Symfony\Component\Form\Exception\TransformationFailedException
Unable to reverse value for property path "actors": Could not find all matching choices for the given values
Caused by:
Symfony\Component\Form\Exception\TransformationFailedException
Could not find all matching choices for the given values
The funny part is the data received is the same as they were when it was an EntityType: [0 => 20, 1 => 21]
I marked field as not mapped, I even changed field name to other than Movie entity's field name. I tried adding empty choices, I tried to leave it as EntityType but with custom query_builder, returning empty collection. Now I'm out of ideas.
How should I do it?
EDIT after Raymond's answer:
I added DataTransformer:
use Doctrine\Common\Persistence\ObjectManager;
use CompanyName\Common\CommonBundle\Entity\Actor;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
class ActorToNumberTransformer implements DataTransformerInterface
{
private $manager;
public function __construct(ObjectManager $objectManager)
{
$this->manager = $objectManager;
}
public function transform($actors)
{
if(null === $actors)
return array();
$actorIds = array();
foreach($actors as $actor)
$actorIds[] = $actor->getId();
return $actorIds;
}
public function reverseTransform($actorIds)
{
if($actorIds === null)
return array();
foreach($actorIds as $actorId)
{
$actor = $this->manager->getRepository('CommonBundle:Actor')->find($actorId);
if(null === $actor)
throw new TransformationFailedException(sprintf('An actor with id "%s" does not exist!', $actorId));
$actors[] = $actor;
}
return $actors;
}
}
Added it at the end of the MovieType buildForm():
$builder->get('actors')
->addModelTransformer(new ActorToNumberTransformer($this->manager));
$builder->get('actors')
->addViewTransformer(new ActorToNumberTransformer($this->manager));
And added service:
common.form.type.work:
class: CompanyName\Common\CommonBundle\Form\Type\MovieType
arguments: ["#doctrine.orm.entity_manager"]
tags:
- { name: form.type }
Nothing changed. On form submit, reverseTransform() gets the proper data, but profiler shows the same error. That's a big mistery for me now...
You'll need to add a DTO (Data Transformer ) to transform the value received from your form and return the appropriate object .
Since you're calling the value from Ajax it doesn't recognized it anymore as a an object but a text value.
Examples :
Symfony2 -Use of DTO
Form with jQuery autocomplete
The correct way isn't Data Transformer but Form Events, look here:
http://symfony.com/doc/current/form/dynamic_form_modification.html#form-events-submitted-data
In the example you have the field sport (an entity, like your Movie) and the field position (another entity, like actors).
The trick is to use ajax in order to reload entirely the form and use
PRE_SET_DATA and POST_SUBMIT.
I'm using Symfony 3.x but I think it's the same with 2.8.x
When you add data transformers and nothing seems to change, it sounds like the data never goes through your data transformers. The transformation probably fails before your new data transformers are called. Try to add a few lines to your code:
$builder->get('actors')->resetViewTransformers();
$builder->get('actors')->resetModelTransformers();
// and then add your own
I'm starting developing with Symfony2 and looks like I need help. I have Product entity related with SynchronizationSetting entity. I can edit product data by form maped with his entity. But I also need to modify some data related to product in SynchronizationSetting. To do that I've modified the form so it look like that (Vendor\ProductBundle\Form\ProductType.php):
...
->add('synchronization_setting', 'choice', array(
'choices' => array('daily' => 'Daily', 'weekly' => 'Weekly', 'never' => 'Never'))
After form is submitted selected checkbox values are passed to setSynchronizationSetting method in Product Entity. Then I do that (Vendor\ProductBundle\Entity\SynchronizationSetting.php):
public function setSynchronizationSetting($data)
{
$synchronizationSetting = new SynchronizationSetting();
$synchronizationSetting->setDaily(in_array('daily', $data) ? '1' : '0');
...
}
And now I need to somehow save those SynchronizationSetting entity into database. I read that calling entity manager from here is very bad practice so... how should I save this?
One possible way (I'm not sure if it's good practice)
public function setSynchronizationSetting($data)
{
$synchronizationSetting = new SynchronizationSetting();
$synchronizationSetting->setDaily(in_array('daily', $data) ? '1' : '0');
}
public function retSynchronizationSetting()
{
return $this->synchronizationSetting;
}
Then in your controller in place where you handle form data you call retSynchronizationSetting() and save entity using EntityManager.
For some reason my ajax form does not work if I change my form elements to hidden. It does how ever work if i change them to inputs instead. Why would that be?
Here is the view
<div id="price">
<?php
$this->Js->get('#phonepricearea');
echo $this->Form->create('offer', array('url' => '/PhoneKarma/PhoneQueries/ajaxOffer', 'class' => 'custom'));
echo $this->Form->hidden('phoneCapacity',array('value'=>''));
echo $this->Form->hidden('phoneCondition',array('value'=>''));
echo $this->Form->hidden('carrier',array('value'=>''));
echo $this->Js->submit('Check', array('class' => 'button expand',
'title' => 'Check',
'url' => array(
'action' => 'ajaxOffer'
),
'update' => '#price'
));
echo $this->Form->end();
?></div>
Controller
public function ajaxOffer($capacity=null, $condition = null , $carrier = null) {
if (!empty($this->data) && $this->request->is('ajax')) {
//do stuff this doesn't effect the code..
$this->render('ajaxOffer', 'ajax');
} else {
$this->set('offer', "0");
}
}
Javascript to change Value
$('#offerPhoneCapacity').val(id);
400 errors are usually security component blackholes. The documentation says it would issue 404 errors, but that's wrong, it throws a BadRequestException if not configured otherwise.
If an action is restricted by the Security Component it is black-holed
as an invalid request which will result in a 404 error by default. You
can configure this behavior by setting the
$this->Security->blackHoleCallback property to a callback function in
the controller.
SecurityComponent::blackHole(object $controller, string $error)
Black-hole an invalid request with a 404 error or a custom callback.
With no callback, the request will be exited. If a controller callback
is set to SecurityComponent::blackHoleCallback, it will be called and
passed any error information.
Your problem is probably caused by the security components form tampering prevention functionality. Hidden fields need to be static, because their values are used to genereate the security token, if the values change, the genereated comparision token will be different and thus the form will be treated as invalid.
By default SecurityComponent prevents users from tampering with forms.
It does this by working with FormHelper and tracking which files are
in a form. It also keeps track of the values of hidden input elements.
All of this data is combined and turned into a hash. When a form is
submitted, SecurityComponent will use the POST data to build the same
structure and compare the hash.
If you need to change hidden fields, then you have to define them in the unlockedFields property/option or by using the form helpers unlockField() method.
Examples (untested):
public $components = array
(
'Security' => array
(
'unlockedFields' => array
(
'Offer.phoneCapacity',
'Offer.phoneCondition',
'Offer.carrier'
)
)
);
public function beforeFilter()
{
$this->Security->unlockedFields => array
(
'Offer.phoneCapacity',
'Offer.phoneCondition',
'Offer.carrier'
);
}
$this->Form->unlockField('Offer.phoneCapacity');
$this->Form->unlockField('Offer.phoneCondition');
$this->Form->unlockField('Offer.carrier');
Im using drupal and im trying to create my own form in a block.
Ive wrote a module which creates a block with a submit button.
When the form is submitted im trying to write the values to my db.
im using this code
function my_module_my_form_submit($form, &$form_state) {
block_example_insert_credits($credits_record);
}
function block_example_insert_credits() {
global $user;
$credits_record = array(
'nid' => $node->nid,
'uid' => $user->uid,
'credits' => $form_state['values']['bids'],
);
drupal_write_record('example_table', $credits_record, 'nid');
}
the form submits and validates, and the table and columns exist in my db. When I submit the form nothing gets sent to database, why isnt my code correct?
There are a few things not quite right:
You're not passing any parameters to block_example_insert_credits()
You have no reference to $credits_record in my_module_my_form_submit() so nothing would be passed to the insert function anyway.
You're trying to access $form_state from a function in which it doesn't exist
You don't have a reference to the node object anywhere so you can't use it. $node isn't a globally available variable, if you want a node object it will have to come from a value save in your form or from the menu_get_object() function (if the block is being displayed on a node page).
Try this code and see if you have any luck:
function my_module_my_form_submit($form, &$form_state) {
block_example_insert_credits($form_state);
}
function block_example_insert_credits($form_state) {
global $user;
$node = menu_get_object();
$credits_record = array(
'nid' => $node->nid,
'uid' => $user->uid,
'credits' => $form_state['values']['bids'],
);
drupal_write_record('example_table', $credits_record, 'nid');
}
Hope that helps.
i'm a drupal novice so apologies if i'm wrong...isn't it the $node or &$form_state that should be passed to the insert method from form_submit?......maybe you could check by using watchdog for the credit records array to check values are passed properly...cheers