Silverstripe 4 Form on custom PageController submit - forms

I have a Form in my custom PageController.
The Form renders properly in template Register.ss.
In my setup the submit - function on PageController doesn't work.
I have tried it without setting the submit-route in routes.yml
resulting in a 404.
I have tried to set the submit-route in routes.yml
and in $url_handlers which results in an error:
Uncaught ArgumentCountError: Too few arguments to function
TherapyRegisterController::submit(), 1 passed in
/var/home/xxxxx/vendor/silverstripe/framework/src/Control/RequestHandler.php
on line 323 and exactly 2 expected
How to get the submit - function to work?
//routes:
SilverStripe\Control\Director:
rules:
therapy//anmeldung/$ID: TherapyRegisterController
# therapy//submit: TherapyRegisterController
TherapyRegisterController:
class TherapyRegisterController extends PageController{
private static $allowed_actions = ['registerForm', 'submit'];
private static $url_handlers = [
'anmeldung/$ID' => 'index',
//'anmeldung/submit' => 'submit',
];
public function registerForm($id)
{
$fields = new FieldList(
TextField::create('Name', 'Name')
);
$actions = new FieldList( [
$cancelButton = FormAction::create('cancel')->setTitle('ABBRECHEN')->setAttribute('formaction', 'therapy/cancel'), // 'cancel'
$sendButton = FormAction::create('submit')->setTitle('ANMELDEN')->setAttribute('formaction', 'therapy/submit') // 'submit'
]);
$validator = new RequiredFields('');
$form = new Form($this, 'registerForm', $fields, $actions, $validator);
$form->setTemplate('Register');
return $form;
}
public function submit($data, Form $form)
{
Debug::show($data);
}
public function index(HTTPRequest $request)
{
$arrayData = array (
'ID' => $request->param('ID')
);
return $this->customise($arrayData)->renderWith(array('Anmeldung', 'Page'));
}
Register.ss :
$registerForm($ID)

The ArgumentCountError does say that the submit method was only getting 1 argument but that submit method is expecting 2 as seen in your code. I'm not sure what exact version of SilverStripe you have but I can see this on line 323 of that RequestHandler.php:
$actionRes = $this->$action($request);
That first argument is going to be a SilverStripe\Control\HTTPRequest. The form submission should be a POST. You can get that array $data using the example below:
public function submit($request)
{
$data = $request->postVars();
}
It seems that you can't get that Form $form from the arguments here.

Related

Injecting variables into Forms __construct function Zend 2

I am trying to prepopulate a form's drop down options via data stored statically in the module.config.php in Zend 2 and am running into a problem which entails:
I try to get the Servicemanager in the __construct() function but it is unavailable.
I move the element declarations to another function within the form class so I can pass variables into it but the view controller cannot find the elements.
I currently call the form via a Servicemanager Invokable. How can I pass these arrays into the form's __construct() function?
Here is the code:
class ILLForm extends Form
{
public function __construct($fieldsetName, $campuses, $ILLTypes, $getFromOptions)
{
parent::__construct('create_ill');
$this
->setAttribute('method', 'post')
->setHydrator(new ClassMethodsHydrator(false))
->setInputFilter(new InputFilter())
;
$ill = new ILLFieldset('ill', $campuses, $ILLTypes, $getFromOptions);
$ill->setName('ill')
->setOptions(array(
'use_as_base_fieldset' => true,
));
$captcha = new Element\Captcha('captcha');
$captchaAdapter = new Captcha\Dumb();
$captchaAdapter->setWordlen(5);
$captcha->setCaptcha($captchaAdapter)
->setLabelAttributes(array('class' => 'sq-form-question-title'))
->setAttribute('class', 'sq-form-field required')
->setLabel("* Captcha")
->setAttribute('title', 'Help to prevent SPAM');
$submit = new Element\Submit('submit');
$submit->setAttribute('value', 'Submit ILL')
->setAttribute('class', 'sq-form-submit')
->setAttribute('title', 'Submit ILL');
$this->add($ill)
->add($captcha)
->add($submit);
}
}
The Indexcontroller Factory that calls the Form:
class IndexControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $controllers)
{
$allServices = $controllers->getServiceLocator();
$sm = $allServices->get('ServiceManager');
$smConfig = $allServices->get('config');
$campuses = $smConfig['campuses'];
$illCategories = $smConfig['ILLCategories'];
$getFromOptions = $smConfig['getFromOptions'];
$controller = new IndexController();
$controller->setCampuses($campuses);
$controller->setILLCategories($illCategories);
$controller->setGetFromOptions($getFromOptions);
$controller->setILLForm($sm->get('ILL-form'));
$controller->setILLFormFilter($sm->get('ILL-form-filter'));
//$controller->setParams($sm->get('params'));
return $controller;
}
}
and the relevant module.config.php excerpt:
'service_manager' => array(
'abstract_factories' => array(
'Zend\Cache\Service\StorageCacheAbstractServiceFactory',
'Zend\Log\LoggerAbstractServiceFactory',
),
'invokables' => array(
'ILL-form-filter' => 'ILL\Form\ILLFormFilter',
'ILL-form' => 'ILL\Form\ILLForm',
),
I ended up taking the form out of the service manager invokables in module.config.php (removed line for 'ILL-form') and call it from the indexControllerFactory.php instead
$create_ill = new Form\ILLForm('create_ill', $campuses, $illCategories, $getFromOptions);
$controller->setILLForm($create_ill);
instead of
$controller->setILLForm($sm->get('ILL-form'));

Create Zend form element dynamically

I'm using Zend Framework 1.12 on a project. I having some problems with the Zend_Form. Some fields are generated dynamically on execution time, but the Zend_Form is static, a element predefined at creation.
So, when the form is sent, the validation doesn't work because new fields were added and the sent form doesn't match the form created.
How do that adaptation?
You should try, following solution: after sending the form, get the $_POST array, then check which fields/values do you have and create/modify form Object with this fields/validation.
I would have done this way :
class MyForm extends Zend_Form
{
public function init()
{
//... Create here the basic elements
}
public function initFromPostValue( $post )
{
if( array_key_exists( 'dynamicsField', $post ) ) {
$el = $this->createElement( 'select', 'dynamicsField' )
->setValidators( array( ... PUT your validators here ) );
$this->addElement( $el );
}
}
}
In the validation action :
public function validationAction()
{
$form = new MyForm();
$form->initFromPostValue( $_POST );
if( $form->isValid( $_POST ) ) {
// Form is valid
} else {
// Form is invalid
}
}

How to merge subform array into single array in Zend Framework1.11

I am using Zend Framework1.11. In my Zend Form I have two zend sub form, I have added these two sub form using addSubForm function.
Now when I call this zend form in controller then isValid function is not working. I have called it as follow..
public function registeredAction(){
$form = new Application_Form_RegisteredForm();
$form->setAction('registered');
$formData = $this->_request->getPost();
if($form->isValid($formData)){
// save into database using model class.
} else {
$form->populate($formData);
}
$this->view->form = $form;
}
In following code isValid is not working, while I print_r the $fotmData requested array, it print array like:-
Array(
[personal] => Array
(
[firstname] => 'Example',
[lastname] => 'Solution'
)
[MAX_FILE_SIZE] => 8388608
[address] => Array
(
[country] => 'IND',
[state] => 'RAJ'
)
);
I have also used the setData() function but it is not working, it's give exceptional error "Message: Method setData does not exist", I have used php array_merge function but return array is not working with isValid().
Can anyone help me to solve this problem. so I can easily store form data into database.
Thanks!
Take a look at array_merge
http://php.net/manual/de/function.array-merge.php
$newFormData=array_merge($formData["personal"],$formData["address"]);
My solution is to create new base form with new method getSubFormsValues(), i.e.:
class My_Form extends Zend_Form
{
public function getSubFormsValues()
{
$values = array();
foreach ($this->getSubForms() as $form) {
$name = $form->getName();
$value = $form->getValues();
$values = array_merge($value[$name], $values);
}
return $values;
}
}
When you can call $my_form_obj->getSubFormValues() in your code.

Validate a set of fields / group of fields in Zend Form

Is there any good solution for the following requirements:
A form with one field for zip code and default validators like number, length, etc.
After submission, the form is checked against a database.
If the zip code is not unique we have to ask for an city.
Examples:
Case 1: Submited zip code is unique in database. Everything is okay. Process form
Case 2: Submited zip code is not unique. Add a second field for city to the form. Go back to form.
We want to handle this in an generic way (not inside an controller). We need this logic for
a lot of forms. First thought was to add it to isValid() to every form or write a
validator with logic to add fields to the form. Subforms are not possible for us, because we need this for different fields (e.g. name and street).
Currently I'm using isValid method inside my forms for an User Form to verify the password and confirm password field. Also, when the form is displayed in a New Action, there are no modifications, but when displayed in an Edit Action, a new field is added to the form.
I think that is a good option work on the isValid method and add the field when the validation return false, and if you want something more maintainable, you should write your own validatator for that purpose.
Take a look at my code:
class Admin_Form_User extends Zf_Form
{
public function __construct($options = NULL)
{
parent::__construct($options);
$this->setName('user');
$id = new Zend_Form_Element_Hidden('id');
$user = new Zend_Form_Element_Text('user');
$user->setLabel('User:')
->addFilter('stripTags')
->addFilter('StringTrim')
->setAllowEmpty(false)
->setRequired(true);
$passwordChange = new Zend_Form_Element_Radio('changePassword');
$passwordChange->setLabel('Would you like to change the password?')
->addMultiOptions(array(1 => 'Sim', 2 => 'Não'))
->setValue(2)
->setSeparator('');
$password = new Zend_Form_Element_Password('password');
$password->setLabel('Password:')
->addFilter('stripTags')
->addFilter('StringTrim')
->setRequired(true);
$confirm_password = new Zend_Form_Element_Password('confirm_password');
$confirm_password->setLabel('Confirm the password:')
->addFilter('stripTags')
->addFilter('StringTrim')
->addValidator('Identical')
->setRequired(true);
$submit = new Zend_Form_Element_Submit('submit');
$submit->setLabel('Save');
$this->addElements(array($id,$name,$lastname,$group,$user,$passwordChange,$password,$confirm_password,$submit));
$this->addDisplayGroup(array('password','confirm_password'),'passwordGroup');
$this->submit->setOrder(8);
$this->setDisplayGroupDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'div','id' => 'div-password'))
)
);
$passwordChange->clearDecorators();
}
public function addPasswordOption()
{
$this->changePassword->loadDefaultDecorators();
$this->getDisplayGroup('passwordGroup')
->addDecorators(array(
array('HtmlTag', array('tag' => 'div','id' => 'div-password'))
)
);
$this->password->setRequired(false);
$this->confirm_password->setRequired(false);
}
public function setPasswordRequired($flag = true)
{
$this->password->setRequired($flag);
$this->confirm_password->setRequired($flag);
}
public function isValid($data)
{
$confirm = $this->getElement('confirm_password');
$confirm->getValidator('Identical')->setToken($data['password']);
return parent::isValid($data);
}
}
So, in my controller:
public function newAction()
{
$this->view->title = "New user";
$this->view->headTitle($this->view->title, 'PREPEND');
$form = $this->getForm();
if($this->getRequest()->isPost())
{
$formData = $this->_request->getPost();
if($form->isValid($formData))
{
$Model = $this->getModel();
$id = $Model->insert($formData);
$this->_helper->flashMessenger('The user data has beed updated.');
$this->_helper->redirector('list');
}
}
$this->view->form = $form;
}
public function editAction()
{
$this->view->title = "Edit user";
$this->view->headTitle($this->view->title, 'PREPEND');
$id = $this->getRequest()->getParam('id');
$form = $this->getForm();
// Add yes or no password change option
$form->addPasswordOption();
$Model = $this->getModel();
if($this->getRequest()->isPost())
{
$formData = $this->getRequest()->getPost();
// Change password?
if($formData['changePassword'] == 2) $form->setPasswordRequired(false);
if($form->isValid($formData))
{
$Model->update($formData);
$this->_helper->flashMessenger('The user data has beed updated.');
$this->_helper->redirector('list');
}
}
$data = $Model->getById($id)->toArray();
$form->populate($data);
$this->view->form = $form;
}
You will probably need a Javascript form validator for that. In the submit function perform an AJAX call to check if the zipcode is unique. If not, show an extra city field.
But you still have to perform the validation server side: never trust user input, even if it's validated on the client side.

