Symfony2 | Why controller action executes twice? - php

Its been few days I'm facing a weird behavior with Symfony.
I have an action in which for some reason I need to store a random value as form nonce in the session. The nonce is passed to the twig template to be used by an ajax function.
While sending nonce to the corresponding action a difference nonce value is examined and therefore the request rejects.
Tests showed that the action is executed twice by Symfony hence a new nonce will be stored without updating the front-end. I couldn't determine the reason.
After hundreds of tests I figured out that a minor change in the route could fix the problem but I'm not convinced this is the ultimate solution and I couldn't find the root cause.
Anyone can help?
here is the problematic code:
/**
*
* Condo Apartments management
*
* #Route("/condo/apartment")
*/
class ApartmentController extends Controller
{
/**
* Index Condo Apartments
*
* #Route("/edit/{id}/{apartment}", name="edit_apartment")
* #Route("/manage/{id}", name="manage_apartments")
* #ParamConverter("apartment", class="RimakishCondominiumBundle:Apartment", options={"mapping":{"apartment"="id"}})
* #Method({"GET", "POST"})
*/
public function indexApartmentsAction( Request $request, Complex $complex, Apartment $apartment=null){
$session = $request->getSession();
$nonce = sha1(uniqid());
if($session->has('nonce')){
$session->remove('nonce');
}
$session->set('nonce', $nonce);
I just changed the first route as follows and it worked. Now I need to know the root cause of this issue.
* #Route("/{id}/{apartment}/edit", name="edit_apartment")

I had a similar problem a few days ago, and as Richard said, the problem was to be found in the js part of my app.
In my case, I'm using the on() jquery method because of the dynamic content in my page, and event handlers were "accumulating" in some specific circumstances.
I had to use the off() method and this solved my multiple ajax calls.
My code looks like this:
// A function executed when clicking a button with class ".modal-form"
$('body').on('click', '.modal-form', function (e) {
//...(loading a form in a modal)
//calling a function that initializes my form
initializeForm();
//...
});
To solve my problem I had to add $( "body" ).off( "submit", "**" ); in my function initializeForm to clear all event handlers attached to the element with id "my-form"
function initializeForm(){
$( "body" ).off( "submit", "**" ); // I had to add this to avoid sending multiple ajax
//requests in case the button with ".modal-form"
//class has been clicked several times
$('body').on('submit', '#my-form', function(e) {
//...
$.ajax({
// That request was being sent several times
}
});
});
}

Related

How to enable my CodeIgniter controller to correctly identify ajax calls from FullCalendar4?

I am using FullCalendar 4.4.0 with a CodeIgniter application that I am working on.
In many places in my project, I am successfully using CI's $this->input->is_ajax_request() method to determine whether my controller is being loaded by an ajax request or not.
The problem is that when I am using FullCalendar's single-source events option and passing a POST request, the is_ajax_request() call is not identifying the call as coming from ajax.
What can I do to enable my controller to correctly identify ajax calls?
One of the major breaking changes from FullCalendar version 3 to version 4 is
Removal of jQuery as a dependency.
This is relevant because consequently:
X-Requested-With is set only by AJAX libraries like jQuery, Mootools, Prototype etc.
according to #InsiteFX
CodeIgniter's is_ajax_request() source code looks like this:
/ci/system/core/input.php:
/**
* Is AJAX request?
*
* Test to see if a request contains the HTTP_X_REQUESTED_WITH header.
*
* #return bool
*/
public function is_ajax_request()
{
return ( ! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
}
This means a solution can be found a couple of ways...
Hack FullCalendar's /core/main.js file and add:
Add the following line immediately after the xhr.open() call in the requestJson() method:
xhr.setRequestHeader('X-Requested-With', 'xmlhttprequest'); /* Food for CI's $this->input->is_ajax_request() */
If you are never expecting any $_POST values with a standard page load (this is true of my application), simply do not use is_ajax_request() for this specific task. In your controller, just use this instead:
if (!empty($_POST)) {
or more simply (because the superglobal will always be declared)
if ($_POST) {

Dusk does not handle POST requests the same way Laravel does when testing a form submission

When running tests in Dusk, submitting a form generates a validation error that reads "The query field is required." The error does not occur when testing the page with manual input.
I added dd( $request ) to the first line of the controller method that handles the POST request. When I test the page manually, the system dumps the request to the page. When I test the page with Dusk, I get a screenshot that shows the line of code was never executed: The page reloads with the validation error.
I have searched the form for a hidden input with a name of 'query.' It does not exist.
I have searched the controller class and base classes for any validations that test the 'query' input. I have found none.
Can anyone point me in the right direction to figure out why the page does not work in the automated testing environment, when it does work using the serve command?
Has anyone seen a similar error in the past?
Short Answer: Check the page to verify that the selector is grabbing the correct form. In this case, the tester forgot that a form existed in the menu bar. The test was clicking the button in the menu bar instead of in the main page content.
Original Text:
I guess sometimes you just need to walk away and come back to the problem. I was so focused on the form at the center of the page that I overlooked the form in the menu bar that has an input with the name 'query'.
I was clicking the wrong button with my Dusk commands, because my selector applied to multiple buttons on separate forms.
For example, we can take as Post Model With PostController.
Your store function may look like
public function store(Request $request)
{
Post::create($request->all());
return redirect()->route('post.index')->with('success','PostCreated Successfully');
}
If you add dd function at the begining of the function it will work ie) dd($request->all());
But if you use custom requests, for example PostStoreRequest
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class PostStoreRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'post_name' => 'required',
];
}
/**
* Custom message for validation
*
* #return array
*/
public function messages()
{
return [
'post_name.required' => 'Enter Post Name',
];
}
}
and the PostController#store
public function store(PostStoreRequest $request)
{
Post::create($request->all());
return redirect()->route('post.index')->with('success','PostCreated Successfully');
}
Even though if you add dd at the top of function because it validated the request first and it will enter into the function.

