Empty relations when serializing with JMSSerializer - symfony

I am having troubles while writing a controller-action inside a Symfony project, that should return data (in this particular case orders of a web-shop). Yeah ... It's a kind of a REST-API. That route just get's called from some JavaScript. And the data has to be visualized on the client-side.
The Problem:
I cannot find out, why the serialization of related entities results in empty objects. In this example it is the user of an order, which is empty.
This is a sample output:
orders: [
{
id: 2,
created: '2016-05-04T11:40:27+00:00',
user: {},
}
]
When I do something like
$orders = $this->getDoctrine()->getRepository('AppBundle:Order')
->findAllCompleted();
$serializationContext->setSerializeNull(true);
$serializationContext->setGroups(['statistics']);
$json = $serializer->serialize($orders, 'json', $serializationContext);
$response = new Response($json, $statusCode, [
'Content-Type' => 'application/json',
]);
return $response;
... i get a nice JSON response from the server, but every related entity of each order, like let's say user is {} (empty).
Even if I dump the related entity before it gets serialized like so:
[...]
$myOrder = array_filter($orders, function($order) {
if ($order->getId() == 2) {
return true;
}
return false;
});
dump($myOrder[0]->getUser());
die();
... it results in an empty (unhydrated) entity.
But if I change this debugging code to:
$myOrder = array_filter($orders, function($order) {
if ($order->getId() == 2) {
return true;
}
return false;
});
dump($myOrder[0]->getUser()->getUsername());
die();
... I get a clear output (string) with the value of the username of that entity.
So I think the issue is about a non hydrated entity, and not the serializer or its wrong configuration.
How can I get the JMSSerializer to take care of the hydration of those related entities?
I didn't find any hint in the docs ...
BTW, this are the JMS entity configs of order and user.
AppBundle\Entity\User:
exclusion_policy: ALL
properties:
userMeta:
expose: true
address:
expose: true
username:
expose: true
email:
expose: true
isReseller:
expose: true
acceptPublicOrders:
expose: true
vatNumber:
expose: true
taxRate:
expose: true
AppBundle\Entity\Order:
exclusion_policy: NONE
properties:
id:
groups: ['statistics']
user:
groups: ['statistics']
configuration:
groups: ['statistics']
created:
groups: ['statistics']
invoiceItems:
groups: ['statistics']
exclude: true

I think your problem is caused by doctrine lazy loading maybe you can try to change the fetch mode of the User association to EAGER in your Order entity
#ManyToOne(targetEntity="Cart", cascade={"all"}, fetch="EAGER")
By default i think it doesn't fetch the associations unless you call it directly like you did here
dump($myOrder[0]->getUser()->getUsername());
https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html#annref-onetoone
Or this if you use DQL
14.7.6.6. Temporarily change fetch mode in DQL
http://doctrine-orm.readthedocs.io/en/latest/reference/dql-doctrine-query-language.html#temporarily-change-fetch-mode-in-dql
Edit : i was wrong
I made some tests everything worked fine with lazy loading or eager until i tried with groups, even if the fields are exposed you don't use the Default group so it only take the things with the 'statistics' group on it
Try to add the default group here
$serializationContext->setGroups(['Default','statistics']);
Or add the statistics group on your user fields both worked for me

Related

How to do FOSElasticaBundle bulk index?

