How to process nested json with FOSRestBundle and symfony forms - json

By nested json I mean something that keeps address data in its own "address" array:
{
"user": {
"id": 999,
"username": "xxxx",
"email": "xxxx#example.org",
"address": {
"street": "13th avenue",
"place": 12
}
}
}
instead of flat one
{
"user": {
"id": 999,
"username": "xxxx",
"email": "xxxx#example.org",
"street": "13th avenue",
"place": 12
}
}
Flat one is processed fine there using User entity and it's properties: "id", "username" and "email". It is nicely validated using symfony form feature:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('username');
$builder->add('email', 'email');
$builder->add('password', 'password');
$builder->add('street');
$builder->add('place');
}
I want to have both "street" and "place" as properties in User entity, to store it all in one user table in the database, using doctrine. But the json I get comes from third party, so I can not modify it.
Is there any way of constructing the form so it can validate the json with "address" field correctly, still being able to keep all the user data in one table?

This is a pretty good question. One solution that comes to mind is making an unmapped form and binding data manually using a form event, for example:
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Make a "nested" address form to resemble JSON structure
$addressBuilder = $builder->create('address', 'form')
->add('place')
->add('street');
$builder->add('username');
$builder->add('email', 'email');
$builder->add('password', 'password');
// add that subform to main form, and make it unmapped
// this will tell symfony not to bind any data to user object
$builder->add($addressBuilder, null, ['mapped' => false]);
// Once form is submitted, get data from address form
// and set it on user object manually
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
$user = $event->getData();
$addressData = $event->getForm()->get('address')->getData();
$user->setStreet($addressData['street']);
$user->setPlace($addressData['place']);
})
}

Related

Can't access POST Variables sent from angular 4 in slim php

I'm trying to build a little Angular 4 Application with a rest api build with slim php. I'm having touble to access the POST variables on specific routes, while it works perfectly on at least one route. Here is my code:
// Log In A League { "email": $email, "pass": $pass }
$app->post('/api/users/league-login', function(Request $request, Response $response){
require "user_management/league_login.php";
$logIn = new LeagueLogIn();
$logIn->logIn($request->getParam("email"), $request->getParam("pass"));
});
Angular sends this JSON:
Object { email: “test#test.com", pass: "000" }
This works perfectly. But on this rout I can't access the POST variables with $request->getParam()
// Add Team To A League { "name": $name, "email": $email }
$app->post('/api/league/team', function(Request $request, Response $response){
require "user_management/team_create.php";
$user = authorizeToken($request->getHeader('Authorization'));
$teamCreate = new TeamCreate();
$teamCreate->create($request->getParam("name"), $request->getParam("email"), $user);
});
Angular sends this JSON
Object { name: "name", email: "test2#test.com" }
As soon as I add double quotes on the keys manually for the request:
Object { "name": "name", "email": "test2#test.com" }
it works perfectly.
Does somebody know what kind of problem this is, or what I'm overlooking?

Access object property while working with another object in ng-repeat

In my project I got a JSON response via GET request. The subTopics will be selected by the user and stored. Afterwards I send a POST request to the server with the selected ids.
Example JSON1: (from GET request)
{
"TopicList" :
[{
"id": "1234",
"name": "topic1",
"number": "1",
"subTopics": [
{
"id": "4567",
"name": "subTopic1.1",
"number": "1.1"
},
{
"id": "9876",
"name": "subTopic1.2",
"number": :1.2"
}
]
}]
}
In the POST response I get another JSON object from the server, which I have to show in my HTML view as a table. In the response JSON I have the subTopics id (selected by the user) but I do not have the subTopic name associated with the id.
I have to show the subTopic name in my table which is available in a separate object(see above JSON file). I don't know how to access the first JSON object while working with another.
My table view looks like this,
<tr ng-repeat-start="tableRow in searchCtrl.tableViewData" ng-click="tableRow.expanded = !tableRow.expanded">
<td>{{tableRow.project.name}}</td>
<td>{{tableRow.project.number}}</td>
<td>{{tableRow.project.endDate | date}}</td>
<td>{{tableRow.topicIds[0]}}</td>
<td>{{tableRow.matching.score}}</td>
</tr>
As you can see the 4th row: <td>{{tableRow.topicIds[0]}}</td> shows the id. How can I show the topicName?
Any help would be appreciable.
EDIT
In my controller this variable contains the above JSON object.
if (!self.topic) {
searchService.getTopic().then(
function (response) {
self.topic = response.data;
},
function (error) {
alert("Server is not found");
}
);
}
So, the topic variable contains the response JSON object. Maybe it will help.
You can create a function that takes an id and returns the subTopic.
$scope.getSubTopic = function(id) {
var selectedSubTopic = {};
angular.forEach(subTopics, function(subTopic) {
// loop through subTopics until a matching id is found
if (subTopic.id === id) {
selectedSubTopic = subTopic;
return;
}
});
return selectedSubTopic;
};
then you can update your fourth row to:
<td>{{getSubTopic(tableRow.topicIds[0]).name}}</td>
This assumes you have an array named subTopics.
Edit
As mentioned in my comment this will end up performing pretty slow for heavy pages and/or large datasets. You will likely want to generate a map object for the subTopics for quick access. The downside being you have to generate this each time the TopicList is modified.
function generateSubTopicMap(topics) {
var map = {};
angular.forEach(topics, function(topic) {
angular.forEach(topic.subTopics, function(subTopic) {
// use this if you want the map to reference the same data
// (i.e. updating subTopic.name will update the map at the same time)
map[subTopic.id] = subTopic;
// use this if you don't want the map to reference the same data
// map[subTopic.id] = {};
// angular.copy(subTopic, map[subTopic.id]);
// you can also add the parent id here if you need access to it
// this will modify the original object if you use the first method!
// map[subTopic.id].parentId = topic.id
});
});
return map;
}
The output looks like:
{
"4567": {
"id": "4567",
"name": "subTopic1.1",
"number": "1.1"
},
"9876": {
"id": "9876",
"name": "subTopic1.2",
"number": :1.2"
}
}
With this you would call it after every GET request and pass it the array of topics.
// where topics is the response from the GET request
$scope.subTopics = generateSubTopicMap(topics);
And finally to display you just need:
<td>{{subTopics[tableRow.topicIds[0])].name}}</td>
Edit 2
Here is a jsfiddle showing how to use the second method. All you have to do is pass the array containing your TopicList to generateSubTopicMap and it returns an object with the keys as subTopic ids and the value as the subTopic itself.
I wouldn't worry about my first solution. It isn't going to be performant inside an ng-repeat or grabbing 2nd level objects.