TYPO3 extension createAction nothing happens

I am currently trying to write a simple input form extension: the user enters the input field values, the submit action inserts the values into the database and then redirects to an external payment service.
Unfortunately, the createAction function does not show any reaction after a click on the submit button.
For test purposes, I just want to output a text after the submit. But not even that works.
If I use the exact same function of flashMessageContainer in the newAction, it works: the message is displayed immediately. But when I want to show it after a click on the submit button, nothing but a page reload happens.
What could be the problem?
Resources / Private / Templates / Payment / New.html:
<f:form method="post" controller="Payment" action="create" id="newPayment" name="newPayment" object="{newPayment}">
<f:render partial="Payment/FormFields" />
<div class="buttons row">
<div class="col c6">
<f:form.submit value="{f:translate(key:'tx_chilipayment_domain_model_payment.submit')}" />
</div>
</div>
......
Classes / Controller / PaymentController.php:
<?php
namespace chilischarf\ChiliPayment\Controller;
class PaymentController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {
/**
* paymentRepository
*
* #var \chilischarf\ChiliPayment\Domain\Repository\PaymentRepository
* #inject
*/
protected $paymentRepository;
/**
* action new
*
* #param \chilischarf\ChiliPayment\Domain\Model\Payment $newPayment
* #dontvalidate $newPayment
* #return void
*/
public function newAction(\chilischarf\ChiliPayment\Domain\Model\Payment $newPayment = NULL) {
$this -> view -> assign('newPayment', $newPayment);
}
/**
* action create
*
* #param \chilischarf\ChiliPayment\Domain\Model\Payment $newPayment
* #return void
*/
public function createAction(\chilischarf\ChiliPayment\Domain\Model\Payment $newPayment) {
$this -> flashMessageContainer -> add('Your new Payment was created.');
}
}
?>
Usually you dont want your createAction to render anything. You just want it to validate and persist the user input and then redirect to another action, where e.g. a flash message is rendered. That being said, the problem you describe can have several causes, so I will point to a few issues you might have or your problem may be related to:
Do you have a Create.html Template in Resources/Private/Templates/Payment/Create.html? This is the template the create action will render.
Do you have a <f:flashMessages /> viewHelper in this template? Since you dont assgin anything to your view in your createAction (which is perfectly fine if you dont plan to render anything here, as mentioned above) this is he only "dynamically" created content.
Do you reach your createAction after submitting your form? Or is something with the validation going wrong (in that case you will be redirected back to your newAction with a default flashMessage that an error occured while trying to call createAction)? You can figure that out, when you add a die(); to your createAction. After submiting you'll see a white page if your creatAction was called successfully.
Is 'create' a valid action for your controller as configured in your ext_localconf.php? If not, add it.

How do I add a field to a Symfony2 form on preBind event?

I need to modify a form on preBind (using Symfony 2.2).
Here is a segment of my preBind function in my EventSubscriber:
public function preBind(FormEvent $event)
{
$form = $event->getForm();
$form->get('locationType')->setData('default');
}
However, when I submit the form, this value is not being saved. Am I missing a step? Do I need to call setData on the FormEvent object itself in order to propagate the new data?
Something that would really help me figure this out is to see the protected function customizeForm($form, $positions) completed in the example at the bottom of this page:
http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html
As cheesemacfly suggested, I ended up changing this data within the controller rather than trying to do it within the event subscriber.

