Route and Post parameter in the Yii2 with ajax - javascript

I call AJAX-request in my jQuery-script. I have created the controller (AjaxController) and method actionRequest inside this. JQuery sends this request:
jQuery.ajax({
url: location.origin + '/web/index.php?r=ajax/request',
async: false,
type: 'POST',
data: {'param': 32},
dataType: 'json',
success: function(data) {result = data}
});
and this code is in controller's script:
public function actionRequest() {
$param = Yii::$app->request->post('param');
echo json_encode($param);
}
Controller has accepts this option and send its back. JavaScript displays this option (result) with the help of "alert()". If I send it with GET methods, then all works correctly and window displays "32". But if I change this request into the "POST", window displays "undefined". If I send request to my PHP-script (path: /web/php), then all works correctly too. And if I will delete all code from my controller and leave only:
<?php
$param = $_POST['param'];
echo json_encode($param);
?>
and ask the script directly, then all works correctly too...
There are all signs which mean that error is in the framework, when I use AJAX-method POST. Because if I write this code inside my controller:
$root = $_SERVER['DOCUMENT_ROOT'];
$file = fopen($root . '/text.txt', 'w');
fwrite($file, '1');
fclose($file);
then I send GET-request, and code is completed and create file in the root directory on the site. But if I change request into the POST, then file is not created.
Source of my controller:
<?php
namespace app\controllers;
use Yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\filters\VerbFilter;
use app\models\LoginForm;
use app\models\ContactForm;
use yii\helpers\Html;
class AjaxController extends Controller {
public function behaviors() {
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['logout'],
'rules' => [
[
'actions' => ['logout'],
'allow' => true,
'roles' => ['#'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'logout' => ['post'],
],
],
];
}
public function actions() {
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
],
];
}
public function actionIndex() {
return '';
}
public function actionRequest() {
$param = Yii::$app->request->post('param');
echo json_encode($param);
}
}
?>
My UrlManager:
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'suffix' => '/',
'rules' => [
'' => 'site/index',
'register' => 'site/register'
]
]

Based on your UrlManager settings, try to change your ajax url to this instead.
url: location.origin + '/ajax/request/',
Just append the controller name, action name, and trailing forward slash.
The reasons are
you set enablePrettyUrl to true
showScriptName to false
suffix to /
I have tested this and it works for both GET and POST type.
Just remember to change the function to obtain GET and POST param accordingly.

Related

JSON Decode not receiving data