Push new items into JSON array

Let's say this is the table inside my collection:
{
"_id" : ObjectId("557cf6bbd8efe38c627bffdf"),
"name" : "John Doe",
"rating" : 9,
"newF" : [
"milk",
"Eggs",
"Beans",
"Cream"
]
}
Once a user types in some input, it is sent to my node server, and my node server then adds that item to the list "newF", which is then sent back to my MongoDB and saved.
I'm trying to use update, which can successfully change the values inside of this table, but I'm not sure how to add new items onto that list. I did it with $push inside the MongoDB shell, but not sure how to do it on node.
Here's a snippet of my code:
db.collection('connlist').update({ _id: new ObjectId("e57cf6bb28efe38c6a7bf6df")}, { name: "JohnDoe", rating: 9, newF: ["Milk, Eggs", "Beans"] }, function(err,doc){
console.log(doc);
});
Well the syntax for adding new items is just the same as in the shell:
// make sure you actually imported "ObjectID"
var ObjectId = require('mongodb').ObjectID;
db.collection('conlist').update(
{ "_id": new ObjectId("e57cf6bb28efe38c6a7bf6df") },
{ "$push": { "newF": { "$each": [ "cream", "butter" ] } } },
function(err,numAffected) {
// do something in the callback
}
)
Or perhaps use .findOneAndUpdate() if you want to return the modified document instead of just making the alteration.
Of course use $push and possibly with $each which allows multiple array elements to be added when adding to an array. If you want "unique" items then use $addToSet where your operation allows.
And generally speaking for other items you should use $set or other operators in the update portion of your document. Without these operators you are just "replacing" the document content with whatever structure you place in the "update" portion of your statement.

Storing nested Json in parse.com classes

