In my project I have a piece of my form that sends an AJAX request:
$.ajax({
url: '/bio_control/sample',
type: 'POST',
dataType: 'json',
data: {sample_number: $(input_field).val()},
});
Which activates the following controller method:
/**
* #Route("/bio_control/sample", name="get_bio_control_sample")
*/
public function getBioControlSampleAction(Request $request)
{
$sample_number = $request->request->get('sample_number');
/**
* Additional logic not shown for brevity.
*/
$user_id = $user->getId();
$response = array("code" => 100, "success" => true, "sample_number" => $sample_number, "sample_data" => $sample[0], "new_user" => false, "user_id" => $user_id);
return new JsonResponse($response);
}
I'd like to be able to test this request in isolation, but I'm unsure how to write the request object.
So far my first attempt:
public function testGetBioControlSample()
{
$helper = $this->helper;
$client = $this->makeClient();
$crawler = $client->request('POST', "/bio_control/sample", array(), array('sample_number' => 67655), array(
'CONTENT_TYPE' => 'application/json',
'HTTP_X-Requested-With' => 'XMLHttpRequest'
));
$this->assertStatusCode(200, $client);
}
Fails because it appears to be submitting the form (I get an error related to a form field completely unrelated to the AJAX request being blank).
Can anyone demonstrate how to correctly write such a test?
Does this URL need authentication?
I like to use LiipFunctionalTestBundle for my functional tests and they usually looks like this:
<?php
declare(strict_types=1);
namespace Tests\Your\Namespace;
use Liip\FunctionalTestBundle\Test\WebTestCase;
class PostResourceActionTest extends WebTestCase
{
public function testShouldReturnResponseWithOkStatusCode(): void
{
$credentials = [
'username' => 'user',
'password' => 'pass'
];
$client = $this->makeClient($credentials);
$payload = ['foo' => 'bar'];
$client->request(
'POST',
'/the/url/',
$payload,
[],
['HTTP_Content-Type' => 'application/json']
);
$this->assertStatusCode(200, $client);
}
}
Maybe the error you are getting is the login form asking for authentication?
The exact syntax I used to solve this problem was:
public function testGetBioControlSample()
{
$helper = $this->helper;
$client = $this->makeClient();
$crawler = $client->request(
'POST',
"/bio_control/sample",
array('sample_number' => 67655),
array(),
array('HTTP_Content-Type' => 'application/json')
);
$JSON_response = json_decode($client->getResponse()->getContent(), true);
$this->assertStatusCode(200, $client);
$this->assertNotEmpty($JSON_response);
$this->assertEquals($JSON_response["code"], 100);
$this->assertEquals($JSON_response["success"], true);
$this->assertEquals($JSON_response["sample_number"], 67655);
}
I believe I didn't need: 'HTTP_X-Requested-With' => 'XMLHttpRequest' in the final array argument.
Additionally, I had array('sample_number' => 67655) in the wrong argument.
Symfony 4.1 has a new way of testing AJAX requests:
// Before
$crawler = $client->request('GET', '/some/path', [], [], [
'HTTP_X-Requested-With' => 'XMLHttpRequest',
]);
// After
$crawler = $client->xmlHttpRequest('GET', '/some/path');
Related
I'm starting to work with Codeigniter 4 Shield.
I added this piece of code to my app/Config/Routes.php file.
$routes->get('/access/token', static function() {
$token = auth()->user()->generateAccessToken(service('request')->getVar('token_name'));
return json_encode(['token' => $token->raw_token]);
});
When I try to access the route in my web browser using the URL https://example.com/access/token, I obtain the error:
Call to a member function generateAccessToken() on null
produced by the line of code below:
$token = auth()->user()->generateAccessToken(service('request')->getVar('token_name'));
Background information:
I have installed Codeigniter 4 Shield using Composer, ran the respective database migrations, and everything else works fine.
My Codeigniter 4 Shield 'login' and 'register' pages work fine.
How can I load generateAccessToken() automatically in the app/Config/Routes.php file?
You need to submit the login credentials (email & password) along with your HTTP POST request to help identify the User requesting the access token. Otherwise, auth()->user() is empty, hence the error.
To generate an access token, you need to first authenticate the User.
For example: (Using email & password)
Define your 'access token' route. Notice the use of ->post(...) instead of ->get(...).
File: app/Config/Routes.php
$routes->post('auth/token', '\App\Controllers\Auth\LoginController::accessToken');
Define your Controller method that will handle the 'access token' generation. Read: Issuing the Tokens
File: app/Controllers/Auth/LoginController.php
<?php
namespace App\Controllers\Auth;
use App\Controllers\BaseController;
class LoginController extends BaseController
{
public function accessToken()
{
// Validate credentials
$rules = [
'email' => [
'label' => 'Auth.email',
'rules' => config('AuthSession')->emailValidationRules,
],
'password' => [
'label' => 'Auth.password',
'rules' => 'required',
],
];
if (!$this->validate($rules)) {
return $this->response
->setJSON(['errors' => $this->validator->getErrors()])
->setStatusCode(422);
}
if (auth()->loggedIn()) {
auth()->logout();
}
// Attempt to login
$result = auth()->attempt([
'email' => $this->request->getPost('email'),
'password' => $this->request->getPost('password')
]);
if (!$result->isOK()) {
return $this->response
->setJSON(['error' => $result->reason()])
->setStatusCode(401);
}
// Generate token and return to client
$token = auth()->user()->generateAccessToken($this->getDeviceName());
return $this->response
->setJSON(['token' => $token->raw_token]);
}
public function getDeviceName()
{
$agent = $this->request->getUserAgent();
if ($agent->isBrowser()) {
$currentAgent = $agent->getBrowser() . ' ' . $agent->getVersion();
} elseif ($agent->isRobot()) {
$currentAgent = $agent->getRobot();
} elseif ($agent->isMobile()) {
$currentAgent = $agent->getMobile();
} else {
$currentAgent = 'Unidentified User Agent';
}
return $agent->getPlatform() . " - " . $currentAgent;
}
}
Protect your /api routes using the $filters setting on app/Config/Filters.php. Read: Protecting Routes
Exclude your 'access token' ("auth/token") route together with all API routes ("api/*") from the global "session" & "toolbar" filters.
File: app/Config/Filters.php
<?php
// ...
class Filters extends BaseConfig
{
// ...
public array $globals = [
'before' => [
'session' => ['except' => [
"login*",
"register",
"auth/a/*",
"auth/token",
"api/*"
]],
],
'after' => [
'toolbar' => ['except' => ["auth/token", "api/*"]],
],
];
// ...
public array $filters = [
'tokens' => ['before' => ["api/*"]],
];
}
Make a one-time initial HTTP POST request to the auth/token route to receive the 'access token'. Upon receiving the token, store it with the client. I.e: in localStorage
$.ajax({
url: "https://your-site-domain.com/auth/token",
type: "POST",
data: {
"email": "USER-EMAIL-ADDRESS-HERE",
"password": "USER-PASSWORD-HERE",
},
success: function (response) {
window.localStorage.setItem('token', response.token);
},
error: function (jqXHR) {
console.log(jqXHR.responseText);
},
});
You may now send the received/stored access token using the Authorization header along with all your other protected API HTTP requests in your application without reauthenticating the user. i.e:
$.ajax({
url: "https://your-site-domain.com/api/rest/v1/employees",
type: "GET",
beforeSend: function (jqXHR) {
jqXHR.setRequestHeader(
"Authorization",
"Bearer " + window.localStorage.getItem('token')
);
},
data: {},
success: function (response) {
// Use the response here on success.
// I.e: listing all employees in a table.
},
error: function (jqXHR) {
console.log(jqXHR.responseText);
},
});
I am trying to use PayPal as a payment method on my website. I am using Laravel framework and GuzzleHttp package. Creating an order runs smoothly. Problem comes when trying to capture an order using id.
php artisan tinker
>>> $paypal = new App\Services\PayPalService;
=> App\Services\PayPalService {#3447}
>>> $paypal->createOrder(500.00, 'EUR');
=> {#3478
+"id": "3NK71944KB9544808",
+"status": "CREATED",
+"links": [
{#3476
+"href": "https://api.sandbox.paypal.com/v2/checkout/orders/3NK71944KB9544808",
+"rel": "self",
+"method": "GET",
},
{#3461
+"href": "https://www.sandbox.paypal.com/checkoutnow?token=3NK71944KB9544808",
+"rel": "approve",
+"method": "GET",
},
{#3472
+"href": "https://api.sandbox.paypal.com/v2/checkout/orders/3NK71944KB9544808",
+"rel": "update",
+"method": "PATCH",
},
{#3465
+"href": "https://api.sandbox.paypal.com/v2/checkout/orders/3NK71944KB9544808/capture",
+"rel": "capture",
+"method": "POST",
},
],
}
>>> $paypal->capture("3NK71944KB9544808");
TypeError: strtolower(): Argument #1 ($string) must be of type string, int given
Consuming API code:
public function makeRequest($method, $requestURL, $queryParams = [], $formParams = [], $headers = [], $isJSONRequest = false)
{
$client = new Client([
'base_uri' => $this->baseUri,
]);
if (method_exists($this, 'resolveAuthorization')) {
$this->resolveAuthorization($queryParams, $formParams, $headers);
}
$response = $client->request($method, $requestURL, [
$isJSONRequest ? 'json' : 'form_params' => $formParams,
'headers' => $headers,
'query' => $queryParams,
]);
if (method_exists($this, 'decodeResponse')) {
return $this->decodeResponse($response->getBody()->getContents());
}
return $response->getBody()->getContents();
}
Capturing an order code:
public function captureOrder($id)
{
return $this->makeRequest('POST', "/v2/checkout/orders/$id/capture", [], [], [
"Content-Type: application/json",
]);
}
Well, I switched to PayPal HTTP client and it worked just fine.
I am using Laravel 7 and Vue.js 2.
When I edit a room I should update the rooms table and then to redirect to the admin page with a succesfull message.
Unfortunately when I submit the form I edit correctly the table but then the redirect fails. It appears the following error message:
message: "The PUT method is not supported for this route. Supported methods: GET, HEAD.
This is my two methods in AdminController:
public function index()
{
$permissions = Permission::select('id')->get();
$rooms = Room::all();
$languages = Language::all();
$users = UserResource::collection(User::all());
return view('admin')->with(['success_message' => '', 'permissions'=>$permissions, 'users'=>$users, 'rooms'=>$rooms, 'languages'=>$languages]);
}
public function edit_room (Request $request) {
$validator = Validator::make($request->all(), [
'id' => 'required',
'name' => 'required'
]);
if ($validator->fails()) {
return response($validator->errors());
}
$room = Room::find($request->id);
$room->name = $request->name;
$room->save();
$success_message = "The room " . $request->name . " has been correctly edited";
return Redirect::route('admin')->with( ['success_message' => $success_message] );
}
This is the axios call in my child component:
editRoom: function() {
axios.put('edit_room', { id: this.rooms[this.index].id, name: this.roomName })
.then((response) => {
console.log(response);
this.errors = response.data;
if (Object.keys(this.errors).length === 0) {
alert('viva');
this.user = {};
} else {
alert('noviva');
}
})
.catch(error => {
alert(noooooo);
console.log(error);
});
}
This is my two routes in web.php:
Route::put('edit_room', 'AdminController#edit_room')->name('edit_room');
Route::get('/admin', 'AdminController#index')->name('admin');
This is an update of the table so I suppose I should use a PUT method but for some reason it doesn't.
Which is the best way to solve this error?
I think the problem is that you send your request via XHR
So when you using
return Redirect::route('admin')->with( ['success_message' => $success_message]
it sends an response with 301 http code to redirect your browser
i think you should refactor your code like this for example
return 'success_message'
and then in your axios after console.log(response);
window.location.href = "http://www.your_address.com/admin";
Try to connect to external API.
In first function, I already received token with authentication.
To send POST request, I need to put xtoken that I received from first function as second function.
I don't know how to send value to second function (registerUser)
Route::get('/connect', 'Guzzlecontroller#registerUser')->name('registeruser');
this is my route file
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
use App\Http\Controllers\Auth\RegisterController;
class GuzzleController extends Controller
{
public function Gettoken()
{
$client = new \GuzzleHttp\Client();
$request = $client->get(
'http://api01.oriental-game.com:8085/token',
[
'headers' => [
'X-Operator' => 'mog189b',
'X-key' => 'sQxAVNaEMe0TCHhU',
]
]
);
$response = $request->getBody();
$tokenReturn = json_decode($response, true);
$xtoken = array("x-token:" . $tokenReturn['data']['token'],);
$this->registerUser($xtoken);
}
public function registerUser($xtoken)
{
$client = new \GuzzleHttp\Client();
$url = "http://api01.oriental-game.com:8085/register";
$request = $client->post($url, [
'headers' => $xtoken,
'body' => [
'username' => 'test1',
'country' => 'Korea',
'fullname' => 'test user1',
'language' => 'kr',
'email' => 'testuser1#test.com',
]
]);
$response = $request->send();
dd($response);
}
}
Too few arguments to function App\Http\Controllers\GuzzleController::registerUser(), 0 passed and exactly 1 expected
this is error I am getting.
please help me to how to send $xtoken value to registerUser function
The problem is Laravel is calling registerUser directly instead of going through getToken. So the token is never retrieved and passed to the register action.
Instead of calling registerUser() from Gettoken(). Have Gettoken() return the token and call it from registerUser()
public function Gettoken()
{
...
return $xtoken;
}
public function registerUser()
{
$xtoken = $this->Gettoken();
...
}
Im new to PHP.
I'd like to build a module and i need json to pass specific content type fields.
Im trying with this but i dont know how to deal with callback function.
here is my ajax in .js
$.ajax({
type: 'GET',
url: '/mappy/ajax/poi',
data: {
nid: nid
},
dataType: 'json',
success: function(data){
alert(data)
}
});
})
here is my php in .module
function mappy_menu() {
$items = array();
$items['/mappy/ajax/poi'] = array(
'title' => 'Mappy Pois',
'page callback' => 'mappy_get',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
function mpapy_get() {
$nid = $_GET('nid');
$title = field_get_items('node', $node, 'field_title');
$result = json_encode(
db_query("SELECT nid,title FROM {node}", $nid)
);
drupal_json_output($result);
print $result;
}
Many thanks for advice.
Once you get the JSON response you need to convert it to a javascript array. For that, you can do:
var javaArray = $.parseJSON(data);
Now you can retrieve the data, using code like javaArray['key1']['key2'], etc.
.js
$.ajax({
type: 'GET',
// Do not use slash at the beginning, use Drupal.settings.basePath instead
url: Drupal.settings.basePath + 'mappy/ajax/poi',
data: {
nid: nid
},
dataType: 'json',
success: function(data) {
alert(data)
}
});
.module
function mappy_menu() {
$items = array();
// Never use slash at the beginning in hook_menu
$items['mappy/ajax/poi'] = array(
'title' => 'Mappy Pois',
'page callback' => 'mappy_get',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
function mappy_get() {
$node = node_load($_GET('nid'));
// Or equivalent
/* $node = db_select('node', 'n')
->fields('n', array('nid', 'title'))
->condition('n.nid', $_GET('nid')))
->execute()
->fetchAll(); */
$values = array(
'nid' => $node->nid,
'title' => $node->title
);
#$result = json_encode(
# db_query("SELECT nid,title FROM {node}", $nid)
#);
// drupal_json_output already print the value
// print $result;
drupal_json_output($values);
drupal_exit();
}