I have a custom operation that, once called, performs some more tasks than simply adding an entity.
The result of these operations is the creation of a new entity that is then persisted in the database.
Now, as a return value I'm currently using the IRI of the just created entity.
I generate the IRI using \ApiPlatform\Core\Api\IriConverterInterface.
This approach works but has a drawback: in the frontend I have to issue a new call to retrieve the data of the just created entity.
To avoid this call, I'd like to simply return the entity in the JSON format, but I don't find a way to immediately serialize it to return it.
In practice: I currently return simply the IRI of the entity: how can I return the fully serialized entity, according to its serialization configuration?
If your custom operation controller returns an instance (or a collection of instances) of an Api-Platform managed entity instead of a Response, the object will be automatically serialized with your desired serializer configuration (using the same serialization groups you've already defined, etc).
If you return instead an instance of Response, the SerializeListener will not serialize it and just return it unchanged. If not, it will serialize it. You can see it working here.
I am not sure, if you are just asking for the php-internal json-serialization:
https://www.php.net/manual/de/function.json-encode.php
Which can be included in any Model-class by just implementing the JsonSerializable Interface. Just return an alternative structure, which will be serialized.
https://www.php.net/manual/de/class.jsonserializable.php
You can use spl_object_hash() or spl_object_id(), see SplObjectStorage::getHash()
Related
I have a laravel Model with a one to many relationship which the user can edit via a multiple select tag.
Before exporting the model as a JSON, I use the "pluck" method to get an array of related IDs instead of an array of models, so that they can be used in the select tag and later be synced again with the "sync" method of Laravel.
However the result of "pluck()" seemingly doesn't persist over serialization. The following code doesn't work -upon serialization, "relationship" becomes again an array of objects-
$model->relationship = $model->relationship->pluck('id');
This one, however, does what it should: somePropertyIHaveJustCameUpWith is an array of IDs
$model->somePropertyIHaveJustCameUpWith = $model->relationship->pluck('id');
1) Why does this happen?
2) I have seen there is this resources way in the documentation, but creating an entire new class for something that could be solved with a single line of code feels like a bit overkill. Isn't there a cleaner way to do that?
I think this is likely a result of the way the model implements toArray().
The you can trace the steps taken, but eventually the relations are read from the $this->relations property on the Model, not from each individual relationship.
So, instead of setting the value of your relation directly like:
$model->relationship = $newValue
... you could try setting it using:
$model->setRelation('relationship',$newValue)
This will update the $model->relations property.
This should allow the toArray() method to get the new value that you set when serializing.
Note that the toJson() method in turn calls the toArray() method when serializing. So either approach will be the same result.
I serialized an object of PHP class in Laravel. Afterwards, I changed the class definition adding a new method and when I unserialize the old object and make a call to that newly implemented method, it works.
I wonder why and how?
Because serialization process keeps only state of the objects (fields) but not your methods with implementation.
I'm building an API for a Symfony2 project using FOSRestBundle, and I often simply return Doctrine objects to be encoded in JSON. Typically this way :
/**
* #Rest\View()
*/
public function getEventsAction(Request $request)
{
// security checks
return $this->getDoctrine()->getRepository('SomeBundle:Event')->findAll();
}
Thing is, this returns all object properties and relationships, and in many cases I don't want that, for example with a User object that contains the hashed password and everything.
Is there a way to set up automatic filters when encoding Doctrine objects in JSON ? Or do I have to create a QueryBuilder only fetching required data ?
I highly recommend using the JMSSerializerBundle, it will give you as many options as you need. Once you have included it in your project, you can configure your entities to only have specific properties serialized. There are quite a few ways to do it, you can see all the different ways in the exclusion strategies part of the documentation.
You are able to exclude specific properties and much more. Take a look.
Also, take a look at my other answer for a bit more info on how to use it with FOSRestBundle
The documentation warns a few times about ensuring wakeup and clone implementations on Doctrine 2 entities, e.g.:
http://docs.doctrine-project.org/en/latest/cookbook/implementing-wakeup-or-clone.html
It doesn't say why, however... I presume it has something to do with not messing with Doctrine's Entity Manager and the in-memory-cache it might maintain, but a cursory dive into the (enormous) code base doesn't say much more, nor did a quick search of the users list.
Might anyone knowledgeable with the Doctrine internals know precisely why Doctrine 2 needs safe wakeup and clone methods on entities?
Doctrine 2 needs to convert data fetched from the database into entities (php objects). This process is called hydration.
Doctrine 2 doesn't use the traditional method of using the new operator (thus constructing a class), but it uses the following method:
It builds a string which represents a serialized empty version of your class.
It unserializes that string to make it an actual object. This is why you need to safely implement __wakeup.
It stores that object as prototype.
This process is done once for each entity class.
Then whenever it needs to hydrate an entity, it will clone the prototype. This is why you need to safely implement __clone.
This is all done a small method newInstance() in ClassMetadataInfo.
The advantage of doing this is that the constructor isn't used when a new entity is hydrated. That leaves the developer free to do anything he/she wants in the constructor (including using parameters).
update
The reason why you need to safely implement the __wakeup and __clone methods is this:
Because Doctrine 2 unserializes and clones entities when it needs to hydrate them, those methods will get called. But when Doctrine 2 does this, the entities will not have any data set (not even the identifier(s)). The data will be set afterwards.
So when you implement them in such a way that your logic is only performed when the entity does have the identifier(s), you are sure that your logic is only performed when you unserialize or clone the entity, not when Doctrine 2 does it.
Jasper's answer nails the precise reason, but it doesn't give the background — as in why Doctrine is proceeding to unserialize and clone entities.
The rational for doing so is to allow entities to define whatever constructor they want, as explained in this blog post:
http://www.doctrine-project.org/2010/03/21/doctrine-2-give-me-my-constructor-back.html
At ConFoo 2010 during my presentation, someone asked about the constructor of entities in Doctrine 2 and whether or not it could be used. I think this is something worth writing about since in Doctrine 1 this was not possible. The constructor was hi-jacked from you and used internally by Doctrine.
In Doctrine 2 it is possible to define the constructor in your entity classes and is not required to be a zero argument constructor! That’s right, Doctrine 2 never instantiates the constructor of your entities so you have complete control!
This is possible due to a small trick which is used by two other projects, php-object-freezer and Flow3. The gist of it is we store a prototype class instance that is unserialized from a hand crafted serialized string where the class name is concatenated into the string. The result when we unserialize the string is an instance of the class which is stored as a prototype and cloned everytime we need a new instance during hydration.
Have a look at the method responsible for this:
<?php
public function newInstance()
{
if ($this->_prototype === null) {
$this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
}
return clone $this->_prototype;
}
I have a database structure which has a field content. In the database this is a BLOB (so a string). content contains a JSON-encoded string.
When I load this, I would like to load it in to a specific Content object (with a different Content subclass for each possible format of the JSON).
Are there any events or anything which I can hook up so I can catch the value right before the Entity is built (so I can have setContent() type-hint the Content class instead of having to be generic, which I'd have to do if the information is loaded in to the Entity before I intercept).
Any ideas?
A bit more details. Basically what I imagine is having some sort of Factory class which takes a JSON string and converts it to a proper object.
{
body: "ABC",
value: 5
}
Goes to an object of a class like this:
class MyContent extends Content
{
protected $body;
protected $value;
}
I can't use typical object mapping because it's a JSON string to an object.
Further clarification
Basically, I have an entity named Box. Box has a content value, which is supposed to be an instance of Content.
Under normal circumstances, if Content was a normal database Entity, I would just hook up a One-to-One relationship between Box and Content, which would load Content properly in to the Box without needing to do anything special.
However, in this case, Content can have many forms. To handle this, it is stored as a JSON object in a BLOB field in Box's table. This means when Doctrine tries to load Box, it will try to load a string.
I could simply have Box::setContent() accept any parameter and deal with it accordingly based on if it is a string or Content object.
However, I'd like it so when it is used it is always a Content object, so I want type-hinting for the function (i.e., Box::setContent(Content $content)). The problem is this would prevent Doctrine from giving that field a string.
Which is why I want to intercept the value Doctrine has for content and replace it with a proper object before it loads it in to the entity Box.
I don't think any of the Doctrine events do exactly what I want, so it may not really be possible. =S
You should be able to add code to the entities get method to do this. You can also check out the lifecycle call backs for doctrine: http://docs.doctrine-project.org/en/2.0.x/reference/events.html#lifecycle-callbacks
You can use JMSSerializer to create entities from JSON - https://github.com/schmittjoh/JMSSerializerBundle.
And I can`t understand you question, but maybe you can use json_array type - http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#doctrine-mapping-types
You need to load data from DB and than create entities? Or load entities with some field that have nested entity deserialized from JSON?
Ultimately what I ended up doing was just creating different tables and Entities for each possible Content type and then using Doctrine's inheritence to let Doctrine handle them. Not idea (since there may be many, many types which means more tables and I can't just dynamically constructor new types), but it works well for now.
Doctrine Inheritence