I'm having problems with callback on empty fields.
For some reason i need to compare value of field with some other parameters and to make it "required" only if some other conditions are fulfilled buy checking it from callback function.
Unfortunately, if field is empty, callback function is not called.
DO you have any idea how to overcome this issue?
This "bug" is actually a "feature" of the Form Validation. If the input is empty and the required rule is not set, then it skips all other rules (even non-callback functions). You can extend (or hack) the validation to continue through all functions even if the input is blank.
In system/libraries/Form_validation.php at about line 486, you will see the comment:
// If the field is blank, but NOT required, no further tests are necessary
You will then see two lines with a return; shortly after that comment. The lines for me are at line 498 and line 536 (about 12 and 50 lines after the comment). If you comment out those two lines, then the validation will continue through all function even if the input is blank.
That being said, you can extend the Form Validation by creating a file at application/libraries/Form_validation.php and overriding the _execute function:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
class MY_Form_validation extends CI_Form_validation
{
protected function _execute($row, $rules, $postdata = NULL, $cycles = 0)
{
...
}
}
Just copy the entire function to the new file and comment out the two returns described above.
you can define validation rule like this
you can set with a condition
if($this->input->post('first_field_name')){
$this->form_validation->set_rules('2ndfield','Field Name','required|callback_myfunction');
}
Related
Am running a form using fluid with two fields, username and password. When first run, correct data is sent to the action. When different data is resubmitted, original data is sent to the controller action instead of the new values. How do I solve this problem?
EDIT: I've discovered that an array containing the user-submitted form fields and their values are obtained from the request, serialized as is and the string stored in a hidden input field called '__referrer[arguments]', which is then submitted with the form back to the user. When the user resubmits the form again with new values, the user doesn't realize that the old values are in the form, in the form of a serialized string, which is submitted together with the new values. Turns out, this is ok if no errors are reported by the validators. In that case the data is simply passed to the controller action and processing continues. But if errors are collected, processing is not sent to the controller action but is sent to the error action. The error action unserializes the old data and forwards that data (instead of the the new values) to the intended action controller. See \TYPO3\CMS\Extbase\Mvc\Controller\ActionController::forwardToReferringRequest().
EDIT: Steps to reproduce;
Create an action with one argument and create a form with one input element and a submit button. Create a validator for the element. Make it a string property. When all is said and done, start by sending a VALID value and let the form return. it will come back with no errors. but also if you look at it's hidden values, you find that the __referrer[arguments] has changes. That's because your previous values were serialized and are there. Now submit an INVALID value and check values entering your action. You'll be stunned they are the old ones.
This is weird. Currently if I disable the production of the __referrer[arguments] input field in the \TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::renderHiddenReferrerFields() method, everything works properly. How do I solve this? Please help.
Where things go wrong:
Here: \TYPO3\CMS\Extbase\Mvc\Controller\ActionController::forwardToReferringRequest() . In this method, arguments are sought from __referrer internal arguments instead of the submitted values. Normally, if errors are found, only two elements are found in the arguments form variable: controller for controller name and action for action name. This is because they were submitted originally with the form and they will recycle without changing as long as the validator finds errors. Nothing else will be added to it. This is good... until it validates. When it validates, results are sent directly to the action controller and not through the error controller. When the process goes back to the form, the object or arguments submitted will be added to the form and returned to the user. Remember it validated. When you then send a wrong value next, it goes to the error controller, but the old values (that validated) and were serialized in the __referrer[arguments] are unserialized and forwarded to your action. And that's how you end up with old values in your action. This is because it assumes that the variable carries only two values inside it; the controller and action names only. It's wrong.
Assumption:
Form post values processing seem to be built under the presumption that once a form validates, you won't need it again.
News.
The values you send are sought from extbase arguments and serialized in \TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::renderHiddenReferrerFields() method, added to the form just before presenting it to you. All TYPO3 has to do is skip your values and let the default values only be serialized. Just saw that the doc section of the function has this:
/**
* Renders hidden form fields for referrer information about
* the current controller and action.
*
* #return string Hidden fields with referrer information
* #todo filter out referrer information that is equal to the target (e.g. same packageKey)
*/
The #todo part is the news. Hope it's done in the next-patch since there are many scenarios where you want to resume the form such as in data entry.
Solution
Easiest solution: Initialize the empty argument in your action and hand the argument to the form. In my case, I use a DTO.
Example
public function accountLoginAction(DTO\Account\LoginDTO $accountLogin=null):ResponseInterface
{
if($accountLogin){
$this->repository->logon();
...
} else{
$accountLogin=DTO\Account\LoginDTO::getNewInstance();
}
$this->view->assign('object', $accountLogin);
return $this->response();
}
Hope it helps someone.
I need to make some changes to an SAPScript. I have the program and form name
Program: RBOSORDER01
Form: RBOSORDER02
I am looking to change some of the data shown in the form. I have debugged the program and I get see the call to write to the form, for example:
CALL FUNCTION 'WRITE_FORM'
EXPORTING
ELEMENT = 'ITEM_TEXT'
EXCEPTIONS
ELEMENT = 1
WINDOW = 2.
But how is the data passed between the program and the form. I cannot link between each. I was expecting to see a structure or a data element passed with 'ITEM_TEXT' and then this data is printed at this element "ITEM_TEXT" in the form but the link is not clear to me.
I have looked at the form also in SE71 and cannot see where you define this. Where is the link here, what am I missing?
This is in the form, so SE71 is what you need. You have to find the window first, where this element (ITEM_TEXT) is displayed, than look for the element and see what is displayed inside. The SAPSript form uses the global variables (structures, internal tables) of the print program directly by default (there are some other options as well, INCLUDE texts for example). So for example if a global variable gv_text is declared in the print program, and it is displayed in the SAPScript, than it will look like &GV_TEXT& in the form.
You can also debug the SAPScript if you switch on debugging in SE71 (can be painful, if the form is big).
Function 'WRITE_FORM' just calls the EntryPoint of the Form (SE71 / RBOSORDER02) in this case with ELEMENT='ITEM_TEXT'.
So you will end up in MAIN-Window at:
/E ITEM_TEXT
/: INCLUDE &VBDPA-TDNAME& OBJECT VBBP ID 0001 PARAGRAPH IT
In this case you have to debug what "VBDPA-TDNAME" is at this time and then you will find its value with transaction "SO10" (Standard-Text)
The INCLUDE can be a complex text and can have its own format strings.
As Jozsef said before, VBDPA-TDNAME is defined global in the print programm. (SE38n / RBOSORDER01)
This is an odd one. I have this method, that is the callback for the FormEvents::POST_SET_DATA event in an EventSubscriber of a form in Symfony2:
public function preSetData(FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
$resource_provider = $data->getResourceProvider();
$type = $resource_provider->getType(); //PROBLEM HERE
\Doctrine\Common\Util\Debug::dump(is_object($resource_provider));
}
It has more code but the error is firing in the line with the comment PROBLEM HERE.
The issue is that the dump there prints bool(true), so it is an object, and have the data inside, but trying to call getType(), that exist, fails with this error:
Error: Call to a member function getType() on a non-object
To add more confusion, if I dump $resource_provider->getType() I get the expected content dumped, but the error remains.
Does someone knows or has a clue on what can be happening here?
EDIT
Can't believe it! I found the answer, but doesn't makes sense at all! I had to wrap the problematic code with a conditional like this:
if(is_a($resource_provider, 'My\Bundle\Path\Entity\ResourceProvider')){}
and it worked! If someone can tell me what is the sense of that, I will happily select his/her answer as correct.
The problem is that $resource_provider is probably null as error says $resource_provider is not an object.
When printing the value of $resource_provider and skipping the script, it shows the right object of class 'My\Bundle\Path\Entity\ResourceProvider' :
echo get_class($resource_provider); exit; // Display My\Bundle\Path\Entity\ResourceProvider and skip the script
When adding a condition to test is the object is of the expected class, the script doesn't crash anymore :
if(is_a($resource_provider, 'My\Bundle\Path\Entity\ResourceProvider')){}
In addition, when skipping script when it's not an object, $resource_provider is null and the script skipped :
if(is_a($resource_provider, 'My\Bundle\Path\Entity\ResourceProvider')){
} else {
var_dump($resource_provider); exit;
}
$resource_provider is an object and null in the same "execution" of the script.
So we can deduce that the EventSubcriber is loaded more than one time, perhaps before the form binding and after form binding for example and the first time with the expected object, and a second (or more) time with no value, as null, perhaps because the $resource_provider is not filled when submitting form or something else.
I want to add two custom fields for newsletter subscriber at which date customer subscribed and its ip address:
1) I had added two columns in ‘newsletter_subscriber’ table.
How this can be achieved?
In the file app/code/core/Mage/Newsletter/Model/Subscriber.php
I found the the functions like:
$this->setSubscriberEmail($email);
$this->setStoreId(Mage::app()->getStore()->getId());
$this->setCustomerId(0);
But i did not found its code.
I think I also save data like that but how it will be possible ? where I have to define and declare code for the function like $this->seSubscriptionDate();
And how to display in Admin panel under Newsletter->Newsletter Subscriber ?
I had not find the solution or help from any person.
I bang my head and find the solution its very simple hope in future may some one’s time save by this post:
1) Add columns in the table “newsletter_subscriber” say in my case “subscription_date” and “sub_ip_address”
2) Add following two lines in the file # two places
app\code\core\Mage\Newsletter\Model\Subscriber.php
**$this->setSubscriptionDate(date("d-m-Y"));
**$this->setSubIpAddress($_SERVER['REMOTE_ADDR']);
One place: in the function: public function subscribe($email)
Before: $this->save();
Second place: in the function: public function subscribeCustomer($customer)
Before: $this->save();
Now data will be added in the table
Now to show in admin panel
1) Open the file app\code\core\Mage\Adminhtml\Block\Newsletter\Subscriber\Grid.php
Just add two required columns in it like
$this->addColumn('subscription_date', array(
'header' => Mage::helper('newsletter')->__('Subscription Date'),
'index' => 'subscription_date',
'default' => '----'
));
$this->addColumn('sub_ip_address', array(
'header' => Mage::helper('newsletter')->__('IP Address'),
'index' => 'sub_ip_address',
'default' => '----'
));
Now finish.
** This is the point where I spent my time and at the end I added this function on hit and trieal basis but it works.
Someone from Core magento team plz explain why this {setSubscriptionDate()}function work ? I had not declared anybody of this function.
It seems it shows intellisense to detect table field?
I also had the similar problem. However, in my case I am supposed to add a "country" and "gender" field in the newsletter form. So, on digging into the core for hours I figured this out. Below is the explaination:
In the app/design/frontend//default/template/newsletter/subscribe.phtml,
the form has getFormActionUrl() ?> in its action field. This referes to app/code/core/Mage/Newsletter/Block/Subscribe.php
The $this object used in the subscirbe.phtml file referes to this class (i.e. Mage_Newsletter_Block_Subscribe) and thus $this->getFormActionUrl() in the subscribe.phtml file refers to the function getFormActionUrl() of this class.
Now, this getFormActionUrl() is returning a method $this->getUrl('newsletter/subscriber/new', array('_secure' => true)) which belongs to its parent Mage_Core_Block_Template (I mean the method getURL()). This part is not improtant to us.
See the parameter passed to the getURL() function which is newsletter/subscriber/new
The first part "newsletter" is the module name (app/code/code/Mage/newsletter), the second part "subscriber" is the controller name (app/code/code/Mage/newsletter/controllers/SubscriberController) and the third part "new" is the method newAction in the controller SubscriberController.
Controller names are suffixed with Controller and function names are suffixed by Action. (Thanks to Phalcon framework to help understand this)
Now in the newAction() method you can see that by default, only the email is being posted
as
$email = (string) $this->getRequest()->getPost('email');
What I did was, I cloned the subscribe.phtml template with a name custom-newsletter.phtml and add two fields with name "country" and "gender". Then added the following lines in the newAction():
$country = (string) $this->getRequest()->getPost('country');
$gender = (string) $this->getRequest()->getPost('gender');
Now at line number 67 of the SubscriberController (inside the newAction() method) there is a code :
$status = Mage::getModel('newsletter/subscriber')->subscribe($email);
This line is calling the subscribe method of app/code/code/Mage/newsletter/Model/Subscriber.php and is passing the $email that is posted from newsletter form into it. I modified the line as:
$status = Mage::getModel('newsletter/subscriber')->subscribe($email,$country,$gender);
Now, I have to edit the app/code/code/Mage/newsletter/Model/Subscriber.php
When we talk about Models, we are talking about the database table the Model refers to. The model name is Subscriber and it belongs to module Newsletter so, the database table that this model affects is newsletter_subscriber
From this part on #Hassan Ali Sshahzad's question will be answered.
I added two new columns there with the name: subscriber_country and subscriber_gender.
Now, Magento' system automatically makes available a getter and a setter function to these columns with the following name:
function getSubscriberCountry(){}
function setSubscriberCountry($country_name){}
So, all I had to do in the model was:
Edit the method subscribe($email) to subscribe($email,$country,$gender)
add the following code in the subscribe($email,$country,$gender) function right before the try statement as follows:
$this->setSubscriberCountry($country);
$this->setSubscriberGender($gender);
try{
$this->save()
Hassan's method played a key role in my understanding of the MVC of Magento so its a return from my side.
How do I submit a form that can do two different things based on the URI?
For example, if the URI contains a string "new" the form will submit differently than it would if "new" were not in the URI.
I'm having trouble implementing this, as when a form is submitted, it takes the URI of whatever "form_open" says.
Altering the form_open path is probably not the way to do this. How are you using this? Does the person filling out the form affect the "new" string?
What I would do is put a hidden input on the form and set THAT value to "new". Then in the controller, use a GET to take the value of the input form, and do a simple IF / ELSE statement based off the value of that variable.
This way, you could setup several different ways to use the same form - hidden=new, hidden=old, hidden=brandnew, hiddend=reallyold could all process the form values differently, even sending them to different tables in your DB or whatever.
Kevin - I thought I'd done something like this before and I had - here's a quick look:
In routes.php:
$route['some/pathname/(:any)'] = "my_controller/my_function/$1";
Then in mycontroller.php:
function my_function($type)
{
if ($type == "new") {
do this }
elseif ($type == "update)" {
do this }
}