The company I work for has an API and I am porting that API over to PHP. Right now, the API returns to me a large JSON object and I'm trying to figure out how I should handle the data. I could have a whole bunch of "get" methods, like:
$t = new APIThing();
$t->getJSONObjects();
for ($i=0; ...) {
$t->getHeadline($i);
}
Or, I could return the JSON object and let people play with the data themselves, so that would be something like this
$t = new APIThing();
$t->getJSONObjects();
foreach ($t as $u) {
echo $u->headline;
}
So what do you think? Just expose the JSON object or wrap the whole thing up into functions?
instead of that you can have a class that gets anything from the JSON
class GETAPI {
protected $api;
function __construct(){
$this->api = new APIThing();
$this->api->getJSONObjects();
}
function getAllFromAPI($name){
foreach($this->api as $u){
echo $u->$name;
}
}
//or :
function getFromAPI($name, $index){
return $this->api[$index]->$name;
}
}
its rudimentary and could use some work, but that work over making many many get functions
than all you would have to do is something like:
$api = new GETAPI();
$api->getAllFromAPI('headline');
//or
echo $api->getFromAPI('headline', 1); // with one as the array index
Related
I have written some functions using Authorize.Net's PHP SDK's that look like the following:
public function getCustomerProfiles() {
$customerProfiles = array();
// Before we can get customer profiles, we need to get a list of all customer id's.
$customerIdListRequest = new AnetAPI\GetCustomerProfileIdsRequest();
$customerIdListRequest->setMerchantAuthentication(self::getMerchantAuth(Config::LOGIN_ID, Config::TRANSACTION_KEY));
$customerIdListController = new AnetController\GetCustomerProfileIdsController($customerIdListRequest);
$customerIdListResponse = $customerIdListController->executeWithApiResponse(\net\authorize\api\constants\ANetEnvironment::SANDBOX);
if(($customerIdListResponse != null) && ($customerIdListResponse->getMessages()->getResultCode() == "Ok")) {
// TODO: Investigate warning about no method named getIds().
foreach( $customerIdListResponse->getIds() as $id ) {
// Now we can get each customer profile.
$request = new AnetAPI\GetCustomerProfileRequest();
$request->setMerchantAuthentication(self::getMerchantAuth(Config::LOGIN_ID, Config::TRANSACTION_KEY));
$request->setCustomerProfileId($id);
$controller = new AnetController\GetCustomerProfileController($request);
$response = $controller->executeWithApiResponse(\net\authorize\api\constants\ANetEnvironment::SANDBOX);
if(($response != null) && ($response->getMessages()->getResultCode() == "Ok")) {
// TODO: Investigate warning about no method named getProfile()
// Add it to the array.
array_push($customerProfiles, $response->getProfile()->xml);
} else {
throw new \Exception($response->getMessages()->getMessage());
}
}
} else {
throw new \Exception($customerIdListResponse->getMessages()->getMessage());
}
return $customerProfiles;
}
Currently, I'm just returning an array of objects. I'd prefer to get the raw XML response. Is this functionality available via Authorize.Net's PHP SDK? Or am I better of using something like Guzzle and making the request manually?
Looking at the source code I think it would be simple enough.
Look the execute method that is invoked by executeWithApiResponse there. See xmlResponse? Just need to store that as a class property (and add a public getter), or maybe tweak the function to take an extra argument telling it to return the raw response. Could hack it, or better yet, extend that ApiOperationBase class (note the interface IApiOperation gives you a outline to follow).
Seeing that serializer also...
$this->apiResponse = $this->serializer->deserialize( $xmlResponse, $this->apiResponseType , 'xml');
Could maybe do something more elegant with that. But not as clear as path I first described.
I have below code that save the country information in Database. Below code works fine. There is no problem in that.
private function SaveChanges(\App\Http\Requests\CountryRequest $request) {
if($request['CountryID'] == 0) {
$Country = new \App\Models\CountryModel();
}
else {
$Country = $this->GetCountry($request['CountryID']);
}
$Country->Country = $request['Country'];
$Country->CountryCode = $request['CountryCode'];
$Country->save();
return redirect()->route($this->AllCountries);
}
Now, I decided to shift the working of above method inside a new class like below. Here I am reading the JSON data
class CountryData {
public function CreateCountry($CountryObject) {
$obj = json_decode($CountryObject);
$Country = new \App\Models\CountryModel();
$Country->Country = $CountryObject->Country;
$Country->CountryCode = $CountryObject->CountryCode;
$Country->save();
return true;
}
}
and the original function is changed like below. Sending the Request parameter in the form of JSON.
private function SaveChanges(\App\Http\Requests\CountryRequest $request) {
$data = array(
'Country' => $request['Country'],
'CountryCode' => $request['CountryCode'],
'CountryID' => $request['CountryID']
);
if($request['CountryID'] == 0) {
$result = (new \CountryData())->CreateCountry( json_encode($data) );
}
return redirect()->route($this->AllCountries);
}
Question: Is my approach correct to send converted request object to JSON object and reading in an another Class .
I am doing that so that I can create a new controller and call the CreateCountry from class CountryData to return JSON data for an Android App.
Well, I don't think it's a good approach. Your CountryData class acts as a service, so I think it hasn't have to know anything about JSON, that is part of the interface between your business logic and the external side of your system (Android app, web interface, etc.)
Your new Controller may receive JSON objects and answer with JSON objects, but it must convert the JSON received to your business classes, then pass them to your services, in this case CountryData (not a good name, though).
So the logic should be:
Controller:
- receive request data
- call service and save or whatever
- encode to JSON
- send the response in JSON format
So your business classes don't know anything about JSON.
A not fully code solution is provided as an idea, but it lacks error management, and more work to do. It's based on some Laravel 5 features. Also I don't know if you're using REST or what kind of request are you doing...
use App\Http\Controllers\Controller;
class CountryController() extends Controller {
public function store(\App\Http\Requests\CountryRequest $request) {
// TODO manage errors
$countryModel = $this->createOrUpdateCountry($request);
// Laravel way to response as JSON
return redirect()->json($this->country2Array($countryModel);
}
private function createOrUpdateCountry(\App\Http\Requests\CountryRequest $request) {
$countryId = $request['CountryID'];
if($id == 0) {
$countryModel = new \App\Models\CountryModel();
} else {
$countryModel = $this->GetCountry($countryId);
}
$countryModel->Country = $request['Country'];
$countryModel->CountryCode = $request['CountryCode'];
// You must have an initialised instance of CountryDAO
// TODO manage errors
$countryDAO->saveOrUpdate($countryModel);
return $countryModel;
}
private function country2Array($countryModel) {
$data = array(
'country' => $countryModel->Country,
'countryCode' => $countryModel->CountryCode,
'countryId' => $countryModel->CountryID
);
return $data;
}
}
/**
* Formerly CountryData
*/
class CountryDAO {
public function saveOrUpdate($countryModel) {
// TODO Manage errors or DB exceptions
// I'd put the DB save access/responsability here instead of in CountryModel
$countryModel->save();
return true;
}
}
First of you should not do any conversions to objects and so on.
Second, since the request object should be an array as shown on your example I suggest you to use the "fill" method of Laravel, instead of looping on hand all of the request elements.
Your code for saving the request should be as follows:
class CountryData {
public function CreateCountry($requestData) {
$Country = new \App\Models\CountryModel();
$country->fill($requestData);
$Country->save();
return true;
}
}
The "fill" method loops all of the array keys and tries to set them into the object instance if it has those keys as properties. If there are any extra fields, they are trimmed and you wont get any errors.
Cheers! :)
I am trying to make multiple API requests and I have to make the request in different functions that are within a class like so:
class exampleClass
{
function callFunction1 () {
// stuff that makes a call
return $json;
}
function printStuffOut() {
$jsonStuff = $this->callFunction1();
$$jsonStuff->{'result'}[0]->{'fieldName'};
}
function printStuffOut2() {
$jsonStuff = $this->callFunction1();
$jsonStuff->{'result'}[0]->{'fieldName'};
}
}
Am I making two separate API calls?
If I am, is there a way to store that API call information say in an array then use that array in all the other functions in my class?
Answer to first question: Yes you are, each time the method is called it executes all its definition again.
Answer to second question: Yes there is, so called member properties. You can read up about them in the PHP manual here: PHP Manual: Properties
You are making two API calls, but you don't have to.
You can put the contents of a call into a member variable in the class with a default value of NULL, and if you want, you can check if that member variable is NULL before making an API call. For example;
class exampleClass
{
private $api_json = NULL;
private function call_api()
{
if(is_null($this->api_json))
{
$json = // result of api call;
$this->api_json = $json;
}
return $this->api_json;
}
public function printStuffOut() {
$jsonStuff = $this->call_api();
$jsonStuff->{'result'}[0]->{'fieldName'};
}
public function printStuffOut2() {
$jsonStuff = $this->call_api();
$jsonStuff->{'result'}[0]->{'fieldName'};
}
}
You can use following class to achieve multiple API simultaneously/instantly/at once.
Click here to get a class.
How to use it?
Step 1: Get object.
//SANI: GET DATA
$obj = new multiapi();
Step 2: Make a multiple GET Requests.
$obj->data = array(YOUR_URL_1,YOUR_URL_2, OUR_URL_3);
$result = $obj->get_process_requests();
print_r($result);
Step 3: Make a multiple HTTP POST Requests.
//SANI: Request with params only
$obj->data[0]['url'] = 'YOUR_URL_ONE';
$obj->data[0]['post'] = array();
$obj->data[0]['post']['param_1'] = 'param_value_1';
$obj->data[0]['post']['param_2'] = 'param_value_2';
I have a PHP program I'm writing that does a SOAP request, and it returns an Object. I need to write a function where it takes the data from this Object and uses it in various ways, but I don't want it to do a SOAP request each time if the SOAP request for the data in this Object is already resident.
Pseudo-code example:
$price = GetPartPrice("1234");
function GetPartPrice($part_number) {
If Parts_List_Object not found then do SOAP request to get Parts_List_Object.
}
The problem I see is that I don't know where or how to store if the Parts_List_Object is already there. Do I need to set something up to make the StdClass object that gets requested from the SOAP/JSON request global or is there a better method to do all this? Thanks!
One method would be to build a registry of these objects where you store the ones you fetch and look up the ones you need. That allows you to simply grab a reference to the instance that you've already loaded. A very basic example:
class PartListRegistry {
private static $list = array();
// After you do the SOAP request, call this to save a reference to the object
public static function addPartObject($key, $obj) {
self::$list[$key] = $obj;
}
// Call this to see if the object exists already
public static function getPartObject($key) {
if (isset(self::$list[$key])) {
return self::$list[$key];
}
return null;
}
}
function GetPartPrice($part_number) {
$part = PartListRegistry::getPartObject($part_number);
if ($part === null) {
$part = .... // Do your SOAP request here
// Save a reference to the object when you're done
PartListregistry::addPartObject($part_num, $part);
}
// Do your stuff with the part ....
}
So I am writing an API, and I am fearful that the code I am writing is going to become messy very quickly.
Some pages I need to be fetching data from several different resources from the API, and I am ending up with a whole bunch of try and catch statements everywhere. For example:
$topic_id = 100;
try
{
$topic = AwesomeAPI::get('topics/' . $topic_id);
$parts = AwesomeAPI::get('parts?topic_id=' . $topic_id);
try
{
// Get another resource here
}
catch (Exception $e)
{
// Error
}
}
catch (Exception $e)
{
return Response::error('404');
}
I am confident that this code is quite messy, and it gets even worse if I have to loop through a resource and grab another resource in the loop. Yuck.
I am wondering how to best approach client interactions with an API in a neat fashion.
As I commented before, in my opinion you should have all your API logic inside a class and therefore inside methods. So instead of writing code like the one posted, you can write something like this:
$api = new AwesomeAPI();
$topic = $api->GetTopic($topic_id);
And your AwesomeAPI class could look something like this:
public class AwesomeAPI()
{
public $topic_url = 'somewebsite.com/topics?id{0}';
function GetTopic($topicId)
{
//Some code here
$response = 'Some response (could be a JSON Document)';
return $response;
}
}
This way your code in the application will be more elegant and clear.
Hope this helps you