I am trying to use the Stripe API to create a payment form as detailed here:
https://stripe.com/docs/payments/integration-builder
I would like to send the amout (that the user is charged) from the front-end so have attempted to add it to the fetch request as shown below:
var purchase = {
//items: [{ id: "xl-tshirt", price: 400 }]
amount: 2000
};
// Disable the button until we have Stripe set up on the page
document.querySelector("button").disabled = true;
fetch("/create.php", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(purchase)
});
However the value (currently hardcoded at 2000) is not pulling through to the POST body successfully and the payment intent is failing. Below is the code I am using:
try {
// retrieve JSON from POST body
$json_str = file_get_contents('php://input');
$json_obj = json_decode($json_str, false);
$paymentIntent = \Stripe\PaymentIntent::create([
//'amount' => calculateOrderAmount($json_obj->items),
'amount' => $json_obj['amount'],
'currency' => 'usd',
]);
$output = [
'clientSecret' => $paymentIntent->client_secret,
];
echo json_encode($output);
} catch (Error $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
Any advice is much appreciated.
You are sending false here, that converts it into an object
$json_obj = json_decode($json_str, false);
Then you are trying to use it as an array here
'amount' => $json_obj['amount'],
Try using
'amount' => $json_obj->amount,
Or
$json_obj = json_decode($json_str, true);
without changing anything else.

how to pass object from view to controller

I have an object $trial that fills up while in the view. I would like to pass it to route as POST to call a function using it's data. This is to create an event for fullcalendar. I'm pretty sure that I've been sitting on this so long, that I'm way overthinking it.
Wanted to do this with eventRender callback but couldn't figure out how to pass the data into it, tried simple $.post just to get method not allowed or unknown status in the console.
I have some dummy data at the moment.
The goal here is to create event through marked time range.
Controller function to add new record to database if I manage to pass data
public function addEvent(Request $request)
{
//dd($request);
$event = new Event;
$event->title = $request['title'];
$event->start_date = $request['start_date'];
$event->end_date = $request['end_date'];
$event->save();
\Session::flash('success','Event added successfully.');
return redirect('/events');
}
web.php (routing)
Route::get('/events', 'EventController#index');
Route::post('/events', 'EventController#addEvent');
and then there is the index function on which I work the most currently to modify the table (make it editable, etc).
public $trial = [];
//
public function index()
{
$events = [];
$data = Event::all();
if($data->count()) {
foreach ($data as $key => $value) {
$events[] = Calendar::event(
$value->title,
true,
new \DateTime($value->start_date),
new \DateTime($value->end_date.' +1 day'),
null,
// Add color and link on event
[
'color' => '#f05050',
'url' => '/events',
]
);
}
}
$calendar = Calendar::addEvents($events) //add an array with addEvents
//->addEvent($eloquentEvent, [ //set custom color fo this event
//'color' => '#800',
//])
->setOptions([ //set fullcalendar options
'firstDay' => 1,
'selectable' => true,
'unselectAuto' => false,
'selectOverlap' => false,
'editable' => true,
'businessHours' => [
'dow' => [ 1, 2, 3, 4, 5 ],
'start'=> '08:00',
'end' => '19:00',
]
])->setCallbacks([ //set fullcalendar callback options (will not be JSON encoded)
'eventClick' => 'function(event) {
console.log("You clicked on an event with a title of: " + event.title);
}',
'select' => 'function(start, end) {
console.log("Selection start: " + start.format() + " selection end: " + end.format());
$trial = {
title: "rent",
start_date: start.format(),
end_date: end.format()
};
console.log($trial);
}',
]);
any suggestions would be welcome.
EDIT: generally, pretty much the only way i know how to pass data to be used in functions in controller is submitting it through form
I don't use Angular specifically, but it's got many similarities with Vue. I use a platform-agnostic package called Axios which allows you to send requests to your server.
To backtrack a second, a form is basically a general way to send a post request. The url is specified in the form, and the values are the input fields.
With any package, you would do something similar:
specify the url
specify the request type (post, get, etc)
pass parameters (but not required)
With Axios, it would look something like:
axios.post('/mySite.com/something', { datum1: 'value', datum2: true })
.then(response => () {
this.someFunction(response.data);
});
This has some ES6 magic in it (arrow functions), but this is very similar to many requests. Like I said, the second parameter is optional, or you can even pass a data object.
Don't forget to also include the csrf token. This is easiest if you just add in as a meta tag in your page head (see the Laravel Docs), but you can also pass it in directly as a _csrf parameter.

Pusher receives no events

