Converting Request Object into JSON in Laravel 5.2 - php

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! :)

Related

Calling methods on object gets 'call to member function on array' error

I inherited this project from my predecessor, and he was way overqualified. A lot of stuff he wrote goes over my head. But as far as vanilla php goes, I'm pretty confident, and can't for the life of me figure out why the application thinks the object I created is an array. Maybe I don't actually know anything. You tell me.
use via\zoom\Bulletin;
use via\zoom\DatabaseConnection;
require_once('includes/config.php');
require_once(CORE .'sql.php');
require_once(CORE . 'model.php');
require_once(CORE . 'bulletin.php');
// If we've passed the validation step we can guarantee we have a valid $active_user
validate();
//run if a page deletion has been requested
if (isset($_GET['delpage'])) {
$del = $_GET['delpage'];
$bulletin = new Bulletin;
$bulletin = Bulletin::get($del);
if(!empty($bulletin))
{
$bulletin->delete();
/*
So.
For some reason, the above object is cast as an array.
If you try to cast it as an object, it defaults to stdClass.
On the left we have a method complaining that it can't work outside of its class. Hard stop, array to method exception.
On the right we have an object with all the right data, but set to the wrong class, so it can't find the delete method at all. Hard stop, undefined method exception.
*/
//this is the workaround, pulled the script straight from the delete method in the model class
/*$dbh = DatabaseConnection::get();
$query_string = "DELETE FROM brochure_generator_bulletin WHERE id = $del";
try {
$dbh->query($query_string);
//return true;
} catch (\Exception $e) {
//return false;
}*/
}
header('Location: bulletins');
exit();
}
Here's the get method from the Bulletin class, extends Model--
public static function get( ...$ids )
{
$matches = parent::get( ...$ids );
foreach( $matches as &$match )
{
$match->content = json_decode( $match->content );
}
return $matches;
}
And here's the delete method from the Model Class:
public function delete()
{
if (isset($this->id)) {
$dbh = DatabaseConnection::get();
$query_string = "DELETE FROM {$this->table_name} WHERE id = \"{$this->id}\"";
try {
$dbh->query($query_string);
return true;
} catch (\Exception $e) {
return false;
}
}
return false;
}
What am I missing? Is he using a framework I'm not familiar with? I'm utterly grasping at straws here, and at this point my options are grab all the method scripts and stick them where they need to be inline, or just starting over from the ground up.
You don't need to create a new Bulletin object before using the static get() method, so you can remove this:
$bulletin = new Bulletin;
That $bulletin variable is immediately overwritten by the next line anyway.
$bulletin = Bulletin::get($del);
get() takes one or more ids and returns an array of one or more corresponding objects. You're giving it one id and expecting one object back, but it's still going to return that object inside an array. You just need to get the object out of the array so you can call its delete method.
if(!empty($bulletin))
{
$bulletin = reset($bulletin); // get the first item in the array
$bulletin->delete();
You could also review the model and see if it has a different method that returns a single object rather than an array of objects.

How to handle correctly JSON request in symfony?

I'm trying to handle this problem:
My app send JSON POST request with several information encoded in a Json. Example:
{"UserInfoA":{"1":123,"2":"hello","3":"bye","4":{"subinfo":1,"subinfo2":10}},
"UserInfoB":{"a":"12345678","b":"asd"}} // and so on...
each UserInfo have:
Its own entity (although some request may have information of more than one entity).
A controller to persist this Object on DB and then give back the ID on this DB.
So, to achieve this problem I did another controller like JsonHandler, which receive this request and then forward to each controller after gut this JSON into differents objects. Example:
public function getJson (Request $request){
if (0 === strpos($request->headers->get('Content-Type'), 'application/json')) {
$data = json_decode($request->getContent(), true);
}
if (!isset($data['UserInfoA'])){
return new JsonResponse('ERROR');
}
$uia = $data['UserInfoA'];
$idInfoA = $this->forward('Path::dataAPersist',array('data'=>$uia));
}
// same with userinfoB and other objects
return $idInfoA ;
This works perfectly but Is it correct? Should i use services instead?
EDIT : I need to response the ID in a Json and this->forward returns a Response, so i can't use JsonResponse and if a send directly $idInfoA just send the IDNUMBER not in a JSON, how can i do it?
To sum up : a Json listener that receive the information, work it and then dispatch to the corresponding controller. This listener, should be a controller, a service or another thing?
I recommend the use of symfony-bundles/json-request-bundle since it does the JsonRequestTransformerListener for you. You just need to recieve the request parameters as usual:
...
$request->get('some_parameter');
...
hi you have to use service to make the Transformation
class php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class JsonRequestTransformerListener {
public function onKernelRequest(GetResponseEvent $event) {
$request = $event->getRequest();
$content = $request->getContent();
if (empty($content)) {
return;
}
if (!$this->isJsonRequest($request)) {
return;
}
if (!$this->transformJsonBody($request)) {
$response = Response::create('Unable to parse request.', 400);
$event->setResponse($response);
}
}
private function isJsonRequest(Request $request) {
return 'json' === $request->getContentType();
}
private function transformJsonBody(Request $request) {
$data = json_decode($request->getContent(), true);
if (json_last_error() !== JSON_ERROR_NONE) {
return false;
}
if ($data === null) {
return true;
}
$request->request->replace($data);
return true;
}
}
And In Your Service.yml
kernel.event_listener.json_request_transformer:
class: You\NameBundle\Service\JsonRequestTransformerListener
tags:
- { name: "kernel.event_listener", event: "kernel.request",method: "onKernelRequest", priority: "100" }
Now You can call The Default request function to get Data
$request->request->all();
You can use symfony ParamConverter to to convert the json into any object you want and raise a meaningful Exception if anything goes wrong.
You can add custom ParamConverters and use them in your actions with annotation

How to make an object resident to a function call?

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 ....
}

