Serialize Persistent colletions with JMS serialize - doctrine-orm

I have been using the "jms/serializer": "0.13.*#dev" in order to serialize my objects.
I am using it in a Zend Framework (2) and a Doctrine project.
This is my code:
use JMS\Serializer\SerializerBuilder as SerializerBuilder;
(....)
public function getList() {
$em = $this->getEntityManager();
$repo = $em->getRepository('MyApp\Entity\Product');
$hydrator = new DoctrineHydrator($em);
$data = array();
foreach ($repo->findAll() as $found) {
$data[] = $hydrator->extract($found);
}
$serializer = SerializerBuilder::create()->build();
$jsonContent = $serializer->serialize($data, 'json');
$this->getResponseWithHeader()->setStatusCode(self::OK_200);
return new JsonModel($jsonContent);
}
But I am getting this error:
Resources are not supported in serialized data. Path: MyApp\Entity\FOO -> Doctrine\ORM\PersistentCollection -> MyApp\Entity\Product -> Doctrine\ORM\PersistentCollection -> DoctrineORMModule\Proxy__CG__\MyApp\MyApp\Brand
Apparently you can't serialize persistent collections.
I have googled around and found this Symfony related question. But how can I solve this problem within the stand alone Serializer library?
Thanks very much.
EDIT
Can this have anything to do with JMS annotations? Should I use certain annotations to get this working?

I'm guessing the issue here is that the PersistentCollection may contain a reference to a database connection so JMS can't handle the Resource type.
If you only want the first level serialised then I guess you could hack at it like:
foreach ($repo->findAll() as $found) {
// Set these to basically empty arrays
$found->setRelation1(new \Doctrine\Common\Collections\ArrayCollection);
$found->setRelation2(new \Doctrine\Common\Collections\ArrayCollection);
$data[] = $hydrator->extract($found);
}
Or you could extend the JMS Serialize function to just ignore Resource collections rather than throwing an Exception.

Related

Symfony4 returning serialized json repsonse

I'm looking at a new Symfony5 project , where I'm trying to return a JSON response of some data.
I have a Project and a ProjectItem
I have the following:
// Project.php
/**
* #ORM\OneToMany(targetEntity="App\Entity\ProjectItem", mappedBy="project")
*/
private $projectItems;
// ProjectItem.php
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Project", inversedBy="projectItems")
*/
private $project;
I have one Project, that can have many ProjectItems
I then have a controller that I'm trying to return a json response:
public function index()
{
$itemsList = $this->getDoctrine()
->getRepository(Project::class)
->findAll();
$items = $this->get('serializer')->serialize($itemsList, 'json');
return new Response($items, 200);
}
This is currently returning an error:
A circular reference has been detected when serializing the object of class "App\Entity\Project" (configured limit: 1)
Am I using the serializer correctly or are my models incorrectly configured?
Simply use json_encode:
public function index()
{
$itemsList = $this->getDoctrine()
->getRepository(Project::class)
->findAll();
return new Response(
json_encode($itemsList),
200
);
}
or use JsonResponse class:
return new JsonResponse($itemsList);
You have a circular reference with your relations. Im guessing ProjectItem has a field project that is referencing Project which causes a loop for the serializer. You can ignore said attribute to prevent this from happening. Checkout the ignored attributes section of the serializer documentation.
Another option would be to use Serialization Groups. Every property would get a Group annotation like for example #Groups("group1") excluding that reference property back to Project.
You would then tell the serializer to serialize that group:
$json = $serializer->serialize(
$itemList,
'json', ['groups' => 'group1']
);
You may also checkout JMS Serializer which adds #Exclude and #Include annotations to make this step a bit easier.

Symfony2 : Handling error on a single controller

I made a controller to provide some webservices in JSON and i would like to provide some errors informations when Symfony throw an exception ( Error 500 ) , how can i write such a thing ?
The main purpose of the webservice is to update informations in Symfony DB provided by the caller in POST values.
in my controller i return response in JSON and i would like to handle Symfony exception ( like when the values provided or not fitting the schema designed ) to return details informations about errors .
i thought about making a test of every values but it would be a long time to write and not e easy code to read or using a try / catch system , but i think Symfony already provide such a function .
What do you think ?
Thx :)
I think you should use an EventListener to catch errors and return the proper response.
You can place it inside your SomethingBundle/EventListener folder and also you need to define a service in order to be loaded by Symfony.
More info: Event Listener
I hope I helped you, if you think I might be wrong, let me know. Good luck!
EDIT
If you only want to catch the errors inside a specific controller (for example) a controller called Webservice inside your SomethingBundle, you must check it before doing anything:
public function onKernelException(GetResponseForExceptionEvent $event)
{
$request = $event->getRequest();
if($this->getBundle($request) == "Something" && $this->getController($request) == "Webservice")
{
// Do your magic
//...
}
}
private function getBundle(Request $request)
{
$pattern = "#([a-zA-Z]*)Bundle#";
$matches = array();
preg_match($pattern, $request->get('_controller'), $matches);
return (count($matches)) ? $matches[0] : null;
}
private function getController(Request $request)
{
$pattern = "#Controller\\\([a-zA-Z]*)Controller#";
$matches = array();
preg_match($pattern, $request->get('_controller'), $matches);
return (count($matches)) ? $matches[1] : null;
}
DANGER This code is not tested, is only an approach for you to build your own code. But, if I have something wrong on it, tell me. I'd like to keep my examples clean.
Use JsonResponse Symfony class in sandbox:
use Symfony\Component\HttpFoundation\JsonResponse;
$data = array(); // array of returned response, which encode to JSON
$data['error_message'] = 'Bad request or your other error...');
$response = new JsonResponse($data, 500); // 500 - response status
return $response;

