A circular reference has been detected (configured limit: 1) Serializer SYMFONY [duplicate] - json

I am using Doctrine 2 and Zend framework since a few days.
I am generating my entities across yaml files.
Now I met an issue to convert my entities Doctrine into Json format (in order to use it through AJAX).
Here is the code used :
$doctrineobject = $this->entityManager->getRepository('\Entity\MasterProduct')->find($this->_request->id);
$serializer = new \Symfony\Component\Serializer\Serializer(array(new Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer()), array('json' => new Symfony\Component\Serializer\Encoder\JsonEncoder()));
$reports = $serializer->serialize($doctrineobject, 'json');
below is the return I get :
Fatal error: Maximum function nesting level of '100' reached, aborting! in /Users/Sites/library/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php on line 185
the issue seems to be the same than here :
http://comments.gmane.org/gmane.comp.php.symfony.symfony2/2659
but there is not proper solution proposed.
Any idea how can I do it ?
Cheers

I solved the same problem by writing my own GetSetNormalizer my class. Defined static variable in a class for branching
class LimitedRecursiveGetSetMethodNormalizer extends GetSetMethodNormalizer
{
public static $limit=2;
/**
* {#inheritdoc}
*/
public function normalize($object, $format = null)
{
$reflectionObject = new \ReflectionObject($object);
$reflectionMethods = $reflectionObject->getMethods(\ReflectionMethod::IS_PUBLIC);
$attributes = array();
foreach ($reflectionMethods as $method) {
if ($this->isGetMethod($method)) {
$attributeName = strtolower(substr($method->name, 3));
$attributeValue = $method->invoke($object);
if (null !== $attributeValue && !is_scalar($attributeValue) && LimitedRecursiveGetSetMethodNormalizer::$limit>0) {
LimitedRecursiveGetSetMethodNormalizer::$limit--;
$attributeValue = $this->serializer->normalize($attributeValue, $format);
LimitedRecursiveGetSetMethodNormalizer::$limit++;
}
$attributes[$attributeName] = $attributeValue;
}
}
return $attributes;
}
/**
* Checks if a method's name is get.* and can be called without parameters.
*
* #param ReflectionMethod $method the method to check
* #return Boolean whether the method is a getter.
*/
private function isGetMethod(\ReflectionMethod $method)
{
return (
0 === strpos($method->name, 'get') &&
3 < strlen($method->name) &&
0 === $method->getNumberOfRequiredParameters()
);
}
}
And usage
LimitedRecursiveGetSetMethodNormalizer::$limit=3;
$serializer = new Serializer(array(new LimitedRecursiveGetSetMethodNormalizer()), array('json' => new
JsonEncoder()));
$response =new Response($serializer->serialize($YOUR_OBJECT,'json'));

JMSSerializerBundle seems to handle circular references fine.

Related

Yii2 relation between three table using ActiveRecord

