PUT Operations in Symfony 5 and Doctrine ODM - php

We're developing an API on top of Symfony 5 using Doctrine ODM and MongoDB as well as API Platform. Problem is trying to support PUT operations.
What we find is that if we PUT a JSON document with some fields not included, the object (eg $data) seems to have those fields filled in from the original document in the database.
In some cases, this means that the document passed in via the PUT request will pass validations such as NotBlank() when the document coming n over the wire is missing a required field, but the $data object being validated has that field filled in from the database prior to validation.
As an example, if the original JSON document in Mongo is
{
projectID: "ABCD",
projectName: "My Project",
cost: 100
}
And assuming projectName is a required field and we PUT the following document to our endpoint /v1/example-endpoint/ABCD
{
cost: 100
}
then in out custom data persister we have the following:
public function persist($data, array $context = [])
{
...do something here...
}
The $data object passed in to the persister will include the previous projectName "My Project" from the database.
Is this expected behavior and/or is there a way around this to validate the data sent over the wire as-is?
FWIW, we tried created a new Object of the class and setting the data from the PUT content, then creating a validator and validating the new object, but ran in to an issue where custom validation methods that had constructors were not being autowired when calling the validator manually. If we could successfully validate the new object that would work too.
$putData = json_decode($this->requestStack->getCurrentRequest()->getContent(), true);
$newProject = (new Project());
foreach ($putData as $property => $value) {
$newProject->setProperty($property, $value);
}
$validator = Validation::createValidatorBuilder()
->addMethodMapping('loadValidatorMetadata')
->getValidator();
$errors = $validator->validate($newProject);

Related

Zend Framework: Transforming post data before reaching Resource