It seems that the default doctrine listener used by FOSElasticaBundle does not support bulk index by default. I have an application where I want to add support for more complex search queries through ElasticSearch. The search engine only will perform queries through one unique entity Post. When I create, edit or delete there is not any problem, the index in elasticsearch is updated automatically through the listener. My problem comes when I want to do bulk updates to hide or show more than one post at once, the listener is not receiving the signal to make the bulk index update in elasticsearch.
I am new to FOSElasticSearch so I do not know if I am missing something. I am using FOSElasticaBundle 6, Symfony 5.2 and ElasticSearch 7
Here you can find my fos_elastica.yaml
fos_elastica:
messenger: ~
clients:
default: { host: 127.0.0.1, port: 9200 }
indexes:
product:
settings:
index:
analysis:
analyzer:
my_analyzer:
type: snowball
language: English
persistence:
# the driver can be orm, mongodb or phpcr
driver: orm
model: App\Entity\Post
listener: { enabled: true }
provider: ~
finder: ~
elastica_to_model_transformer:
ignore_missing: true
properties:
title: { boost: 10, analyzer: my_analyzer }
tags: { boost: 8, analyzer: my_analyzer }
description: { boost: 6, analyzer: my_analyzer }
ispublished: { type: integer}
And here you can find the way I am updating more than once entity element at once in PostRepository (the function is to update all post from one unique author, it is just an example):
public function bulkUpdate($ispublished, $authorid){
return $this->createQueryBuilder('p')
->update()
->set('p.ispublished', $ispublished)
->andWhere('p.authorid = :id')
->setParameter('id', $authorid)
->getQuery()
->execute();
}
Also I found that I could disable default listener, dispatch messages for each create, update or delete action through symfony/messenger and consume them async in the background. I guess that I should create my own handler and dispatch specific messages (although I could not find an example about this in the doc) in each modifying action, although at the end I also have the same problem, as I do not know how to send a bulk index update/delete action
In the other hand I was thinking in executing all time a background script in python to check what rows were modified in mysql database and update those index with the script directly through ElasticSearch Api
I do not think that I will need to update more than 1k posts at once, so I would like to keep using the default listener to update posts automatically and avoid gaps between that an entity is modified and the index is updated in ElasticSearch. I just need to find the best way to update indexes in bulk as I have everything else already implemented and working
Sorry for all the text but I wanted to give all details about what I need to do

Symfony CMF Dynamic Router no contentDocument

Following the Dynamic Router doc, I can:
Create a route linked to my content (Page entity)
Persist it with ORM
Load my controller matching the route
But as my controller should expect the $contentDocument parameter, I have nothing.
Here's how I create my route and my entity:
$page = new Page();
$page->setTitle('My Content')
->setBackground(1)
->setDescription('My description')
->setContent('<h1>Hello</h1>');
$manager->persist($page);
$manager->flush(); // flush to be able to use the generated id
$contentRepository = $this->container->get('cmf_routing.content_repository');
$route = new Route();
$route->setName('my-content');
$route->setStaticPrefix('/my-content');
$route->setDefault(RouteObjectInterface::CONTENT_ID, $contentRepository->getContentId($page));
$route->setContent($page);
$page->addRoute($route); // Create the backlink from content to route
$manager->persist($page);
$manager->flush();
Here's my config section:
cmf_routing:
chain:
routers_by_id:
router.default: 200
cmf_routing.dynamic_router: 100
dynamic:
enabled: true
persistence:
orm:
enabled: true
generic_controller: AppBundle:Default:showPage
templates_by_class:
AppBundle\Entity\Page: AppBundle:Default:index.html.twig
My controller:
public function showPageAction($page)
{
return $this->render('default/index.html.twig');
}
And the error:
Controller "AppBundle\Controller\DefaultController::showPageAction()" requires that you provide a value for the "$page" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.
Dynamic routing puts the content document into the request with the name "contentDocument". You need to explicitly use that name:
public function showPageAction($contentDocument)
{
return $this->render('default/index.html.twig');
}
if you do not need to do anything in your controller, you don't need to specify the generic_controller. template_by_class will make the bundle provided controller render that template with the page instance found in $contentDocument.

JMSSerializer not serializing entity

I am trying to get a serialized entity response using the JmsSerializerBUndle and the FOSRestBundle. unfortunatly i'm unable to do so because i'm having problems
with the response.. i'm not sure the system recognizes the file Entity.SearchEngine.yml
Any Ideas how I could find why its not working?
#src\example\CoreBundle\Resources\config\serializer\Entity.SearchEngine.yml
Example\CoreBundle\Entity\SearchEngine:
exclusion_policy: ALL
properties:
id:
expose: true
groups: [search.list,search.details]
has_product_flight:
expose: true
groups: [search.details]
selfLink:
expose: true
groups: [self.link]
Rest action (using the FOSRestBundle):
/**
* GET /search/engines
*
* #return array
* #Rest\View(serializerGroups={"search.details", "self.link"})
*/
public function getSearchEnginesAction()
{
$searchEngineManager = $this->get('search_manager');
return $searchEngineManager->getSearchEngineList();
}
the response im getting is
{
0: { }
}
First, is SerializerBundle configured correctly? You should supply config for paths to your .ymls and corresponding namespaces, like this:
jms_serializer:
metadata:
auto_detection: true
directories:
ExampleCoreBundle:
namespace_prefix: "Example\\CoreBundle"
path: "#ExampleCoreBundle/Resources/config/serializer/"
Be sure to check that your search.manager service returns instances of SearchEngine. Also, I'm not sure if dots are supported in group names.

