Using WordPress API with Slim 3 and Twig Views - php

I'm trying to integrate a decoupled wordpress/wp-api to a Slim 3 framework with Twig views.
Ideally I want to send all posts to /posts route rendered via Twig and
Individual posts to /post/hello-world route
I'm having two issues for now:
I want to pass the response back (queried via guzzle) to a Twig view and I'm struggling with that.
The JSON response I'm getting back in the view is not an appropriate JSON response when I echo $body;
// ROUTES
$app->get('/', function ($request, $response) {
return $this->view->render($response, 'home.html');
})->setName('home');
$app->get('/posts/', function ($request, $response,array $args) {
$client = new Client();
$url ='/cms/wp-json/wp/v2/posts';
$res = $client->request('GET',$url,array(
'content-type' => 'application/json'
),array());
$body = $res->getBody();
// echo $body;
return $this->view->render( $response,'posts.html',array('posts' => $body));
})->setName('posts');
I would really like to keep using Twig with Slim instead of using Vue.js or Angular and build a SPA.

Related

How to call an API endpoint in Slim Framework v3 from within a route?

I'm using the PHP Slim Framework v3 to make a web app and the routes I have are all typically divided into either a frontend route or an API endpoint. I have a frontend route where I want to call an API endpoint to get data to display. For example:
Frontend Route
$app->get('/order/{order-id}', function(Request $request, Response $response, array $args) {
$order_id = intval($args['order-id']);
$order_details = ______; // API endpoint call to get the order details
$response = $this->view->render($response, 'order-details.html', [
'order_details' => $order_details
]);
return $response;
});
API Endpoint
$app->get('/api/order/{order-id}', function(Request $request, Response $response, array $args) use ($db) {
$order_id = intval($args['order-id']);
$order_details = $db->order_details($order_id); // Query the database for all the order details
$response = $response->withJson($order_details);
return $response;
});
What can I put in place of the ______ so I can grab the JSON being returned by the /api/order/{order-id} call?
Please note that I'm considering using Guzzle to do this, but I feel like that's such an overkill for what I'm trying to do here. I would like to think that Slim already has a way for me to do what I'm attempting to achieve.
From trying a few solutions out, I was able to use subRequest() for this:
$app->get('/order/{order-id}', function(Request $request, Response $response, array $args) use ($app) {
$order_id = intval($args['order-id']);
$order_details = $app->subRequest('GET', '/api/order/' . $order_id)->getBody(); // API endpoint call to get the order details
$order_details = json_decode($order_details);
$response = $this->view->render($response, 'order-details.html', [
'order_details' => $order_details
]);
return $response;
});
Seems to work for me, but this may not be the best solution as I am still just learning how to use Slim. Other better answers are welcome!

How to get all paginated data using guzzle promise

I am using guzzle for getting post of a single page and it is working fine. But now the problem is page has pagination 20 post on each page. i want to get all the posts. How can I do it by using guzzle ?
here is my code:
public function __construct()
{
$this->client = new Client([
'base_uri' => 'https://xxxxxx.com/',
'Content-Type' => 'application/json',
]);
}
public function post($post)
{
$response = $this->client->request('GET', $post);
$output = $response->getBody()->getContents();
$data = $this->getData($output);
return $data;
}
There is no way to do it in general. HTTP as a protocol doesn't specify anything about pagination. So depends on the server you work with. Usually the response contains something like
{
"page": 5,
"total": 631
}
Based on this info you can create an URL for the next page by adding ?page=6 (also depends on the server) and request it.

does Slim framework have URL encoded annotation for POST method?

I'm developing a web RESTful API using slim framework of php.I want to know how do I add some annotation type thing on POST method so that it can behave as URL encoded method.Please help me in this regard.Advance thanks.
There is no pre-programmed way for this - there is no Slim or php method that will definitively check if your string is urlencoded. What you can do is implement Slim Middleware to your route.
<?php
$app = new \Slim\App();
$mw = function ($request, $response, $next) {
if ( urlencode(urldecode($data)) === $data){
$response = $next($request, $response);
} else {
$response = ... // throw error
}
return $response;
};
$app->get('/', function ($request, $response, $args) { // Your route
$response->getBody()->write(' Hello ');
return $response;
})->add($mw); // chained middleware
$app->run();
Discussion: Test if string is URL encoded in PHP
Middleware: https://www.slimframework.com/docs/v3/concepts/middleware.html
Since you're using Slim as the foundation to your API, the easiest way is to just build a GET route with the desired URL parameters defined:
$app->get('/users/filter/{param1}/{param2}/{param3}', function (Request $request, Response $response) {
// Route actions here
});
In your documentation, make sure you inform the consumers of this API that it is a GET endpoint, so that a POST body should not be made; rather, the parameters that you outline in the URL should be used to pass the client's data over to the API.
If you are intent on using a POST route with just URL parameters, then you could also force a response back if the route detects an incoming POST body:
$app->post('/users/filter/{param1}/{param2}/{param3}', function (Request $request, Response $response) {
$postBody = $request->getParsedBody();
if (is_array($postBody)) {
$denyMsg = "This endpoint does not accept POST body as a means to transmit data; please refer to the API documentation for proper usage.";
$denyResponse = $response->withJson($denyMsg, $status = null, $encodingOptions = 0);
return $profileData;
}
});