I have a Zend Framework 2 project using Apigility, and I want to be able to send an array of objects via POST to create multiple entities at once. However, ZF/Rest/Resource automatically converts arrays to objects when making a POST. To make the logic a bit cleaner, I would like to convert the data array to an object of my liking (putting the array into a key such as 'storage') before it reaches the Resource.
// ZF\Rest\RestController
public function create($data) //$data is an array
{
$events = $this->getEventManager();
$events->trigger('create.pre', $this, array('data' => $data));
try {
// I want to convert $data to an object by this point
$entity = $this->getResource()->create($data);
} catch (\Exception $e) {
return new ApiProblem($this->getHttpStatusCodeFromException($e), $e);
}
I thought there must be a way to hook into the create.pre event to do this. I've attached a method which gets the Request from the Event, and gets, converts, and sets the Content of the Request, but my debugger says the Resource is still receiving the original array. I've also tried $event->setParam('data', $object), and this didn't work either. (I assume because the parameters are an array and not passed by reference.) Am I going about this the wrong way, or is this not possible?

How to populate Symfony entities with JSON response coming from remote API

I want to develop an application that will use data coming from remote API calls. I'd like the data to be saved in a local database so that I do not use up the API's quotes and for easier subsequent retrieval.
I've already set up entity mappings. However, I'm not sure how I should approach the task of mapping the data coming from remote calls (I'm planning on using Guzzle HTTP client) on the entities and saving them in the database.
With input coming from users, I'd set up Type classes and use Symfony's Form Component.
In this case, however, my application will be sending HTTP requests and receiving data that should be mapped and saved.
Should I perhaps first collect the data I need in DataFixtures, and then populate my entites from those fixtures?
Another method I thought about was using the FormComponent with the omission of handleRequest.
I will also add that I'd like to be able to easily update my local data with the remote data as the remote will be updated on regular basis.
I guess I need a conceptual hint on how to approach this task.
Using Form component is a viable solution, additional bonus apart from mapping data to entities is validation. So, you could validate if data is correct before persisting your entities. You can use submit method directly:
public function newAction(Request $request)
{
$form = $this->createFormBuilder()
// ...
->getForm();
$form->submit(json_decode($yourData, true));
if ($form->isValid()) {
// perform some action...
return $this->redirectToRoute('task_success');
}
}
Another options is to use serializer's, deserialize method:
$nameConverter = new OrgPrefixNameConverter();
$normalizer = new ObjectNormalizer(null, $nameConverter);
$serializer = new Serializer(array($normalizer), array(new JsonEncoder()));
$obj = new Company();
$obj->name = 'Acme Inc.';
$obj->address = '123 Main Street, Big City';
$json = $serializer->serialize($obj);
// {"org_name": "Acme Inc.", "org_address": "123 Main Street, Big City"}
$objCopy = $serializer->deserialize($json);
// Same data as $obj
Links:
serializer
form handling

How to validate array items in Symfony 2.3?

I use Symfony v2.3.x and I have a validation file that looks something like this:
Namespace\To\MyEntity:
properties:
entityCollection:
- Type: { groups: [mygroup], type: array }
- All:
constraints:
- Type: { groups: [mygroup], type: Namespace\To\AnotherEntity }
So MyEntity must contain an array of AnotherEntitys in the entityCollection field.
It successfully validates that entityCollection must be an array. However, it fails to validate that the elements of that array are of the specified type.
For example, these two var_dumps both show the value 0 on the screen even though I expect something non-zero:
$obj = new MyEntity();
$obj->entityCollection = array(12345);
$errors = $symfonyValidator->validate($obj, array('mygroup'));
var_dump(count($errors));
$obj = new MyEntity();
$obj->entityCollection = array("something");
$errors = $symfonyValidator->validate($obj, array('mygroup'));
var_dump(count($errors));
I even tried removing the constraints entry from the validation file; it made no difference.
I looked at Symfony's official page about the All keyword but I couldn't find anything that would work.
How should I modify the entry in my validation.yml file so that the validation works as intended ?
Thank you in advance.
Your configs look fine, although I'm more accustomed to having them as annotations so it's clear what entity properties they apply to. Have you tried:
$errors = $symfonyValidator->validate($obj, array('mygroup'), true);
The 3rd parameter to the validate() method is a boolean for traversing the object you're validating.
bool $traverse Whether to traverse the value if it is traversable.
Symfony Validator API v2.3

How can I pass the current referenced object in form collection in Symfony 2?

I use this code
$builder->add('userTasks','collection',array('type' => new UserTaskType()));
This is working fine
userTasks will be a collection different userTask objects which will in turn create the form.
Now is there any way that i can pass that individual UserTask object in the constructor like this
$builder->add('userTasks','collection',array('type' => new UserTaskType($userTask)));
so that i can use that to generate Label for the form.
Is it possible
If i use $user->UseTasks then that will be a collection of all tasks but i only want that object whose form is being created
If I understand the question correctly, you want to adjust the form based on the actual data object which the form will be bound to? It is one of the those simple sounding requirements that is actually a bit involved.
You need to use the form event system:
http://symfony.com/doc/master/cookbook/form/dynamic_form_generation.html
It's not so bad once you have worked through it.
===
To answer the first comment:
public function preSetData(DataEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
$data will be your object, in this case a $userTask.

removing the form name from post field in symfony2

I'm trying to use symfony2 to create a web service. I'd like the webservice to be structured and listen for:
POST to /teams/list with params key1=value and key2=value2
For validation purposes, i've created a TeamForm object and a TeamFormModel to validate the data against (using annotations). The problem i'm having is that the form is looking for team[key1] and team[key2] instead of just key1 and key2 to bind to the TeamFormModel.
Is there a way to configure the form to not use the team[*]?
If you are using the 2.1 branch, it's easy you can simply create a form with an empty name.
$form = $this->get('form.factory')->createNamed(
'', // the name
new TeamType(), // the type
$team // the data
);
$form->bindRequest($request);
So it will work as you are expecting.
But if you are using the 2.0 branch, from what I know, it's not supported and you have to do the binding manually:
$form = $this->createForm(new TeamType(), $team);
$from->bind($request->request->all());
You can validate entity without creating form. You can create entity object from POST data and pass it to validator. See validation section of the cookbook.
If you don't like to create entity object from request parameters every time then you can post data in json or xml format and then de-serialize into entity object using JMSSerializerBundle.

Categories