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.
Related
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.
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
}
});
});
}
the adutomatic crud operation generated by symfony and also the symfony demo application has the following code structure for the delete action
/**
* Deletes a testing entity.
*
* #Route("/{id}", name="testing_delete")
* #Method("DELETE")
*/
public function deleteAction(Request $request, testing $testing)
{
$form = $this->createDeleteForm($testing);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->remove($testing);
$em->flush();
}
return $this->redirectToRoute('testing_index');
}
/**
* Creates a form to delete a testing entity.
*
* #param testing $testing The testing entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm(testing $testing)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('testing_delete', array('id' => $testing->getId())))
->setMethod('DELETE')
->getForm()
;
}
my question is why do we need a form to delete? cant we just have a link in the twig with an id parameter set accordingly, cant we just do the following, why do we need to check if the entity isValid() inside a form before deleteing it?
/**
* test delete
* #Route("/{id}", name="testing_delete")
* #Method("DELETE")
*/
public function deleteAction(testing $testing) {
$em = $this->getDoctrine()->getManager();
$em->remove($testing);
$em->flush();
return $this->redirectToRoute('testing_showall');
}
If you used link for delete with id, it's possible to robot can delete you data with looping.
In Symfony action check "DELETE" method as well as if your crsf token verify with method isValid "$form->isValid()"
That's security reason it's create form and validate
Not using a simple link to delete data denotes to the concept of safe methods in HTTP (if you had just a simple link, you would have to send a GET request to the URL):
Some of the methods (for example, HEAD, GET, OPTIONS and TRACE) are, by convention, defined as safe, which means they are intended only for information retrieval and should not change the state of the server. In other words, they should not have side effects [...]
I think it's important to write a word about CSRF.
By using a Symfony form, it creates a CSRF token that ensure the user who deletes the entity is the same user who wanted it.
If there was no form and only a link /{id}, it would be possible by using a bad link in a mail, or an XSS attack, to make someone else sending the request to delete an entity.
If Bob uses an XSS breach or something else to make Alice (the admin) sending a request for deleting an entity, the request is sent by Alice, event if it's an attack from Bob. So, Bob hasn't the rights for this request but he used the session of Alice, who has the rights. The entity is deleted.
To protect against CSRF attacks, using a CSRF token is really important. Symfony's Form includes it automatically, and check if in isValid().
I'm using annotation to set my routes and method types. Is there a way to only allow certain types of post data. Currently I'm doing the following:
/**
* #Route("/myurl", requirements={"varID" = "\d+"} )
* #Method({"POST"})
* #Template()
*/
But if a varID gets submitted with a string value then it goes through anyway... I'm guessing due partly to there being no {varID} in the route? Is there a way to validate POST data like this in Symfony?
Change annotation into this:
/**
* #Route("/myurl/{varID}", requirements={"varID" = "\d+"} )
* #Method({"POST"})
* #Template()
*/
You must tell symfony wich part of url is yours varID variable to allow engine to check datatype. Than you get an exception:
No route found for "GET /myurl/somestring"
404 Not Found - NotFoundHttpException
1 linked Exception:
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...