Calling an internal api within another api in using Slim Framework

Good day,
Im trying to develop a web platform using Slim framework. I've done it in MVC way. some of my APIs are used to render the view and some is just built to get data from the db.
for example :
$app->get('/api/getListAdmin', function () use ($app) {
$data = ...//code to get admins' list
echo json_encode($data);
})->name("getListAdmin");
$app->get('/adminpage', function () use ($app) {
// **** METHOD 1 :// get the data using file_get_contents
$result = file_get_contents(APP_ROOT.'api/getListAdmin');
// or
// **** METHOD 2 :// get data using router
$route = $this->app->router->getNamedRoute('getListAdmin');
$result = $route->dispatch();
$result = json_decode($result);
$app->render('adminpage.php', array(
'data' => $result
));
});
I'm trying to call the db handling Api '/api/getListAdmin' within the view related apis '/adminpage'.
based on solutions i have found in the web i tried method 1 and 2 but:
method 1 (using file_get_contents) take a long time to get the data (few seconds on my local environment).
method 2 (router->getNamedRoute->dispatch) seems dosnt work becuz it will render the result in the view even if i use $result = $route->dispatch(); to store the result in a variable but seems dispatch method still render to result to the screen.
I tried to create a new slim app only for db related API but still calling one of them takes quite long time 2 to 3 seconds.
Really appreciate it if some one can help me on what i'm doing wrong or what is the right way to get data from another api.
Thanks
Method 1
This could be another method, creating a Service layer, where redundant code is deleted:
class Api {
function getListAdmin() {
$admins = array("admin1", "admin2", "admin3"); //Retrieve your magic data
return $admins;
}
}
$app->get('/api/getListAdmin', function () use ($app) {
$api = new Api();
$admins = $api->getListAdmin();
echo json_encode($admins);
})->name("getListAdmin");
$app->get('/adminpage', function () use ($app) {
$api = new Api();
$admins = $api->getListAdmin();
$app->render('adminpage.php', array(
'data' => $admins
));
});
Method 2
If you are ok with an overkill method, you could use Httpful:
$app->get('/adminpage', function () use ($app) {
$result = \Httpful\Request::get(APP_ROOT.'api/getListAdmin')->send();
//No need to decode if there is the JSON Content-Type in the response
$result = json_decode($result);
$app->render('adminpage.php', array(
'data' => $result
));
});

Doing HTTP requests FROM Laravel to an external API