Use the Zend Framework to remember the last clicked position

I want to add a feature to an existing website thats built using the Zend Framework. One of the problems with this website is that it refreshes the page often at different positions on the web page. What I would like to do is use a built-in Zend framework function to keep the same position in a web page after it has been refreshed.
An example would be: A user clicks at the bottom of the page, the same page is reloaded but it does not reload at the top of the page, it stays where the user last clicked.
Is there such a thing?
Thank you
You will have to use a combination of Javascript (in the frontend) and Zend Framework (in the backend) to achieve this.
This is assuming you are using user cookies/sessions.
Create an event handler to detect mouse-clicks using Javascript anywhere on the page. In the event handler, use the event object to get the x and y-coordinates of the mouse position. (Mouse click event coordinates)
Set up an endpoint in your Zend Framework that will accept mouse coordinates for the user. For example: http://localhost/user/updateCoordinates?x=12&y=100
On mouse click, make your event handler perform an AJAX query sending the coordinates to the endpoint you set up in #2.
In Zend Framework, store these coordinates in the user session/cookie (so you can track it later)
On page load, get the value from the session and output Javascript code to automatically scroll to that coordinate. Also, remove the x and y-coordinate from the cookie since it has already been used.
You could use some javascript to record the scroll position of the window right before a refresh, and just save it to a cookie.
Then, when the next page loads, check for the cookie, scroll to its position, and delete it. This won't work well if the webpage has dynamically sized elements(example: an expanding accordion menu).
Personally though, I would try to avoid this. Every now and then I visit some website that likes to do stuff with my scroll bar and try to help me. Way too often they get it wrong and the webpage becomes unusable. Maybe you can just dynamically update the page(ajax?) instead of doing a real reload.
u can use existing components of ZF and combine them in some new plugin:
1) new ZF plugin saved in ZF structure in my case: /library/My/Controller/Plugin/ActionHistory.php
final class My_Controller_Plugin_ActionHistory extends Zend_Controller_Plugin_Abstract
{
/** #var bool flag indicating whether current action has been already logged */
private $dispatched = false;
/**
* #param Zend_Controller_Request_Abstract $request
* #return void
*/
public function pushStack(Zend_Controller_Request_Abstract $request) {
$storage = $this->getStorage();
$storage->stack[] = $request;
// limit the depth of the stack
if (count($storage->stack) > $this->getMaxDepth()) {
array_shift($storage->stack);
}
// mark current action as dispatched (influences getBacklink())
$this->dispatched = true;
}
public function popStack() {
$storage = $this->getStorage();
return array_pop($storage->stack);
}
/**
* Returns request to previous action (not current).
*
* #return Zend_Controller_Request_Abstract|null
*/
public function getBacklink() {
$storage = $this->getStorage();
$depth = count($storage->stack);
// points to top of the stack
$backlinkIndex = $depth - 1;
if ($this->dispatched) {
// current action has been already logged, "backlink" is second from the top
--$backlinkIndex;
}
return ($backlinkIndex >= 0 ? $storage->stack[$backlinkIndex] : null);
}
/**
* Returns stack with performed actions
* #return array
*/
public function getStack() {
return $this->getStorage()->stack;
}
/**
* #return Zend_Session_Namespace
*/
private function getStorage() {
static $storage = null;
if ($storage === null) {
$storage = new Zend_Session_Namespace(get_class($this), true);
if (!is_array($storage->stack)) {
// initialize stack if needed
$storage->stack = array();
}
}
return $storage;
}
/**
* Returns maximal depth of the action history
* #return int
*/
private function getMaxDepth() {
return 3;
}
}
2) Place somewhere this code to pushing history in stack. You can use history only e.g. with cooperation with logging to application or you can save all history. It depends on you:
// this code is example of usage in another ZF controller plugin class
$actionHistory = Zend_Controller_Action_HelperBroker::getStaticHelper("ActionHistory");
if ($actionHistory) {
// $this => instance of Zend_Controller_Plugin_Abstract
$actionHistory->pushStack($this->getRequest());
}
3) Then u can use this plugin in some controller where you want to handle action history like that:
// check if there is an backlink stored in actionStack, if yes than use it
if (($actionHistory = $this->getHelper('actionHistory'))) {
$request = $actionHistory->getBacklink();
if ($request) {
$actionHistory->popStack();
// use any of your redirect classes or simle redirector helper
// right path to previous site is accesible by $request->getUserParams()
$this->redirect($request->getUserParams());
}
}
Thats it:) Hope you can modify what you want but the theory and benefits of this solution are clear enought...

Categories