I have three tables in mysql database like this picture below :
Now, with gii, I create those model like this :
For table BuktiPenerimaan
class BuktiPenerimaan extends \yii\db\ActiveRecord{
/**
* #return \yii\db\ActiveQuery
*/
public function getInvoiceReports()
{
return $this->hasMany(InvoiceReport::className(), ['bukti_penerimaan_id' => 'id']);
}
}
And InvoiceReports:
class InvoiceReport extends \yii\db\ActiveRecord{
/**
* #return \yii\db\ActiveQuery
*/
public function getInvoiceReportDetails()
{
return $this->hasMany(InvoiceReportDetail::className(), ['invoice_id' => 'id']);
}
My question is, how to access all record in table invoice_report_detail if I created an object that came from BuktiPenerimaan.
I use like this :
$model = $this->findModel($id); // model Bukti Penerimaan.
$dataInvoice = $model->invoiceReports; //exist
$dataInvoiceDetail = $model->invoiceReports->invoiceReportDetails // failed, always null.
Please advixe
$dataInvoice = $model->invoiceReports; is Array of InvoiceReport object.
You need to loop over each InvoiceReport to get related InvoiceReportDetail.
$model = $this->findModel($id); // model Bukti Penerimaan.
$dataInvoice = $model->invoiceReports; //exist , but array of objects
$dataInvoiceDetail = [];
foreach($dataInvoice as $dInvoice):
$dataInvoiceDetail[] = array_merge($dataInvoiceDetail,$dInvoice->invoiceReportDetails );
endforeach;
// $dataInvoiceDetail contains all invoice_report_detail

Symfony 2.7 dateTime deserialize

I need some help with SF 2.7 serializer
I have made an API with get Json Data like this :
{
"dateDebut":"2017-02-16",
"dateFin":"2018-02-16",
"caMoisTotalHorsSessions":"5.2",
"caMoisClients":"5.3",
"caMoisGarantie":"5.4",
"caMoisHuile":"5.5" }
I tried many way in order to deserialze into my object Class where dateDebut and dateFin are attending to be Datetime object and not string
try {
$encoder = new JsonEncoder();
$normalizer = new GetSetMethodNormalizer();
$callback = function ($date) {
return new \DateTime($date);
};
$normalizer->setCallbacks(array(
'dateDebut' => $callback,
'dateFin' => $callback, ));
$serializer = new Serializer(array($normalizer), array($encoder));
$entity = $serializer->deserialize($request->getContent(), $class, $format);
} catch (RuntimeException $e) {
return new JsonResponse(
['code' => Response::HTTP_BAD_REQUEST, 'message' => $this->trans('api.message.data_error')],
Response::HTTP_BAD_REQUEST);
}
But callbacks are never used :/ Could anyone help me please ?
Aim is to transform date string into Datetime object automatically before flush the object in database.
Thanks a lot
What you are trying to do is denormalization. The normalizer callbacks are for normalization. I think it's pretty confusing. It's strange that they would offer setting callback for just one direction.
I tested some code doing what I think you want to do. You need a custom normalizer class. The class is not so complicated, it can extend from the GetSetNormalizer or the ObjectNormalizer. You just want to create the \DateTime inside here, and you might add some validation for the date time.
class BoardNormalizer extends GetSetMethodNormalizer
{
public function denormalize($data, $class, $format = null, array $context = array())
{
if (isset($data['created'])) {
$data['created'] = new \DateTime($data['created']);
}
return parent::denormalize($data, $class, $format, $context);
}
}
I tested it with this code:
$json = json_encode([
'created' => '2017-02-20T05:49:51-0500'
]);
$encoder = new JsonEncoder();
$normalizer = new MyCustomNormalizer();
$serializer = new Serializer([$normalizer], [$encoder]);
$entity = $serializer->deserialize($json, MyCustomClass::class, 'json');
And it produced my custom class where the created property was a \DateTime object.
Aim is to transform date string into Datetime object automatically before flush the object in database.
Something like this? Using setters/getters ? I'm using the following code in entity
private $created;
public function setCreated($created)
{
if (!($created instanceof \DateTime)) {
$created = date_create($created);
}
$this->created = $created;
return $this;
}

Problems storing image url in database on upload (CodeIgniter)

This is widely discussed topic:how to save image url in database on upload?
I have read them but still can not understand why my controller is not working. I followed CodeIgniters documentation on uploading files and created controler that uploads file in desired directory, so far so good. I now want to save image url in database on upload. I modified code from this question but I have multiple errors that I can not get around.
1.
Message: Illegal string offset 'file_name'
Filename: controllers/Upload.php
Line Number: 36
2.Message: Undefined property: Upload::$db
Filename: controllers/Upload.php
Line Number: 37
3.Message: Call to a member function insert() on null
Filename: controllers/Upload.php
Line Number: 37
Here is my controller(I have test table puzz with two fields - auto increment primary key id and image_url):
<?php
class Upload extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->helper(array('form', 'url'));
}
public function index()
{
$this->load->view('upload_form', array('error' => ' ' ));
}
public function do_upload()
{
$config['upload_path'] = './uploads/';
$config['allowed_types'] = 'gif|jpg|png';
$config['max_size'] = 100;
$config['max_width'] = 1024;
$config['max_height'] = 768;
$this->load->library('upload', $config);
if ( ! $this->upload->do_upload('userfile'))
{
$error = array('error' => $this->upload->display_errors());
$this->load->view('upload_form', $error);
}
else
{
$data = $this->upload->data();
$file_array = $this->upload->data('file_name');
$image['image_url'] = $file_array['file_name'];
$this->db->insert('puzz', $image);
$this->load->view('upload_success', $data);
}
}
}
?>
Will appreciate if someone could point to wrongs in my code!
add this before using insert
$this->load->database();
OR change constructor
public function __construct()
{
parent::__construct();
$this->load->helper(array('form', 'url'));
$this->load->database();
}
OR change in autoload.php
$autoload['libraries'] = array('database');
AND change this code block to
$data = $this->upload->data();
$image['image_url'] = $data['file_name'];
$this->db->insert('puzz', $image);
$this->load->view('upload_success', $data);

How to use Request->all() with Eloquent models

