Symfony2 Doctrine Change Value on Entity Load - php

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

Related

How to manually serialize a resource to Json in Api-Platform?

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()

Symfony - Transform Data before sending to view

Hi !
i'd like to transform data before sending it to the view. The view is json response using FOSRestBundle.
For example, I have a Product entity with field Category, that is a reference to a Category Entity. On the output, i'd like to not to display json with the whole category, but rather only its ID field.
Of course, you can further process the entity in the Controller to return only specific field from the category, but..
I wanted to ask, if there's any "standard" solution how to do that, maybe using annotation?
Thank you
I reckon the best practice or standard way to achieve what you want (filtering/processing entity data before return as JSON to view), is to make use of serializer. You can consider using the Symfony Serializer Bundle: http://symfony.com/doc/current/components/serializer.html or the JMS Serializer: http://jmsyst.com/libs/serializer.
I myself recommend the JMS one, it has really good documentation, the definition is completely controlled through annotation, and a well-thought events system and a few feature I can list as follows:
-- Expose / Exclude specific entity attributes.
-- Create Virtual Property (attributes) for the entity once it converted to JSON. (So for example if you have Entity Product with attribute Price and you want to have a calculated field in the JSON object call taxPrice you can achieve it).
-- Access Control who can access which attributes/entities by categorising attributes to different groups.
-- Pre Serialization and Post Serialization events so you can tweak the data even further.
I can say the Serializer works so well with the Forest Bundle that once you get used to it, you can hardly write your project the different way :D.

need to switch between class scopes

I have a class (PersistenceClass), that takes an array of data (posts) and parses that data and puts it into a DB (via doctrine). The field content needs to be parsed by a second class (SyntaxClass) before it is set into the doctrine entity.
Now the problem is, that the SyntaxClass has to set references in the content to other posts (just a link with and ID). So it needs access to the DB, and also needs to search in the persisted but not yet flushed entities from the PersistenceClass.
I can inject a doctrine EM into SyntaxClass and find my references in DB, although I dont like it very much. But the bigger problem is, how I can access the only persisted, but not flushed entities from the PersistenceClass ? I could make an Array of that objects and put it as an parameter to the parser method like:
SyntaxClass->parseSyntax($content, $persistedObjects);
But that does not look very clean. Aside from that, I dont know if it is somehow possible to search in the data of the persisted objects?
Your question is full of sub-question, so, first I'll try to make some things clear.
First, the naming convention you used is a bit abiguos and this not helps, me and also other people that may work on your code in future (maybe you'll grow and need to hire more developers! :P ). So, let's start with some nomenclature.
What you are calling PersistenceClass may be something like this:
class PersistenceClass
{
public function parse(array $posts)
{
foreach ($posts as $post) {
// 1. Parse $post
// 2. Parse content with SyntaxClass
// 3. Persist $post in the database
}
}
}
The same applies also for SyntaxClass: it receives the $content and parses it in some ways, then sets the references and then persists.
This is just to set some boundaries.
Now, go to your questions.
I can inject a doctrine EM into SyntaxClass and find my references in
DB, although I dont like it very much.
This is exactly what you have to do! The OOP development works this way.
But, and here come the problems with naming conventions, the way you inject the entity manager depends on the structure of your classes.
A good design should use services.
So, what currently are PersistenceClass and SyntaxClass in reality should be called PersistenceService and SyntaxService (also if I prefere call them PersistenceManager and SyntaxManager, because in my code I always distinguish between managers and handlers - but this is a convention of mine, so I'll not write more about it here).
Now, another wrong thing that I'm imaging you are doing (only reading your question, I'M IMAGING!): you are instantiating SyntaxService (you currently named SyntaxClass) from inside PersistenceService (your currently named PersistenceClass). This is wrong.
If you need a fresh instance of SyntaxService for each post, then you should use a factory class (say SyntaxFactory), so calling SyntaxFactory::create() you'll get a fresh instance of SyntaxService. Is the factory itself that injects the entity manager in the newly created SyntaxClass.
If you don't need a fresh instance each, time, instead, you'll declare SyntaxClass simply as a service and will pass it to PersistenceService by injection. Below this last simpler example:
# app/config/service.yml
services:
app.service.persistence:
class: ...\PersistenceService
# Pass the SyntaxInstance directly or a factory if you need one
aguments: ["#doctrine.orm.default_entity_manager", "#app.service.syntax"]
app.service.syntax:
class: ...\SyntaxService
aguments: ["#doctrine.orm.default_entity_manager"]
But the bigger problem is, how I can access the only persisted, but
not flushed entities from the PersistenceClass ?
Now the second question: how to search for {persisted + flushed} and {persisted + not flushed} entities?
The problem is that you cannot use the ID as the search parameter as the persisted but not flushed entities doesn't have one before the flushing.
The solution may be to create another service: SearchReferencesService. In it you'll inject the entity manager too (as shown before).
So this class has a method search() that does the search.
To search for the entities persisted but not flushed, the UnitOfWork gives you some interesting methods: getScheduledEntityInsertions(), getScheduledEntityUpdates(), getScheduledEntityDeletions(), getScheduledCollectionDeletions() and getScheduledCollectionUpdates().
The array of which you are speaking about is already there: you need to only cycle it and compare object by object, basing the search on fields other than the ID one (as it doesn't exist yet).
Unfortunately, as you didn't provided more details about the nature of your search, it is not possible for me to be more precise about how to do this search, but only tell you you have to search using the unit of work and connecting to the database if null results are returned by the first search. Also the order in which you'll do this search (before in the database and then in the unit of work or viceversa) is up to you.
Hope this will help.

How to load an entity in Symfony without special fields

i want to load an entity in my Controller, but dont want that the entity contains all fields. I did that before with the Jms-Serializer, where you can use the Groups Annotations, to avoid loading special fields. But i there you have to serialize your object to json/xml etc.
And i dont want it serialized, i just want that groups function. I searched this site and the internet, but didnt found any solution for my problem.
Hope that someone understand what i mean and got an idea :)
There are a couple of possibilities:
Use partial objects (which will deliver objects where only specified attributes will be filled during hydration): http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/partial-objects.html#partial-objects
This is dangerous and you should be extra careful, because it looks like a fully loaded entity from all perspectives. You have to know why a field is null - just because it's null or because it simply hasn't been filled during hydration.
Don't hydrate objects but query for an array as hydration result (by that again you can specify which array keys you would like to get back): http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#array-hydration
Use this for performance-sensitive queries where you need a lot of read-only data and complex joins. But be aware that you don't have any entities you can manage with Doctrine (e.g. updating, deleting etc.).
Use DTOs which are objects but non-Doctrine-managed entities, there again you can specify what you would like to get hydrated with the NEW syntax: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#new-operator-syntax
Basically the same advise as in 2) but this time you'll get objects. So you can use all your OOP wisdom.
Create your own custom hydration mode - there you can define on your own how entities should be hydrated: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#custom-hydration-modes
Very advanced level. Only useful if you need a special hydration mode for several entities and really no other option delivers at performance and quality as you require it.
You can use partial objects, but you should be careful. For example:
$q = $em->createQuery("select partial u.{id,name,otherField} from MyApp\Entity\User u");
You can read more here: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/partial-objects.html