How to get groups working for JMSSerializerBundle and FOSRestBundle?

I am trying to set up different groups to achieve different types of serialization of my entities depending on the context.
My config looks like this:
My\FooBundle\Entity\Asset:
exclusion_policy: ALL
access_type: public_method
properties:
id:
access_type: property
expose: true
groups: [fnord]
name:
expose: true
path:
expose: true
isInQuarantine:
expose: true
groups: [baz]
I expect that the group having properties should not be exposed unless the group is set.
I am trying to set the group in my controller via:
$view->setSerializationContext(SerializationContext::create()->setGroups(array('fnord')));
Yet there is no effect on what is exposed and what isn't. Even if I do not try to change the SerializationContext, the groups options seems to be always ignored.
I know that my config is working because I can toggle the properties via the expose flag.
Yet what am I doing wrong here?
I know this question is a bit old, but it may help others.
I encountered a similar issue.
This was because I had in my controler (that extends FOSRestControler) a method with multiple calls to
$this->getView()
You have to notice that this method creates a new View object.
That means, if you call multiple getView methods, the context get reset.
Have a look at the following code that worked for my app :
use FOS\RestBundle\Controller\FOSRestController as Controller;
class RestController extends Controller
{
public function getUserAction($username)
{
$view = $this->view();
$view->setSerializationContext(SerializationContext::create()->setGroups(array('Product')));
$user = $this->getDoctrine()->getManager()->getRepository('VendorUserBundle:User')->findOneByUsername($username);
if(!is_object($user))
{
throw $this->createNotFoundException();
}
$view->setData($user);
return $view;
}
}
In Model.User.yml file :
FOS\UserBundle\Model\User:
exclusion_policy: ALL
properties:
username:
expose: true
groups: [fnord]
email:
expose: true
groups: [Product]
enabled:
expose: true
groups: [Product]
roles:
expose: true
groups: [Product]
Gives the following output :
{"email":"guiguiboy#xxx.com","enabled":true,"roles":["ROLE_XXX"]}
I didn't have any cache related problems (dev env used).

Simple Symfony2 / Doctrine Validation not working

This just doesn't make sense. I can't seem to get a simple Symfony2 validation working.
$insert = new MyEntity();
$insert->setTest1( 'test' );
$validator = $this->get('validator');
$errors = $validator->validate($insert);
...but $errors is always an object with an empty constraints array. It never fails the validation.
My configuration (Yaml):
MyBundle\Entity\MyEntity:
properties:
test1:
- MinLength: 10
- Email
type: entity
table: null
fields:
id:
type: integer
id: true
generator:
strategy: AUTO
test1:
type: string
length: 255
column: test_1
test2:
type: integer
column: test_2
lifecycleCallbacks: { }
You're mixing doctrine's mapping and symfony's validation in a single yml file.
The validation configuration in yml is loaded from the files:
Acme/YourBundle/Resources/config/validation.yml // YAML
Acme/YourBundle/Resources/config/validation.xml // XML
And the mapping information should be placed in one of:
Acme/YourBundle/Resources/config/doctrine/MyEntity.orm.yml // YAML
Acme/YourBundle/Resources/config/doctrine/MyEntity.orm.xml // XML
Acme/YourBundle/Resources/config/doctrine/orm/MyEntity.orm.yml // YAML
Acme/YourBundle/Resources/config/doctrine/orm/MyEntity.orm.xml // XML

Resources