SilverStripe dependent dropdown - x is not a valid option - php

I have a simple dropdown field with 2 values and a dependent dropdown field:
public function areaForm() {
$datasource = function($val) {
if ($val =='yes') {
$areas = DataObject::get('Area', 'ParentID = 0');
return $areas->map('ID', 'Name');
}
if ($val == 'no') {
return false;
}
};
$fields = new FieldList(
TextField::create('Name', 'Area Name:'),
$dropField = DropdownField::create('isChild', 'Is this a sub Area?', array('yes' => 'Yes', 'no'=>'No' ))
->setEmptyString('Select one'),
DependentDropdownField::create('ParentSelect', 'Select Parent Area:', $datasource)
->setDepends($dropField)
->setEmptyString('Select one')
);
return new Form($this, __FUNCTION__, $fields, FieldList::create(new FormAction('doSaveArea', 'Save area')));
}
public function doSaveArea($data, $form) {
var_dump($data);
exit;
$name = $data['Name'];
$isChild = $data['isChild'];
if ($isChild === 'no') {
$area = new Area();
$area->Name = $name;
$area->ParentID = 0;
$area->write();
}
elseif ($isChild === 'yes') {
$area = new Area();
$area->Name = $name;
$area->ParentID = $data['ParentSelect'];
$area->write();
}
$this->redirectBack();
}
When ever I try to save my object by submitting the form, it gives me the same message:
Please select a value within the list provided. x is not a valid option
The values are being populated correctly. I can see them in the browser by inspecting the element. Yet if I select ID 1 for example it says "1 is not a valid option" etc for each Area object. It gets stuck at validation, doesn't even go to the action. I've done similar things in other parts of the site/other sites and they work fine.
Why is this validation incorrectly blocking the form submission and how do we fix this?

Seems like you just need to create an Array of your Map object.
if ($val =='yes') {
$areas = Area::get()->filter('ParentID', '0');
return $areas->map('ID', 'Name')->toArray();
}
Normally you could just use the Map object as the source for a DropdownField. But I think the DependentDropdownField has a little trouble with the Map object.

Related

want to pass variable to aftersave() method after save(false) in yii