i am using laravel with pusher to send an event message to pusher. the code is in my controller which is a post controller, triggered when an input form is submitted. below is my code. what am i doing wrong? there is no event received.
this is an ajax call route based controller.
$pusher = new Pusher( env('PUSHER_KEY'), env('PUSHER_SECRET'), env('PUSHER_APP_ID'), array( 'encrypted' => true ) );
$pusher->trigger( 'test_channel', 'my_event', 'hello world' );
I am also assuming you have set up your Pusher account correctly and that your environment variables are correct.
If so, you may need to ensure you are using the correct Cluster (the default is fine for the US, but outside the East coast of the US for example, the cluster must be explicitly defined).
Update:
Controller code:
<?php
namespace App\Http\Controllers;
use Vinkla\Pusher\Facades\Pusher;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
class TestPusherController extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
public function test(){
$arr = array('test' => 'hello world 2') ;
$pusher = new Pusher( env('PUSHER_KEY'), env('PUSHER_SECRET'), env('PUSHER_APP_ID'), array( 'encrypted' => true, 'cluster' => 'ap1' ) );
$pusher::trigger( 'test_channel', 'my_event', $arr);
return $arr;
}
public function shortenedTest(){
$message = 'Hello world';
Pusher::trigger('my-channel', 'my-event', ['message' => $message]);
}
}
In web routes:
Route::get('testPusherController', 'TestPusherController#test');
Route::get('shortenedTestPusherController', 'TestPusherController#shortenedTest');
I have got this working on a fresh install of vinkla/pusher following the setup steps in https://github.com/vinkla/laravel-pusher, on Laravel 5.3, using the built in PHP server and Connecting to the EU server (I do not have any Pusher apps using ap1 at this time).
You will notice a small number of changes to the coding in the controller to get the correct format. You must 'use' the Pusher facade above the controller.
For completeness, I have added a neater way of working with this where you can set the Pusher credentials in the Config/pusher.php file without the need to setup the connection for each use. This can be seen in the shortenedTest() method on the controller.
<?php
return [
'connections' => [
'main' => [
'auth_key' => env('PUSHER_KEY'),
'secret' => env('PUSHER_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_CLUSTER')
],
'host' => null,
'port' => null,
'timeout' => null,
],
'alternative' => [
'auth_key' => 'your-auth-key',
'secret' => 'your-secret',
'app_id' => 'your-app-id',
'options' => [],
'host' => null,
'port' => null,
'timeout' => null,
],
],
];

A Mystery in my Code

I have fixed the issue with ajax on typo3. Now comes another issue in hand. This time is related to returning the object found in the action to Javascript. So I tell you what I do exactly.
here is first my ajax call function:
function getContent(id)
{
console.log("Starting process ...");
$.ajax({
async: 'true',
url: 'index.php',
type: 'POST',
data: {
eID: "ajaxDispatcher",
request: {
pluginName: 'listapp',
controller: 'Pays',
action: 'getMyCompanys',
arguments: {
'id': id
}
}
},
dataType: "json",
success: function(result) {
var data = JSON.parse(result)
console.log(data.nom);
},
error: function(error) {
console.log(error);
}
});
}
and here is the "getMyCompanys" action:
public function getMyCompanysAction()
{
$argument = $this->request->getArguments('id');
$company = $this->entrepriseRepository->findBypays(2); // just to test with the id 2
$result = (array) $company;
return json_encode($result);
}
Now on the console I get this Output:
{"\u0000*\u0000dataMapper":{},"\u0000*\u0000persistenceManager":{},"\u0000*\u0000query":{},"\u0000*\u0000queryResult":null}
So at the first glance , i thought the object was not passed to the javascript , but when I tried to add an attribute to the object $company ($company->att = 'val';)
I get this output :
{"\u0000*\u0000dataMapper":{},"\u0000*\u0000persistenceManager":{},"\u0000*\u0000query":{},"\u0000*\u0000queryResult":null**,"att":"val"}**
notice that the added attribute and its value get showed
so I now I'm convinced that there is something wrong with the object. I am also 100 % sure that the method findBypays(2); is working, I tried it in a listAction.
I don't really know what could be the problem :/
Just for your Information: this is part of an extension in typo3 that I developed myself using JBuilder
well after struggling for a long time , I tried a trick and it worked and it proved me that the json_encode does not return an object in typo3 eventhough I applied the :
$object->toArray() or (array) $object
to the object , so my solution was :
$companies = array();
foreach ($this->adresseRepository->findByland($id) as $company)
{
$companies[] = array(
'uid' => $company->getUid(),
'firma' => $company->getFirma(),
'strasse' => $company->getStrasse(),
'plz' => $company->getPlz(),
'ort' => $company->getOrt(),
'land' => $company->getLand(),
'telefon' => $company->getTelefon(),
'email' => $company->getEmail(),
'webseite' => $company->getWebseite()
);
}
return json_encode($companies);
and It Worked ;)

Triggering different actions on same route based on request type in Zend Framework 2

