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.
Related
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.
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;
https://github.com/hasbridge/php-json-schema
Getting error Class 'JsonValidator' not found.
I did install composer.json as in git folder.
I did put my json schema, json data files in src/Json and created a php file to validate as mentioned in git.
getting error class Class 'JsonValidator' not found.
In their example, they're creating a JsonValidator object.
This is different from the actual namespace of the class, which is Json\Validator.
Try dropping use Json\Validator as JsonValidator at the top of your file so that you're able to refer to the class the same way the docs do.
I'd expand their docs from:
$someJson = '{"foo":"bar"}';
$jsonObject = json_decode($someJson);
$validator = new JsonValidator('/path/to/yourschema.json');
$validator->validate($jsonObject);
To
<?php
namespace Your\Domain;
use Json\Validator as JsonValidator;
require_once('./vendor/autoload.php');
$someJson = '{"foo": "bar"}';
$jsonObject = json_decode($someJson);
$validator = new JsonValidator('/path/to/yourschema.json');
$validator->validate($jsonObject);
Alternatively, you could substitute new JsonValidator('/path/to/yourschema.json') for new Json\Validator('/path/to/yourschema.json').
Edit: By the way - you might find the example schemas at json-schema.org helpful when using this library.
Here's the main one from that link:
{
"title": "Example Schema",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
}
},
"required": ["firstName", "lastName"]
}
Save this file somewhere in your project and refer to it instead of /path/to/yourschema.json.
I am using this class https://github.com/ricog/coinbase-exchange-php/blob/master/lib/CoinbaseExchange/CoinbaseExchange.php
When I call it using
$listOrders = $exchange->listOrders();
print_r($listOrders);
This is my output:
[
{
"id": "d50ec984-77a8-460a-b958-66f114b0de9b",
"size": "3.0",
"price": "100.23",
"product_id": "BTC-USD",
"status": "open",
"filled_size": "1.23",
"fill_fees": "0.001",
"settled": false,
"side": "buy",
"created_at": "2014-11-14 06:39:55.189376+00"
}
]
By default all the pending or open orders are called. The documentation states "To specify multiple statuses, use the status query argument multiple times:
/orders?status=done&status=pending." How can I call the above class where the status is 'done' instead of 'open', without showing any pending transactions.
I tried this $listOrders = $exchange->listOrders('status'=>'done');
It didn't work.
Source: https://docs.exchange.coinbase.com/?php#list-orders
the class that you are using is not ment for sending multiple params (kind of stupidly written if you ask me). What you can do is to extend that class:
class MyExchange extends CoinbaseExchange {
public function request($endpoint, $params = array()){
return $response = parent::request($endpoint, $params);
}
}
And then use it like this:
$exchange = new MyExchange();
// any other methods required for authentikation and settings like $exchange->auth(...)
$listOrders = $exchange->request('orders', array('status'=>'done', 'status'=>'pending');
What I have coded is a oneToMany relationship with doctrine
one user ---> has many notifications
This is how I get the data
/**
* #Route("/test")
*/
public function testRoute()
{
//get the user notifications
$notifications = $this->getUser()->getNotifications();
//return json response
$serializer = $this->get('serializer');
$json = $serializer->serialize($notifications, 'json');
$response = new Response($json);
$response->headers->set('Content-Type', 'application/json');
return $response;
}
This is what the controller returns
[
{
"id": 1,
"receiver": 1,
"notification_type": "new_comment",
"triggered_by": {
"id": 1,
"username": "gabriel",
"username_canonical": "gabriel",
"password": "3e6bS2I==",
"email": "ga#ga.de",
"first_name": "Gabriel",
"last_name": "ThaKid",
"likes_counter": 0,
"dislikes_counter": 2,
"favourites_counter": 0,
"profile_pic": "profilepic_60181.png",
"salt": "Lqch0N84UH1QmFI5O",
"form_token": "sO6NgWd",
"is_active": true,
"registration_confirmation": "success",
"secret_confirmation_id": "qTwNGm4CSKHzJOe8ry9DcXavt",
"socket_token": "KuMlxYHa"
},
"created_at": "2014-12-16T13:36:20+0100",
"link_to": "#test"
},
{
"id": 2,
"receiver": 1,
"notification_type": "new_comment",
"triggered_by": {
"id": 1,
"username": "gabriel",
"username_canonical": "gabriel",
"password": "3e6bS2IYX1DONLA/70a8hzMUQ==",
"email": "ga#ga.de",
"first_name": "Gabriel",
"last_name": "ThaKid",
"likes_counter": 0,
"dislikes_counter": 2,
"favourites_counter": 0,
"profile_pic": "profilepic_60181.png",
"profile_rank": "Beginner", (...)
},
"created_at": "2014-12-16T13:36:24+0100",
"link_to": "#test"
}
]
I think you get the point, it returns the notifications of a certain user which is allright,
I also need certain fields from the user, like the lastname and the firstname, so the returned data is usable in the application.
But doctrine also returns the hashed password and the salt along with tokens and information the user doesn't need to know.
how do I tell doctrine not to return those fields or not to fetch them to begin with?
This is not about Doctrine, this is the default Serializer that tries to fetch and return all of the values available.
Take a look at the Serializer component documentation to understand how to ignore properties. Basically, you'll have to pass a normalizer into your serializer's constructor:
<?php
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
$normalizer = new GetSetMethodNormalizer();
$normalizer->setIgnoredAttributes(array('age'));
$encoder = new JsonEncoder();
$serializer = new Serializer(array($normalizer), array($encoder));
$serializer->serialize($person, 'json');
Also, I'd strongly suggest to switch to JMSSerializer and/or use FOSRestBundle, as it gives a lot more flexibility for the serialization. The property list is configured against contexts, and has base exclusion strategies. This way, you'd only need to list properties to expose, not to exclude:
AppBundle\Entity\Car:
exclusion_policy: all
properties:
id:
expose: true
vendor:
expose: true
title:
expose: true
In the given example, all properties that were not listed would be excluded.
You should create a custom repository class.
In this class create a method like this:
// src/AppBundle/Entity/NotificationRepository.php
namespace AppBundle\Entity;
use Doctrine\ORM\EntityRepository;
class NotificationRepository extends EntityRepository
{
public function findAllByUser()
{
return $this->getEntityManager()
->createQuery(
'SELECT [only the fields you need] FROM [....]'
)
->getResult();
}
}
For the DQL query you could use the partial object syntax.