I'm setting up my data structure for my first parse.com app. I am from a relational data background, so I'm not sure what I can and can't do when storing data in parse.
So my question is, can I store the Json below in a parse class as one object or would I have to split it into multiple classes / objects for the "fixtures" and "location" fields?
{
"name": "fast five",
"rules": "round robin",
"location": [
{
"long":"18.633456",
"lat":"-33.880178",
"venue":"Velodrome, Cape Town",
"date_time":"2011-08-21T18:02:52.249Z"
}
],
"teams": [
"gauteng west",
"gauteng north"
],
"fixtures": [
{
"teamA":"gauteng west",
"teamB":"gauteng west",
"court":"court 5",
"date_time":"2011-08-21T18:02:52.249Z"
},
{
"teamA":"gauteng west",
"teamB":"gauteng west",
"court":"court 5",
"date_time":"2011-08-21T18:02:52.249Z"
}
]
}
Parse supports storing JSON in a column on a Parse Object, but you won't be able to query based on values inside that. It's tough to quickly sketch out the perfect schema for using more of Parse Data, but it could be something like this:
Location class
-venue : "Velodrome, Cape Town"
-date : a date object
-location: a Parse GeoPoint object with that lat/lon
Team class:
-name: "gauteng west"
Fixture class:
-teamA : a Team class object
-teamB : a Team class object
-location: a Location class object
-court : "court 5"
-date : a date object
Event class
-name : "fast five"
-rules : "round robin"
-teams : an array of Team class objects
-location: a Location class object
-fixtures: an array of Fixture class objects
With this separation, you could get all the data for an event at once:
var query = new Parse.Query("Event");
query.include(['teams', 'fixtures', 'location']);
query.first().then(function(event) {
var teams = event.get('teams');
console.log(teams[0].get('name'));
});
Or query for events near a given location:
var locQuery = new Parse.Query("Location");
locQuery.near("location", a Parse GeoPoint object);
var query = new Parse.Query("Event");
query.matchesQuery("location", locQuery);
query.find().then(function(results) {
// has all events sorted by distance from provided geopoint
}, function(err) {
// error
});
And many other benefits..

Displaying Nested Models in Marionette

I'm sending a nested model as JSON to a Marionette app. It looks something like this:
{
"Course1": [
{
"id": 51,
"title": "Assignment1",
"created_at": "2013-09-01T08:47:37.908+09:00",
"updated_at": "2013-09-09T20:53:00.193+09:00",
},
{
"id": 52,
"title": "Assignment2",
"created_at": "2013-09-01T09:11:40.547+09:00",
"updated_at": "2013-09-09T20:52:37.630+09:00",
}
],
"Course2": [
{
"id": 19,
"title": "Assignment1",
"created_at": "2013-08-08T22:49:26.621+09:00",
"updated_at": "2013-09-09T20:48:20.015+09:00",
},
{
"id": 20,
"title": "Assignment2",
"created_at": "2013-08-08T23:03:58.131+09:00",
"updated_at": "2013-09-09T20:47:53.997+09:00",
}
],
"Course3": [
{
"id": 29,
"title": "Assignment1",
"created_at": "2013-08-18T09:22:32.299+09:00",
"updated_at": "2013-09-09T20:47:32.971+09:00",
},
{
"id": 30,
"title": "Assignment2",
"created_at": "2013-08-18T09:33:16.882+09:00",
"updated_at": "2013-09-09T20:02:08.731+09:00",
}
]
}
I'm wondering if there is some way to display each "course" and the data nested within the courses as a table in a Marionette view. I don't know how many courses I'll be sending to Marionette on any given request.
Is there some way to iterate over the data above (as a collection in the Marionette app) and dynamically make a new CompositeView for each course?
You could use Underscore each function. Something like this:
var data = <your JSON data here>;
_.each(data, function(value, key, list) {
var compView = new <yourCustomCompositeView>({ collection: value });
// render the view in whatever region you need to
});
That would create a new CompositeView for each entry in your JSON. Here's the documenation for that function: http://underscorejs.org/#each
Update
Take a look at http://jsfiddle.net/craigjennings11/D2qAC/ on a way to solve this. It might not be the cleanest, but it gets the job done. Note that I specify a custom region that overloads the open method for the layout, and then call open to attach the views to the region instead of show. show closes the region before calling open again, which would remove your previous view.
I realize now how foolish I was! What I was trying to do is part of the core Backbone.Marionette functionality.
I needed only to nest the List.Assignments CompositeView inside the List.Courses CompositeView.
#ControlPanel.module "AssignmentsApp.List", (List, App, Backbone, Marionette, $, _) ->
class List.Controller extends Marionette.Controller
initialize: ->
# This is a collection of courses with nested assignments
# as described in the original question.
App.request "courses:entities", (courses) =>
#coursesView = #getCoursesView courses
App.mainRegion.show #coursesView
getCoursesView: (courses) ->
new List.Courses
collection: courses
class List.Assignment extends Marionette.ItemView
template: "assignments/list/templates/assignment"
tagName: "li"
class List.Assignments extends Marionette.CompositeView
template: "assignments/list/templates/assignments"
tagName: "li"
itemView: List.Assignment
itemViewContainer: "ul"
# This is the important part...
initialize: ->
# Get the nested assignments for this particular course
assignments = #model.get("assignments")
# At this point assignments is a regular Javascript object.
# For this to work assignments must be a valid Backbone collection
#collection = new Backbone.collection assignments
class List.Courses extends Marionette.CompositeView
template: "assignments/list/templates/courses"
itemView: List.Assignments
itemViewContainer: "ul"