Actually,
as you see below code,
in model two functions are calling
$this->sendMailtoTutor(),$this->sendMailtoLearner()
i want to check a condition in model that if that condition will true then only these both function will call otherwise not. Is there any way to pass attributes or variable so that i can check condition in aftersave().
my code is as below
in controller
public function actionBooking()
{
$booking_temp = new BookingTemp();
$this->performAjaxValidation($booking_temp);
if (Yii::app()->request->isPostRequest && Yii::app()->request->getPost('BookingTemp'))
{
$booking_temp->attributes = Yii::app()->request->getPost('BookingTemp');
//echo '<pre>';print_r($booking_temp->attributes);exit;
if ($booking_temp->validate())
{
$extra_price = 0;
$post_data = Yii::app()->request->getPost('BookingTemp');
$cam = Cam::model()->findByPk($post_data['temp_cam_id']);
$data = array();
$data = $post_data;
$data['temp_book_user_id'] = Yii::app()->user->id;
$data['temp_book_cam_price'] = $cam->cam_price;
$data['temp_book_duration'] = $cam->cam_duration;
if ($post_data['temp_book_session'] == 2) {
$data['temp_book_cam_price'] = 2 * $cam->cam_price;
$data['temp_book_duration'] = 2 * $cam->cam_duration;
}
if ($post_data['temp_book_is_extra'] == "Y") {
$extra_price = $cam->camExtras->extra_price;
$data['temp_book_extra_price'] = $extra_price;
}
$price_calculation = CamBooking::price_calculation(Yii::app()->user->country_id, $data['temp_book_cam_price'], $extra_price);
$data['temp_book_processing_fees'] = $price_calculation['processing_fees'];
$data['temp_book_service_tax'] = $price_calculation['service_tax'];
$data['temp_book_total_price'] = $price_calculation['total_price'];
$booking_temp->temp_value = serialize($data);
$booking_temp->user_id = Yii::app()->user->id;
$booking_temp->tutor_id = $cam->tutor_id;
$booking_temp_variable = 'check_aftersave';
$booking_temp->save(false, $booking_temp_variable);
//$booking_temp->saveAttributes(array('tutor_id', 'user_id', 'temp_value'));
$created_at = Yii::app()->localtime->fromUTC($booking_temp->created_at);
$created_at_time = strtotime($created_at);
$end_time = $created_at_time + (60 * 3); // 3 min greater from created
$end_time_format = date("Y/m/d H:i:s", $end_time);
echo json_encode(array(
'status' => 'success',
'temp_guid' => $booking_temp->temp_guid,
'end_time_format' => $end_time_format,
), JSON_UNESCAPED_SLASHES);
Yii::app()->end();
} else {
$error = CActiveForm::validate($booking_temp);
if ($error != '[]')
echo $error;
Yii::app()->end();
}
}
}
in model
protected function afterSave()
{
if ($this->isNewRecord)
{
$this->sendMailtoTutor();
$this->sendMailtoLearner();
if ($this->is_message == 'Y' && !empty($this->book_message))
{
Message::insertMessage($this->book_message, $this->book_user_id, $this->cam->tutor_id, $this->cam_id);
}
$user_profile_link = CHtml::link($this->bookUser->fullname, array("/site/user/profile", "slug" => $this->bookUser->slug));
$cam_link = CHtml::link($this->cam->cam_title, array("/site/cam/view", "slug" => $this->cam->slug));
$message = "You have a new booking from {$user_profile_link} for your {$cam_link}";
Notification::insertNotification($this->cam->tutor_id, $message, 'book', $this->book_id);
}
return parent::afterSave();
}
How can I perform this?
Hi See if you want to do it in the afterSave() of BookingTemp Model;. Then you can define a variable in that model like
like
class BookingTemp extends CActiveRecord
{
public $booking_temp_variable;
............
}
now assign this variable before saving your model in actionBooking();
as
$booking_temp->booking_temp_variable = 'check_aftersave';
$booking_temp->save(false);// no need to pass a varible
now in your aftersave function you can easily get it like
protected function afterSave()
{
if ($this->isNewRecord)
{
$this->booking_temp_variable; // this has your value
}
return parent::afterSave();
}
Don't forget to keep it in rules as safe
public function rules(){
// your rules
array('booking_temp_variable','safe');
}
What exacly you want to do?
At first you have to know, that when you are calling save() method and u pass there two args - false and, $booking_temp_variable . The first arg says that you do not run validation, the second one is (should be) array of attributes.
http://www.yiiframework.com/doc/api/1.1/CActiveRecord#save-detail

Registration form fields reset after submit (wordpress & woocommerce)