zf2 acl doctrine 2

Actually using Zend Framework 2, I am looking for a way to implement a performant ACL strategy based on a database.
The whole idea is to directly filter the DQL queries depending on the currently logged in user, and it's permissions.
I found an implementation of this mecanisme in Symfony 2 http://symfony.com/doc/current/cookbook/security/acl_advanced.html, in this case one table seems to store for each user if he has access to a single row, so we can easily dynamically load only allowed rows by joining this table.
To synthesize,I am looking for a way to define access rules to entities based on criterias, but want to be able to get results in a single query to be able to do some ordering, and pagination.
Are there any ZF2 modules to resolve this case ?
It looks like integrating the SF2 security component as standalone is not an option: Security component from Symfony 2.0 as standalone
You have to use doctrine filter for load things for current member
example of my codes adding the filter for member query :
$em = $sm->get('doctrine.entitymanager.orm_default');
$ormconfig = $sm->get('doctrine.configuration.orm_default');
$ormconfig->addFilter("member", "\PatrickCore\Script\ORM\Functional\MemberAccessFilter");
//
$currentUser = $membersService->getCurrentUser();
$uid = $currentUser->getId();
$filter = $em->getFilters()->enable("member");
$filter->setParameter('member', $uid);
and this file \PatrickCore\Script\ORM\Functional\MemberAccessFilter :
<?php
namespace PatrickCore\Script\ORM\Functional;
use Doctrine\ORM\Mapping\ClassMetaData,
Doctrine\ORM\Query\Filter\SQLFilter;
class MemberAccessFilter extends SQLFilter
{
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
// Check if the entity implements the LocalAware interface
if (!$targetEntity->reflClass->implementsInterface('\PatrickCore\Entity\MemberAccessAware')) {
return "";
}
return $targetTableAlias.'.member_id = ' . $this->getParameter('member'); // getParameter applies quoting automatically
}
}

How to implement asynchronyous response?

I have controller with method that blocks the Play server thread due to very slow Database query. I need to implement controller method in a way that it don't block the thread.
I have read documentation: http://www.playframework.org/documentation/1.2.4/asynchronous
There's absolutely no examples anywhere on how to do this. The only thing that I found close is this https://github.com/playframework/play/blob/master/samples-and-tests/chat/app/controllers/LongPolling.java
It simply wraps result in await();
When I try to do that it doesn't work.
routes:
GET /blog Controller.blog
Controller (this is not an actual slow query but everything else is identical):
public static void blog() {
String queryStr = "SELECT b FROM Blog b ORDER BY createTime DESC";
JPAQuery q = Blog.find(queryStr);
List<Blog> bList = q.fetch(100);
List<BlogDTO> list = new ArrayList<BlogDTO>(bList.size());
for (Blog b : bList) {
BlogDTO obj = new BlogDTO(b);
list.add(obj);
}
renderJSON(list);
}
I tried List<Blog> bList = await(q.fetch(100)); but that doesn't work.
I have not worked with Future and promises before.
Can anyone give me any pointers on how to approach this?
For me the best way to do this is to use a Job that returns a List object. Then in your controller you can await for the job termination :
public static void blog() {
List<BlogDTO> list = await(new BlogPostJob().now());
renderJSON(list);
}
and you put your jpa code in your job
Because JDBC uses blocking IO, any slow database query will always block a Thread.
The only way seems to be using Job for that purpose.

web services return type as complex

I have return a web services which return "Instances" from a datamining api. Now the problem is obvious web services by default cannot handle "Instances" as return type. What should be my approach.
Or
I may have to say User defined data types, please guide me of any documentation where I can implement this.
////////////////////////////////////////////////////////
public Instances fetch(){
Properties properties =
Utils.readProperties("weka/experiment/DatabaseUtils.props");
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
String url = "jdbc:oracle:thin:#localhost:1521:XE";
java.util.Properties props = new java.util.Properties();
props.setProperty("user", "system");
props.setProperty("password", "root")
DriverManager.registerDriver(new OracleDriver());
Connection conn = DriverManager.getConnection(url, props);
Statement stmt = conn.createStatement();
ResultSet rset1 = stmt.executeQuery("SELECT * FROM iris"");
return retrieveInstances(rset1);
}
////////////////////////////////////////////////////////
thanks in advance
SR
Well, I think you should focus on data, rather than Instances. Try to serialize to XML or use JSON to pass data across.
Moreover, there are plenty of answers posted on this site:
https://stackoverflow.com/search?q=web+service+complex+data
Hope that helps,