How to Test cakephp parseExtension?

I setup the parseExtension for json in my cakephp 2.3.0. No errors are display. It works?
How can I test is?
In RoR ist ist very easy to test via
https://mydomain.de/view/4.json
How does it run on cakephp?
My View-Action is this.
public function view($id = null) {
if (!$this->Atla->exists($id)) {
throw new NotFoundException(__('Invalid atla'));
}
$options = array('conditions' => array('Atla.' . $this->Atla->primaryKey => $id));
$this->set('atla', $this->Atla->find('first', $options));
$this->Atla->id = $id;
$result = $this->Atla->read();
$this->response->type('json');
$this->response->body(json_encode($result));
return $this->response;
$this->set(compact('atlas'));
}
Why i always get an json-request?
If you use the _serialize key cakephp can automatically create json and xml views for you. I typically use the following to create json or xml views:
public function view() {
// data I want to display
$record1 = $this->ModelName->find('first', ...);
$this->set('record1', $record1);
$record2 = $this->ModelName->find('first', ...);
$this->set('record2', $record2);
// option 1: serialize the hard way
$this->set('_serialize', array('record1', 'record2'));
// option 2: serialize the easy way
$this->set('_serialize', array_keys($this->viewVars));
}
PS: the code after your return statement will never be executed.
you'll have create the view
app/View/Atlas/json/view.ctp
which is the view that is being used for .json requests.
Requests without .json will use the regular view file:
app/View/Atlas/view.ctp
There's more information on creating/using JSON and XML views here:
http://book.cakephp.org/2.0/en/views/json-and-xml-views.html#using-a-data-view-with-view-files
From that page the view.ctp may contain something like;
// View code - app/View/Posts/json/index.ctp
foreach ($posts as &$post) {
unset($post['Post']['generated_html']);
}
echo json_encode(compact('posts', 'comments'));
However, it really depends on what you're trying to achieve. If you will only use the 'Atlas/view' action for JSON responses, and won't be using HTML at all, sometimes you can get away with generating the response-body inside your Controller. Not very much 'in line' with MVC conventions, but it saves you from creating a view that does nothing more than echo json_encode($data); ;)
public function view($id)
{
$this->MyModel->id = $id;
$result = $this->MyModel->read();
$this->response->type('json');
$this->response->body(json_encode($result));
//Return reponse object to prevent controller from trying to render a view
return $this->response;
}
If you do want to use both 'HTML' and 'JSON', depending on the request (with/without .json extension), you should have two view files; 1 for JSON and 1 for HTML;
// This view will be used for your JSON requests
app/View/Atlas/json/view.ctp
// This view will be used for non-JSON (html) requests:
app/View/Atlas/view.ctp
In the json-view, output the data using json_encode(.....);
In the 'normal'/html view, just output normal data
In your controller, set the data as normal
public function view($id = null) {
$this->Atla->id = $id;
if (!$this->Atla->exists()) {
throw new NotFoundException(__('Invalid atla'));
}
$this->set('atla', $this->Atla->read());
}

Best practices to handle JSON data for API SDK

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

Categories