Extending Zend_View_Helper_FormElement

I have created this file at My/View/Helper/FormElement.php
<?php
abstract class My_View_Helper_FormElement extends Zend_View_Helper_FormElement
{
protected function _getInfo($name, $value = null, $attribs = null,
$options = null, $listsep = null
) {
$info = parent::_getInfo($name, $value, $attribs, $options, $listsep);
$info['id'] = 'My new ID';
return $info;
}
}
How can i get the normal form elements to use this instead?
Why i want this?
Say that i use the same form multiple times on a page, the 'id='-tag of the formelements will apear multiple times, this is not w3c-valid. So initially i want to prefix the id with the id of the form.
Any better ideas or ways to do this is much apreciated.
Update: Just realized it's the same problem with the decorators :( Don't think this is the right path i've taken.
Create new form class extending Zend_Form and in the init() method use variable $ns to add prefix/suffix to all elements. You can set $ns variable through form constructor.
class Form_Test extends Zend_Form
{
protected $ns;
public function init()
{
$this->setAttrib('id', $this->ns . 'testForm');
$name = new Zend_Form_Element_Text('name');
$name->setAttrib('id', $this->ns . 'name');
$name->setLabel('Name: *')->setRequired(true);
$submit = new Zend_Form_Element_Submit('submit');
$submit->setAttrib('id', $this->ns . 'submitbutton');
$submit->setLabel('Add')->setIgnore(true);
$this->addElements(array($name, $submit));
}
public function setNs($data)
{
$this->ns = $data;
}
}
In the controller or wherever you are calling this forms specify each form instance:
$form1 = new Form_Test(array('ns' => 'prefix1'));
$this->view->form1 = $form1;
$form2 = new Form_Test(array('ns' => 'prefix2'));
$this->view->form2 = $form2;
// Validation if calling from the controller
if ($form1->isValid()) ...
Using multiple instances of same forms on a page can be validated if used as subform.
SubForms prefix all id's with the name/identifier of the subform.