Is there any solutions on how to change property names or attributes of JSON Request in Laravel?
Something like Eloquent API Resources but instead in responses, it will be done in requests before directing to the request validation?
From this,
{
"agent_reference": "ABC-12345",
"product_instance_id": "aca68c65-44c3-4ea1-a726-ca183de09a31",
"add_ons": [
"string"
],
"transportation": "string",
"guests": [
{
"guest_type_key": "string",
"add_ons": [
"string"
],
"field_responses": [
{
"key": "string",
"response": "string"
}
]
}
]
}
To this,
{
"agent_id": "ABC-12345",
"plan_id": "aca68c65-44c3-4ea1-a726-ca183de09a31",
"additional_params": [
"string"
],
"pickup_place": "string",
"visitors": [
{
"visitor_kty": "string",
"additional_params": [
"string"
],
"responses": [
{
"id": "string",
"result": "string"
}
]
}
]
}
Laravel 4 had support for such a thing, however, sadly, it got removed with the release of Laravel 5.
For Laravel 5.2+, the "neat" way of achieving this is is by overriding the validationData() method in your App\Http\Requests\FormRequest class.
Take a look at:
Illuminate\Foundation\Http\FormRequest
protected function validationData()
{
return $this->all();
}
The function above is meant to return all inputs that will be sent to the validation, $this is the Request itself.
So, in your App\Http\Requests\FormRequest class, you define the very same method, get the inputs, sanitize as you want (even creating a whole new structure with different key names, as requested in your question), replace it in the request to persist and return, like so:
/**
* Get data to be validated from the request.
*
* #return array
*/
protected function validationData()
{
$inputs = $this->all();
// Sanitize $inputs as your likes.
$this->replace($inputs); // To persist.
return $this->all();
}
This is how you get it done in Laravel 5.2+.
Hope it helps.
Related
I am trying to structure my project in Laravel just created to use it as a back-end API. I want all my responses from Laravel to be returned in the JSON:API format defined on the official site: https://jsonapi.org/format/
For example:
I have created the following 2 resource files:
1- php artisan make:resource User
2- php artisan make:resource UserCollection --collection
Two simple resource files, one to return a resource and one to return a collection.
Now, I would like to return in all my responses the following format:
In case of success
1- The status return code can be 200, 201 or 202.
2- The returned response should be similar to the following:
{
"data": [
{
"id": 1,
"email": "collins.elody#example.org"
}
],
"message": null,
"success": true
}
You may be wondering what is the point of passing the message key, in this case it is null because it would be returning a collection of records, that is, reading, but in case you needed to add a new record, update or delete one, you would need to pass a message to my front-end, in this case I would use that key for that.
Example, adding record, response status code 201:
{
"data": [],
"message": "Record created succesfully",
"success": true
}
In case of failure
As said here: https://jsonapi.org/format/#document-top-level : The members data and errors MUST NOT coexist in the same document.
So, in case of error, I need change data key by errors key, for example, suppose I am trying to authenticate myself, and the validation fails, in this case, it should turn out like this:
{
"errors": {
"email": "E-Mail is required",
"password": "Password is required"
},
"message": null,
"success": false
}
or I just want to return an error message, expected output should by:
{
"errors": [],
"message": "Something is Wrong!",
"success": false
}
So in essence what I need is a global wrapper for all the responses I make from Laravel. I would like to call return in an elegant way as follows:
return $this->success($collection);
or
return $this->success('Done!', 201);
So the first thing that came to mind was creating a trait and defining the methods you need to then call them from anywhere in Laravel
My Trait
<?php
namespace App\Traits;
trait APIResponse
{
public function success($data, $status = 200) {
return [
'data' => $data,
'success' => in_array($status, [200, 201, 202]) ? true : false,
'message' => null
];
}
public function failure($data, $status = 500) {
// TODO
}
}
My Controller
class ExampleController extends Controller
{
public function index() {
$collection = new UserCollection(User::all());
return $this->success($collection);
}
}
But I am not sure it is the right way to do it, please, someone skilled in the field who can help me. Thank you very much in advance.
You are on the right path, there is two main solutions i would consider best approaches, to handling your exact problem. Fractal and Eloquent Resources, i prefer Fractal due to some design decisions and experience.
I will show an example in fractal, using the wrapper by Spatie. Firstly create an serializer that will wrap the data as expected.
class YourCustomSerializer extends SerializerAbstract
{
public function collection($resourceKey, array $data)
{
return [
$resourceKey ?: 'data' => $data,
'message': null,
'success': true,
];
}
public function item($resourceKey, array $data)
{
return [
$resourceKey ?: 'data' => $data,
'message': null,
'success': true,
];
}
This should be added to your fractal.php config, there is published through the spatie wrapper.
Transforming your data you need a transformer.
class UserTransformer extends TransformerAbstract
{
public function transform(User $user)
{
return [
'name' => $user->name,
'email' => $user->email,
];
}
}
Now you can transform your data into the expected format.
public function response($data, int $statusCode = Response::HTTP_OK)
{
return fractal($data, $this->transformer)->respond($statusCode);
}
For error codes, you should go to the Handler.php and add something similar to this. This is very naive way of doing it, but for know should get you going on error handling, you need to do something with validation exception, status code etc.
if ($request->wantsJson()) {
return response()->json([
'success' => false,
'message' => $exception->getMessage(),
], Response::HTTP_BAD_REQUEST);
}
I was the simple api-platform resource configuration to access to list of user and just one user:
App\Entity\User:
collectionOperations:
get: ~
itemOperations:
get: ~
This config generate 2 routes:
/GET /api/users
/GET /api/users/{id}
One item of User that represent a user account and linked natural person:
{
"id": 0,
"username": "string",
"email": "string",
"person": {
"id": 0,
"civility": "string",
"lastName": "string",
"firstName": "string",
"language": "string",
"fullName": "string"
}
}
Then I want to update a specific field of my user account: password
/PUT /api/users/{id}/updatePassword
{
"password": "string",
"confirmPassword": "string"
}
My current api-platform configuration for this route is :
App\Resource\DTOs\UpdatePassword:
collectionOperations: []
itemOperations:
put:
method: 'PUT'
path: '/users/{id}/updatePassword'
requirements:
id: '\d+'
swagger_context:
tags: ['User']
summary: Update user account password
namespace App\Resource\DTOs;
class UpdatePassword
{
/** #var string */
public $password;
/** #var string */
public $confirmPassword;
}
The problem is that my DTOs don't know that it has to update a user. How to indicate that it is linked to a user resource? I tried with api-platform events but I don't know when to act.
I managed to do something that works but it's that I removed all the mechanisms of api-platform...
And more generally how to save several updates on the same resource ?
Do you have any idea?
I think the best way is to create a listener that will handled the persist part:
https://api-platform.com/docs/core/events/
class UpdatePasswordPersistSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
KernelEvents::VIEW => [['persist', EventPriorities::PRE_WRITE]],
];
}
public function persist(GetResponseForControllerResultEvent $event)
{
$object = $event->getControllerResult();
$method = $event->getRequest()->getMethod();
if ($object instanceof UpdatePassword && $method === Request::METHOD_PUT) {
// todo: persist
// Set the result in response if needed
// $event->setControllerResult($result);
}
}
}
I use something like that in a API Platform 2.2 version. Maybe you need to adapt for newer version.
Alternatively you can use a custom operation but that does not seem the best way:
https://api-platform.com/docs/core/operations/#creating-custom-operations-and-controllers
I'm working on a project and I came across a problem, explain:
I'm doing a POST to a webserver using the Guzzle http, follows the :
public function sendPost($direction, array $data, array
$options = ['http_errors'=>false])
{
$url = $this->interpolate( self::BASE_PATH."/{direction}",
[
'direction' => $direction
]);
$options = array_merge($options,['body'=>json_encode($data)]);
return $this->client->post($url, $options);
}
The method is working correctly and I am returning the following:
{
"id": "jdhj9h830027hd73hs9q9js9",
"direction": "left",
"status": "Success",
"code": {
"id":"1",
"desc": "ok",
"error": false,
"msg":null
}
}
What I can not do is the following:
A method that returns only the given "jdhj9h830027hd73hs9q9js9", that is, the "id" parameter.
can anybody help me?
PS. By using the "sendPost ()" method I can access the data I need separately, however I can not do this through another method, such as a "getId ()".
Thank you in advance for your help.
Just try:
return ($this->client->post($url, $options))->id;
I got the serializer in Symfony working, i can GET and POST json with the EntityController. I have an edit form where an existing object/entity can be edited and saved. When the edited object gets posted (as a json object) the json objects gets deserialized, persisted and flushed tot the database. So far so good, at least as long as i dont post associated (ManyToMany) entities.
I have two entities (with a ManyToMany association):
Object
Element
This works (see also the jsonEditAction in the controller(posted below)):
{
"id": "1",
"name": "object 1"
}
My question is: How can i edit the object with associated entities in the json, like this:
{
"id": "1",
"name": "object 1",
"elements": {
"0": "1",
"1": "2"
}
}
When i post the above json i get the following notice from Symfony:
"message": "Expected argument of type \"AppBundle\Entity\Element\", \"integer\" given",
"class": "Symfony\Component\PropertyAccess\Exception\InvalidArgumentException",
For further information; this is the editAction where the Json is posted to:
public function jsonEditAction(Request $request, $id) {
$serializer = $this->initSerializer();
$em = $this->getDoctrine()->getManager();
$object = $em->getRepository('AppBundle:Object')->findOneById($id);
$data = $request->getContent();
$serializer->deserialize($data, 'AppBundle\Entity\Object', 'json', array('object_to_populate' => $object));
try {
$em->persist($object);
$em->flush();
$view = $this->view($object, 200)
->setTemplate("object/json.html.twig");
} catch (Exception $e) {
$view = $this->view('Caught exception: ' . $e->getMessage() . "\n", 500)
->setTemplate("object/json.html.twig");
}
return $this->handleView($view);
}
private function initSerializer() {
$encoders = array(new JsonEncoder());
$normalizers = array(new ObjectNormalizer());
$serializer = new Serializer($normalizers, $encoders);
return $serializer;
} `
Should i walk to the array with elements and find them one by one and then add them to the 'object' entity? Or is there a 'build-in' solution in the serializer that i missed/ didn't see?
UPDATE:
Also the JSON like suggested by Alexey didn't work:
{
"id": 2,
"name": "Object 2c",
"elements": [{
"id": 1
}, {
"id": 2
}]
}
UPDATE:
My question is a duplicate of:
Deserialize an entity with a relationship with Symfony Serializer Component
There is a pull request created and will be merged in Symfony 3.2...
You have a bad JSON, Symfony says you the same.
A good JSON can look like this:
{
"id": "1",
"name": "object 1",
"elements": [
{"id": "254", "name": "element 254"},
{"id": "301", "name": "element 301"}
]
}
Symfony tries to deserialize an Element object from "1" and "2" in your original JSON and fails.
I'm using spark in codeigniter as a rest client to talk to the service. The apis are already built separately in swagger.
I'm trying to do an user login functionality. This is my api model schema:
{
"name": "string",
"email": "string",
"password": "string",
"mobile": "string",
"accType": "string",
"meta": "string",
"createdAt": 0
}
For testing user existence, this is my code :
function __construct(){
parent::__construct();
$this->load->library('rest', array(
'server' => 'http://stage.cogxio.co:7070/',
'api_key' => 'local',
'api_name' => 'X-API-KEY',
));
}
function user_exist($data){
$response = $this->rest->get('user/email/'.$data, 'json');
if(property_exists($response, "STATUS")){
$result_code = $response->STATUS;
if($result_code == 200){
return true;
}else{
return false;
}
} else {
return "server error";
}
}
The return of 1 will tell that the email exists otherwise does not exist.
When that checking is over, then I need to check whether the email and password are matching, that is "WHERE email=$email && password=$password"
Since, I'm new to api, I don't know how to do that.
I know, I need to pass two variables.
function login_match($email, $password){
$response = $this->rest->get('user/email/??, 'json'); // what to do with this field?
}
I don't know how to use the where clause here.