I've got woocommerce registration form with two sections:
- One for private person,
- the other for company.
In company option there is two additional fields. I can switch between private and company by radio buttons and then I see relevant fields.
Problem: When I fill the form (as private user) and make some mistake, form reload and show where is the error (that is ok).
But unfortunately, after reload, it loads the form with all fields (the ones with additional company fields too). So I need to click 2 times between private and company to restore the right behavior.
How can i fix this? I mean after this error reloading, to display the form as initially.
I don't be sure that this is code responsible for this, but let's try:
add_filter('woocommerce_registration_errors', 'rs_registration_form_validation', 10, 3);
function rs_registration_form_validation($reg_errors, $sanitized_user_login, $user_email)
{
global $woocommerce;
$company_fields_required = (!empty($_POST['registration_type']) && 'company' === $_POST['registration_type']);
$shipp_to_different_address = (!empty($_POST['register_ship_to_different_address']) && 1 == $_POST['register_ship_to_different_address']);
$errors = false;
$fields = rs_registration_form_fields();
if ($shipp_to_different_address) {
$fields += rs_registration_form_fields_address();
}
if (!$company_fields_required) {
unset($fields['billing_company']);
unset($fields['billing_nip']);
}
//Validate required
foreach ($fields as $field => $settings) {
if (false === isset($settings['required']) || true === $settings['required']) {
if (empty($_POST[$field])) {
$errors = true;
wc_add_notice('Pole: <strong>'.$settings['label'].'</strong> jest wymagane.', 'error');
}
}
}
if ($errors) {
return new WP_Error('registration-error', 'Proszę poprawić błędy w formularzu');
}
return $reg_errors;
}
add_action('woocommerce_created_customer', 'rs_registration_form_submit');
function rs_registration_form_submit($user_id)
{
$fields = rs_registration_form_fields();
$fields += rs_registration_form_fields_address();
foreach ($fields as $field => $settings) {
if (isset($_POST[$field]) && !empty($_POST[$field])) {
update_user_meta($user_id, $field, $_POST[$field]);
}
}
}
add_filter('register_form', 'rs_registration_form');
function rs_registration_form()
{
$fields = rs_registration_form_fields();
include '_rs_registration_form.php';
}
add_filter('register_form_address', 'rs_registration_form_address');
function rs_registration_form_address()
{
$fields = rs_registration_form_fields_address();
include '_rs_registration_form.php';
}
add_filter('woocommerce_edit_address_slugs', 'rs_fix_address_slugs');
function rs_fix_address_slugs($slugs)
{
$slugs['billing'] = 'billing';
$slugs['shipping'] = 'shipping';
return $slugs;
}
function rs_rejestracja_url()
{
return get_permalink(244);
}
function rs_logowanie_url()
{
return get_permalink(20);
}
function rs_show_checkout_progress_bar($step = '')
{
include '_checkout_progress_bar.php';
}
function rs_order_form_buttons()
{
include '_order_form_buttons.php';
}
add_filter('woocommerce_get_checkout_url', 'rs_get_checout_url');
function rs_get_checout_url($url) {
if (is_user_logged_in()) {
$url .= '#step1';
}
return $url;
}
include 'src/RS_Search.php';
I don't know WooCommerce, but I think the error results because of these lines:
$company_fields_required = (!empty($_POST['registration_type']) && 'company' === $_POST['registration_type']);
and
if (!$company_fields_required) {
unset($fields['billing_company']);
unset($fields['billing_nip']);
}
After you submitted your "private" form and the validation failed, your fields are loaded again. Could it now be, that in your $_POST variable the registration_type is somehow set to 'company'? You can test this if you just print_r your $_POST['registration_type'] at the beginning of the function. If that is not the case, then I'm pretty sure the bug happens in another function, because this makes sense to me so far.
EDIT: After taking another look on your code, I think none of the posted functions is responsible for the misbehaviour. The first function is only responsible to check if some of the posted values are missing and to say "hey, here is an error". There has to be another function which is responsible for the fields which later are displayed in your HTML. I think you need to find this function.

How can I use callback functions in groceryCrud for the view record page?

I do not know how to set a callback function for the view record page in codeigniter.
I use the callback_column function and it does what I need in the grid view, but on the view record page it does not work.
I searched their site and forum and did not found anything that could help me.
My code looks like:
$zeus = new grocery_CRUD();
$zeus->set_theme('bootstrap');
// $zeus->set_language('romanian');
$zeus->set_table('programari');
$zeus->columns(array('id_client', 'id_sala', 'denumire', 'numar_persoane', 'observatii'));
$zeus->callback_column('id_sala',array($this,'_test_function'));
$cod = $zeus->render();
$this->_afiseaza_panou($cod);
public function _test_function($row, $value)
{
return '0';
}
write this lines in \libraries\Grocery_CRUD.php
at line number 3530
protected $callback_read_field = array();
than put this function after constructor call
public function callback_read_field($field, $callback = null)
{
$this->callback_read_field[$field] = $callback;
return $this;
}
//Now update this function to manage the field outputs using callbacks if they are defined for the same
protected function get_read_input_fields($field_values = null)
{
$read_fields = $this->get_read_fields();
$this->field_types = null;
$this->required_fields = null;
$read_inputs = array();
foreach ($read_fields as $field) {
if (!empty($this->change_field_type)
&& isset($this->change_field_type[$field->field_name])
&& $this->change_field_type[$field->field_name]->type == 'hidden') {
continue;
}
$this->field_type($field->field_name, 'readonly');
}
$fields = $this->get_read_fields();
$types = $this->get_field_types();
$input_fields = array();
foreach($fields as $field_num => $field)
{
$field_info = $types[$field->field_name];
if(isset($field_info->db_type) && ($field_info->db_type == 'tinyint' || ($field_info->db_type == 'int' && $field_info->db_max_length == 1))) {
$field_value = $this->get_true_false_readonly_input($field_info, $field_values->{$field->field_name});
} else {
$field_value = !empty($field_values) && isset($field_values->{$field->field_name}) ? $field_values->{$field->field_name} : null;
}
if(!isset($this->callback_read_field[$field->field_name]))
{
$field_input = $this->get_field_input($field_info, $field_value);
}
else
{
$primary_key = $this->getStateInfo()->primary_key;
$field_input = $field_info;
$field_input->input = call_user_func($this->callback_read_field[$field->field_name], $field_value, $primary_key, $field_info, $field_values);
}
switch ($field_info->crud_type) {
case 'invisible':
unset($this->read_fields[$field_num]);
unset($fields[$field_num]);
continue;
break;
case 'hidden':
$this->read_hidden_fields[] = $field_input;
unset($this->read_fields[$field_num]);
unset($fields[$field_num]);
continue;
break;
}
$input_fields[$field->field_name] = $field_input;
}
return $input_fields;
}
than call same as other callback functions
As far as I'm aware GroceryCRUD doesn't provide callbacks or another means of overriding the default output in the view state.
The solution to customising this would be to create a custom view to which you will insert the data from your record. This way you can customise the layout and other presentation.
What you would then do is unset the default read view with:
$crud->unset_read();
And add a new action where there are details on how to do this here.
What to do with the new action is point it to a URL that you map in routes.php if necessary and handle it with a new function in your controller. You'll either have to write a model function to retrieve the data since this isn't passed from GC or you can use the action to target a callback and feed $row to it via POST or something so that the data for the record is accessible in the view. (Look at the example in the link above).