What I want is get an object from an API with a HTTP (eg, jQuery's AJAX) request to an external api. How do I start? I did research on Mr Google but I can't find anything helping.
Im starting to wonder is this is even possible?
In this post Laravel 4 make post request from controller to external url with data it looks like it can be done. But there's no example nor any source where to find some documentation.
Please help me out?
Based upon an answer of a similar question here:
https://stackoverflow.com/a/22695523/1412268
Take a look at Guzzle
$client = new GuzzleHttp\Client();
$res = $client->get('https://api.github.com/user', ['auth' => ['user', 'pass']]);
echo $res->getStatusCode(); // 200
echo $res->getBody(); // { "type": "User", ....
We can use package Guzzle in Laravel, it is a PHP HTTP client to send HTTP requests.
You can install Guzzle through composer
composer require guzzlehttp/guzzle:~6.0
Or you can specify Guzzle as a dependency in your project's existing composer.json
{
"require": {
"guzzlehttp/guzzle": "~6.0"
}
}
Example code in laravel 5 using Guzzle as shown below,
use GuzzleHttp\Client;
class yourController extends Controller {
public function saveApiData()
{
$client = new Client();
$res = $client->request('POST', 'https://url_to_the_api', [
'form_params' => [
'client_id' => 'test_id',
'secret' => 'test_secret',
]
]);
echo $res->getStatusCode();
// 200
echo $res->getHeader('content-type');
// 'application/json; charset=utf8'
echo $res->getBody();
// {"type":"User"...'
}
You just want to call an external URL and use the results? PHP does this out of the box, if we're talking about a simple GET request to something serving JSON:
$json = json_decode(file_get_contents('http://host.com/api/stuff/1'), true);
If you want to do a post request, it's a little harder but there's loads of examples how to do this with curl.
So I guess the question is; what exactly do you want?
As of Laravel v7.X, the framework now comes with a minimal API wrapped around the Guzzle HTTP client. It provides an easy way to make get, post, put, patch, and delete requests using the HTTP Client:
use Illuminate\Support\Facades\Http;
$response = Http::get('http://test.com');
$response = Http::post('http://test.com');
$response = Http::put('http://test.com');
$response = Http::patch('http://test.com');
$response = Http::delete('http://test.com');
You can manage responses using the set of methods provided by the Illuminate\Http\Client\Response instance returned.
$response->body() : string;
$response->json() : array;
$response->status() : int;
$response->ok() : bool;
$response->successful() : bool;
$response->serverError() : bool;
$response->clientError() : bool;
$response->header($header) : string;
$response->headers() : array;
Please note that you will, of course, need to install Guzzle like so:
composer require guzzlehttp/guzzle
There are a lot more helpful features built-in and you can find out more about these set of the feature here: https://laravel.com/docs/7.x/http-client
This is definitely now the easiest way to make external API calls within Laravel.
Updated on March 21 2019
Add GuzzleHttp package using composer require guzzlehttp/guzzle:~6.3.3
Or you can specify Guzzle as a dependency in your project's composer.json
{
"require": {
"guzzlehttp/guzzle": "~6.3.3"
}
}
Include below line in the top of the class where you are calling the API
use GuzzleHttp\Client;
Add below code for making the request
$client = new Client();
$res = $client->request('POST', 'http://www.exmple.com/mydetails', [
'form_params' => [
'name' => 'george',
]
]);
if ($res->getStatusCode() == 200) { // 200 OK
$response_data = $res->getBody()->getContents();
}
Definitively, for any PHP project, you may want to use GuzzleHTTP for sending requests.
Guzzle has very nice documentation you can check here.
I just want to say that, you probably want to centralize the usage of the Client class of Guzzle in any component of your Laravel project (for example a trait) instead of being creating Client instances on several controllers and components of Laravel (as many articles and replies suggest).
I created a trait you can try to use, which allows you to send requests from any component of your Laravel project, just using it and calling to makeRequest.
namespace App\Traits;
use GuzzleHttp\Client;
trait ConsumesExternalServices
{
/**
* Send a request to any service
* #return string
*/
public function makeRequest($method, $requestUrl, $queryParams = [], $formParams = [], $headers = [], $hasFile = false)
{
$client = new Client([
'base_uri' => $this->baseUri,
]);
$bodyType = 'form_params';
if ($hasFile) {
$bodyType = 'multipart';
$multipart = [];
foreach ($formParams as $name => $contents) {
$multipart[] = [
'name' => $name,
'contents' => $contents
];
}
}
$response = $client->request($method, $requestUrl, [
'query' => $queryParams,
$bodyType => $hasFile ? $multipart : $formParams,
'headers' => $headers,
]);
$response = $response->getBody()->getContents();
return $response;
}
}
Notice this trait can even handle files sending.
If you want more details about this trait and some other stuff to integrate this trait to Laravel, check this article. Additionally, if interested in this topic or need major assistance, you can take my course which guides you in the whole process.
I hope it helps all of you.
Best wishes :)
Basic Solution for Laravel 8 is
use Illuminate\Support\Facades\Http;
$response = Http::get('http://example.com');
I had conflict between "GuzzleHTTP sending requests" and "Illuminate\Http\Request;" don't ask me why... [it's here to be searchable]
So looking for 1sec i found in Laravel 8 Doc...
**Guzzle is inside the Laravel 8 Http Request !**
https://laravel.com/docs/8.x/http-client#making-requests
as you can see
https://laravel.com/docs/8.x/http-client#introduction
Laravel provides an expressive, minimal API around the Guzzle HTTP
client, allowing you to quickly make outgoing HTTP requests to
communicate with other web applications. Laravel's wrapper around
Guzzle is focused on its most common use cases and a wonderful
developer experience.
It worked for me very well, have fun and if helpful point up!
I also created trait similar to #JuanDMeGonthat's that u can use anywhere in your project.Please check this out
trait ApiRequests
{
public function get($url, $data = null)
{
try {
$response = Http::get($this->base_url . $url, $data);
} catch (\Exception $e) {
info($e->getMessage());
abort(503);
}
if ( $response->status() == 401) {
throw new AuthenticationException();
} else if (! $response->successful()) {
abort(503);
}
return $response->json();
}
public function post($url, $data = [])
{
$token = session()->get('token');
try {
$response = Http::acceptJson()->withToken($token)->post($this->base_url . $url, $data);
} catch (\Exception $e) {
abort(503);
}
if ($response->status() == 401 && !request()->routeIs('login')) {
throw new AuthenticationException();
}
return $response;
}
}
class Controller extends BaseController
{
protected $base_url;
use AuthorizesRequests, DispatchesJobs, ValidatesRequests, ApiRequests;
public function __construct()
{
$this->base_url = env("BASE_URL","http://192.168.xxxxxxx");
View::share('base_url', $this->base_url);
}
}
You can use Httpful :
Website : http://phphttpclient.com/
Github : https://github.com/nategood/httpful
Here is the simple call for laravel 9.4
Route::get('/currency', function () {
$response = Http::withHeaders([
'x-api-key' => 'prtl6749387986743898559646983194',
])->get('https://partners.api.skyscanner.net/apiservices/v3/culture/currencies');
return response()->json(['status'=> true,'data'=> json_decode($response->body()), 'Message'=>"Currency retrieved successfully"], 200);
});
Don't forget to import
use Illuminate\Support\Facades\Http;

Categories