Doctrine2 Define properties via magic methods

Using Doctrine, is it possible to map to properties which don't actually exist using magic methods?
I'm doing the mapping with YAML.
For example, if I wanted to map to a property named "demo", but SomeClass::$demo didn't actually exist. I'd want to some combination of __get(), __set(), __isset() and __call() to handle $demo (and getDemo() and setDemo()) and do something else with them.
I've tried setting this up, but I'm getting an error:
Uncaught exception 'ReflectionException' with message 'Property My\Bundle\DemoBundle\Entity\SomeClass::$demo does not exist'
I'm not sure if there is something special with the ReflectionProperty that causes it to miss my magic methods, or if I'm maybe missing a magic function. However, as far as I can tell, ReflectionProperty should interact with them.
Any ideas?
UPDATE:
Upon further investigation, it looks like the ReflectionProperty constructor will throw an exception and won't trigger the magic methods.
Does anyone else know of means to map Doctrine to dynamic properties?
Thanks.
UPDATE 2:
To example what I'm trying to accomplish.
Basically, I have a generic User object which just contains the base properties needed to handle actually being a user (roles, password, salt, username, etc.). However, I want to be able to extend this object to add application-and-user-specific meta data.
So, say I create a Forum bundle. I could then I could dynamically hook up meta data related to the user for use with the Forum. I don't want to put it directly in the User bundle, because then the User bundle becomes less flexible.
If I could somehow dynamically inject new data in to the user, it could all be loaded in a single query with the user, instead of having to be loaded in a separate query. I know there are some other methods to do this, which I've already explored and even used to a limited extend. However, it'd be much nicer if I could dynamically create these associations, which really shouldn't be that difficult of a leap.
If you don't need to search on these dynamic properties then just add a property called data to your entity and map it to a doctrine array type. Now do your majic stuff and store the dynamic properties in the data array.
A second approach might be along these lines: http://symfony.com/doc/current/cookbook/doctrine/resolve_target_entity.html. For each installation you might be able to give the administrators of making a custom entity.
But as long as you don't need to directly query on your dynamic properties then the first method works well.

Categories