Yii validation tabular form can't upload files

I'm having some problems when I try upload multiples files in multiples instances of my model (tabular way).
I have a model called Files and a view that generate a form for multiple instances of that model. When I save the form without "multpart/form-data", everything works, but if I put this parameter on form and submit it, the validation shows the message that "File cannot be blank."
See my controller code bellow:
public function actionRegistration() {
$company = new Company;
$contacts = $this->getModelItens('Contact', 3);
$banks = $this->getModelItens('Bank' , 2);
$files = $this->getModelItens('File' , 2);
$company->scenario = 'create';
if($_POST['Company']) {
$company->attributes = $_POST['Company'];
$valid = $company->validate();
$valid = $this->validateModels($_POST['Contact'], $contacts) && $valid;
$valid = $this->validateModels($_POST['Bank'], $banks) && $valid;
$valid = $this->validateModels($_POST['File'], $files) && $valid;
if($valid) {
if($company->save()) {
$this->saveModels($contacts, $company->id);
$this->saveModels($banks, $company->id);
$this->saveModels($files, $company->id);
$this->redirect('/obrigado');
}
}
}
$this->render('registration', array('company' => $company, 'contacts' => $contacts, 'banks' => $banks, 'files' => $files));
}
private function getModelItens($model_name, $times, $scenario = 'create') {
$models = array();
for($i = 0; $i < $times; $i++) {
$models[$i] = new $model_name;
$models[$i]->scenario = $scenario;
}
return $models;
}
private function validateModels($forms, $models) {
$valid = true;
foreach($forms as $k => $form) {
$models[$k]->attributes = $form;
$models[$k]->position = $k;
$valid = $models[$k]->validate() && $valid;
}
return $valid;
}
private function saveModels($models, $company_id) {
foreach($models as $k => $model) {
$model->company_id = $company_id;
if($model instanceOf File) {
if($model->save()) $this->upload_file($model, "file");
} else $model->save();
}
}
private function upload_file($model, $field, $k) {
$path = Yii::app()->basePath . "/../assets/files/companies/{$model->company_id}/";
$file = CUploadedFile::getInstance($model, $field);
if($file instanceof CUploadedFile) $model->$field = $file;
if($model->$field instanceof CUploadedFile) {
if(!file_exists($path)) exec("mkdir -p {$path}");
$model->$field->saveAs($path . $model->$field);
}
}
I've tried everything but I can't fix it, any suggestion?
Thanks.
I am not sure if this assignment $models[$k]->attributes = $form; will do the $_FILES as well as the $_POST for your model. Looking at this example, I think you need to do that CUploadedFile stuff before you validate() or save() (which also validates), otherwise your file field will be blank.
I think you need this order of events:
$model->$field = CUploadedFile::getInstance($model, $field); // set file field
$model->save() OR $model->validate() // THEN validate
$model->$field->saveAs($path . $model->$field); // then save the file
Instead, you have this order of events currently:
$model->save() AND $model->validate()
$model->$field = CUploadedFile::getInstance($model, $field);
$model->$field->saveAs($path . $model->$field);
I could be way off though, I'm just looking at your code and not actually testing this. Also, I have no idea why it only throws an error when you set the multpart/form-data attribute on the form; it should not work at all without that! Maybe it was skipping validation on the file fields without that? Or attributes() was assigning something from $_POST besides the file? /shrug
UPDATE:
Another thing that might be happening, is that with "tabular" input you need to reference the input field with the '$key' as well (i.e. Model[0][imageFile]). So here, where you are calling:
$this->upload_file($model, "file");`
You probably need to call something like this, so that getInstanceByName() is getting the right field name:
$this->upload_file($model, "[".$k."]file");`
I think you will still need to re-arrange your code as I mention above too.
Good luck!
More reading:
Yii forum post about this subject
Your same question in the Yii forum