I have a lumen application where I need to store incoming JSON Request. If I write a code like this:
public function store(Request $request)
{
if ($request->isJson())
{
$data = $request->all();
$transaction = new Transaction();
if (array_key_exists('amount', $data))
$transaction->amount = $data['amount'];
if (array_key_exists('typology', $data))
$transaction->typology = $data['typology'];
$result = $transaction->isValid();
if($result === TRUE )
{
$transaction->save();
return $this->response->created();
}
return $this->response->errorBadRequest($result);
}
return $this->response->errorBadRequest();
}
It works perfectly. But use Request in that mode is boring because I have to check every input field to insert them to my model. Is there a fast way to send request to model?
You can do mass assignment to Eloquent models, but you need to first set the fields on your model that you want to allow to be mass assignable. In your model, set your $fillable array:
class Transaction extends Model {
protected $fillable = ['amount', 'typology'];
}
This will allow the amount and typology to be mass assignable. What this means is that you can assign them through the methods that accept arrays (such as the constructor, or the fill() method).
An example using the constructor:
$data = $request->all();
$transaction = new Transaction($data);
$result = $transaction->isValid();
An example using fill():
$data = $request->all();
$transaction = new Transaction();
$transaction->fill($data);
$result = $transaction->isValid();
You can either use fill method or the constructor. First you must include all mass assignable properties in fillable property of your model
Method 1 (Use constructor)
$transaction = new Transaction($request->all());
Method 2 (Use fill method)
$transaction = new Transaction();
$transaction->fill($request->all());
Create your TransactionRequest with rules extends FormRequest
public function store(TransactionRequest $request)
{
$transaction = new Transaction($request->validated());
$transaction->save();
}

Update discriminator column Doctrine2 with Symfony2

I have an entity called User which has inheritance for Student, Professional and Business.
When a user is registered, is only a User but they must update their profile and choose which kind of user is, I have a form which handles this, a controller which gets the form data, but I can't update the discriminator field type with $userEntity->setType()
This is my mapping stuff
class User
{
const TYPE_BASIC = "Basico";
const TYPE_STUDENT = "Estudiante";
const TYPE_PROFESSIONAL = "Profesional";
const TYPE_BUSINESS = "Empresa";
protected $type = self::TYPE_BASIC;
public function getType()
{
return self::TYPE_BASIC;
}
public function setType($type)
{
$this->type = $type;
}
class Student extends User
{
protected $type = self::TYPE_STUDENT;
And then Professional and Business just like Student (changing const)
<entity name="User" table="user_base" inheritance-type="JOINED">
<discriminator-column name="type" type="string"/>
<discriminator-map>
<discriminator-mapping value="Basico" class="User"/>
<discriminator-mapping value="Estudiante" class="Student"/>
<discriminator-mapping value="Profesional" class="Professional"/>
<discriminator-mapping value="Empresa" class="Business"/>
</discriminator-map>
the child tables are named user_xxx where xxx = Student/Professional/Business
And this is my controller
if($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$data = $form->all();
$type = $data['type']->getData();
$email = $data['email']->getData();
$profile = $data['profile']->all();
$name = $profile['name']->getData();
$lastName = $profile['lastName']->getData();
$birth = $profile['birth']->getData();
$profileEntity = new Profile();
$profileEntity->setBirth($birth);
$profileEntity->setName($name);
$profileEntity->setLastName($lastName);
$profileEntity->setUser($user);
$em->persist($profileEntity);
ladybug_dump($type);
$userEntity = $em->getRepository('User')->find($user);
$userEntity->setProfile($profileEntity);
$userEntity->setType($type);
if($user->getEmail() != $email)
$userEntity->setEmail($email);
$em->persist($userEntity);
$em->flush();
}
Everything is persisted but type field, which remains it's original data. I know when I change discriminator column I need to create a new row inside it's child element, but first I want to know how to change the discriminator column.
it is possible if you use this custom bit of code in the Form of a Trait which you can use inside a Repository.
The Trait:
namespace App\Doctrine\Repository;
use App\Exception\InvalidDiscriminatorClassException;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
/**
* Discriminator Trait
*/
trait DiscriminatorTrait
{
/**
* #return ClassMetadata
*/
abstract public function getClassMetadata();
/**
* #return EntityManager
*/
abstract public function getEntityManager();
/**
* Update Discriminator Column
*
* #param integer $id
* #param string $class
* #return boolean
* #throws InvalidDiscriminatorClassException
*/
private function updateDiscriminatorColumn($id, $class)
{
/* #var ClassMetadata $classMetadata */
$classMetadata = $this->getClassMetadata();
if (!in_array($class, $classMetadata->discriminatorMap)) {
throw new InvalidDiscriminatorClassException($class);
}
$identifier = $classMetadata->fieldMappings[$classMetadata->identifier[0]]["columnName"];
$column = $classMetadata->discriminatorColumn["fieldName"];
$value = array_search($class, $classMetadata->discriminatorMap);
/* #var Connection $connection */
$connection = $this->getEntityManager()->getConnection();
try {
$connection->update(
$classMetadata->table["name"],
[$column => $value],
[$identifier => $id]
);
}
catch (DBALException $e) {
return false;
}
return true;
}
}
According to the Doctrine documentation on Inheritance mapping, it is not possible to either get or set the type. You may wish take advantage of PUGXMultiUserBundle, which readily handles the mapping. This bundle also makes it possible for your users to register with the appropriate profile.