I am trying to make ZF2 respond in REST way to different request type.
In my module.config.php I have this router config.
'router' => array(
'routes' => array(
'student' => array(
'type' => 'segment',
'options' => array(
'route' => '/student[/:action][/:id]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Student\Controller\Student',
'action' => 'index',
),
),
),
),
),
On frontend I am using Backbone to send GET, POST, DELETE requests to server based on user interactions.
When user trigger action to delete student with id of n, backbone will send /somePath/student/n using DELETE request.
When user trigger action to get student with id of n, backbone will send /somePath/student/n using GET request.
If I want current setup to work I have to change Backbone request and change URL from student/n to student/delete/n if I want to delete student with that id and similarly for GET.
This is what I did on client side which I would like to avoid.
define(['backbone'], function(Backbone){
return Backbone.Model.extend({
defaults:{
//set default model values
},
initialize: function(){
//initialize
},
methodToURL: {
'delete': '/student/delete'
},
sync: function(method, model, options) {
options = options || {};
var id = arguments[1]['id']
options.url = model.methodToURL[method.toLowerCase()] + '/' + id;
return Backbone.sync.apply(this, arguments);
}
});
});
In controller on server side I have different action methods I would like to run for different request types.
public function deleteAction()
{
//some code
}
public function getAction()
{
//some code
}
I don't want to change default backbone behaviour (intercepting and altering requests).
Is there a way to configure ZF2 router to use same route but trigger different actions based on request method type?
You can use Method route as child route of segment route. http://framework.zend.com/manual/2.3/en/modules/zend.mvc.routing.html
There is a example named A complex example with child routes where author for blog literal route create child routes each of which is different type.
Simply You can create child routes in your student route which will be type Method for each method You want to use and then change only action for this method types.
'router' => array(
'routes' => array(
'student' => array(
'type' => 'segment',
'options' => array(
'route' => '/student[/:action][/:id]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Student\Controller\Student',
'action' => 'index',
),
),
'may_terminate' => true,
'child_routes' => array(
'delete' => array(
'type' => 'method',
'options' => array(
'verb' => 'delete',
'defaults' => array(
'action' => 'delete'
),
),
),
'put' => array(
'type' => 'method',
'options' => array(
'verb' => 'put',
'defaults' => array(
'action' => 'put'
),
),
),//and so on...
),
),
),
),
You can use the Zend\Mvc\Controller\AbstractRestfulController
It will call Certain function depending on the HTTP request method
class StudentRest extends AbstractRestfulController {
/**
* will be called with a GET request when it detects that route
* matches paramater id has been set
*/
public function get($id) {
/*load studentdata*/
return new JsonModel($data);
}
/**
* Will be called on a POST request, and data should be sent with a $_POST
* This should create a new object/row in db and return 201
*/
public function Create($data) {
/*create student*/
$this->response->setStatusCode(201); // if new student was created.
return JsonModel();
}
/**
* Will be called on a PUT request. Data should be sent via $_POST
* It also requires that the $id parameter is set in the route match
*/
public function Update($id, $data) {}
/**
* Will be called on a DELETE request, It requires that the $id parameter is set in the route match
*/
public function Delete($id) {}
}
with that u can just link to the controller via the route as so
'student' => array(
'type' => 'segment',
'options' => array(
'route' => '/student[/:id]',
'constraints' => array(
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Student\Controller\StudentRest ',
),
),
),
there are also some function for getList which will be called when not suppling $id in route parameters.
Also might be worth saying that the default implementation of all the functions returns a 405 with a ['content' => 'Method Not Allowed']
Documentation how to use it
As per the documentation :
http://framework.zend.com/manual/1.12/en/zend.controller.request.html
Determining the Request Method
getMethod() allows you to determine the HTTP request method used to request the current resource. Additionally, a variety of methods exist that allow you to get boolean responses when asking if a specific type of request has been made:
isGet()
isPost()
isPut()
isDelete()
isHead()
isOptions()
The primary use case for these is for creating RESTful MVC architectures.

Categories