zend form validation

I wonder how Zend_Form validates inputs, I mean how does it know which input fields to validate. I looked to php globals($_POST, $_GET) and I didn't see anything set as an identifier(for example ) in order to know how validate. Can anyone suggest me any guide for this stuff?
Well, the best option to find out is to look at the code of Zend_Form:
/**
* Validate the form
*
* #param array $data
* #return boolean
*/
public function isValid($data)
{
if (!is_array($data)) {
require_once 'Zend/Form/Exception.php';
throw new Zend_Form_Exception(__METHOD__ . ' expects an array');
}
$translator = $this->getTranslator();
$valid = true;
$eBelongTo = null;
if ($this->isArray()) {
$eBelongTo = $this->getElementsBelongTo();
$data = $this->_dissolveArrayValue($data, $eBelongTo);
}
$context = $data;
foreach ($this->getElements() as $key => $element) {
if (null !== $translator && $this->hasTranslator()
&& !$element->hasTranslator()) {
$element->setTranslator($translator);
}
$check = $data;
if (($belongsTo = $element->getBelongsTo()) !== $eBelongTo) {
$check = $this->_dissolveArrayValue($data, $belongsTo);
}
if (!isset($check[$key])) {
$valid = $element->isValid(null, $context) && $valid;
} else {
$valid = $element->isValid($check[$key], $context) && $valid;
$data = $this->_dissolveArrayUnsetKey($data, $belongsTo, $key);
}
}
foreach ($this->getSubForms() as $key => $form) {
if (null !== $translator && !$form->hasTranslator()) {
$form->setTranslator($translator);
}
if (isset($data[$key]) && !$form->isArray()) {
$valid = $form->isValid($data[$key]) && $valid;
} else {
$valid = $form->isValid($data) && $valid;
}
}
$this->_errorsExist = !$valid;
// If manually flagged as an error, return invalid status
if ($this->_errorsForced) {
return false;
}
return $valid;
}
which means in a nutshell, Zend_Form will iterate over all the configured elements in the form and compare them against the values in the array you passed to it. If there is a match, it will validate that individual value against the configured validators.
So, you create form in action and then check is there post|get data. You can check is_valid form right here. You need pass $_POST or $_GET data to isValid() function. Example:
if ($request->isPost() && $form->isValid($request->getPost())) {
isValid() is function Zend_Form class. Form runs all validations for each element (just if you dont set to stop in first validation fail) and then for subforms too.
Look at Zend_Form quickstart, it's a very bright example on how to start dealing with forms in Zend.
Validating a text input looks like this:
$username = new Zend_Form_Element_Text('username');
// Passing a Zend_Validate_* object:
$username->addValidator(new Zend_Validate_Alnum());
// Passing a validator name:
$username->addValidator('alnum');
Or you can use:
$username_stringlength_validate = new Zend_Validate_StringLength(6, 20);
$username = new Zend_Form_Element_Text('username');
$username->setLabel('Username: ')
->addFilters(array('StringTrim', 'HtmlEntities'))
->setAttrib('minlength', '6')
->setAttrib('class', 'required')
->removeDecorator('label')
->removeDecorator('HtmlTag')
->removeDecorator('DtDdWrapper')
->setDecorators(array(array('ViewHelper'), array('Errors')))
->addValidator($username_stringlength_validate);

Categories