Controlling which nested resources serializer group context is applied - symfony

I have four classes: MainEntity, SubEntityA, SubEntityB, and CommonMiniEntity.
MainEntity has a SubEntityA and SubEntityB property, and both SubEntityA and SubEntityB have a CommonMiniEntity property.
CommonMiniEntity has two properties where one uses the normal group and the other uses the extend group.
MainEntity
SubEntityA
CommonMiniEntity
- propertyA #[Groups(['normal'])]
- propertyB #[Groups(['extended'])]
SubEntityB
CommonMiniEntity
- propertyA #[Groups(['normal'])]
- propertyB #[Groups(['extended'])]
How can I instruct the serializer to only apply a given group to select sub-resources when serialized and deserialized? For instance, serializing MainEntity with normal applied to both and extended only applied to SubEntityB should return the following:
{
"subEntityA" : {
"propertyA": 111
},
"subEntityB" : {
"propertyA": 222,
"propertyB": 333
}
}

You can create custom normalizers to do this and inject other normalizer like done in this example.
https://symfony.com/doc/current/serializer/custom_normalizer.html
When you call the original normalizer, you can add some groups to the $context variable

Related

Unique constraint at field in collection

is it possible to make this validation:
class Man {
#Unique
String name;
}
class Order {
#Valid
List<Man> manCollection;
}
where is unique logic is: every item in collection manCollection is unique.
You could make this snippet ambiguous just by adding a Customer class that contains a List of Orders:
class Man {
#Unique
String name;
}
class Order {
#Valid
List<Man> manCollection;
}
class Customer {
#Valid
List<Order> orderCollection;
}
Then one couldn't possibly know whether the Man objects must be unique within a given Order or within a given Customer (or both).
So I don't think it's possible with this exact syntax, regardless of what the Bean Validation APIs allow.
What you could do is move the annotation to manCollection, e.g. #UniqueMen List<Man> manCollection;, and implement a ConstraintValidator<List<Man>>.
If it's useful to you, you could even make a more generic #UniqueContent annotation, but that would be much more complex. You would need to pass the target type as a parameter (#UniqueContent(target = Man.class)) and write a validator that parses annotations on the target class in its initialize method. Be careful to use some caching mechanism, though, because annotation parsing is quite slow.

Jackson custom deserializer module to abstract class

I have a big set of classes (like more that 100) and they are all extend from some abstract class, let's call it ParentClass. Let's call child classes ChildA,ChildB, etc. How can I register custom deserializer for all children and get class type inside my Deserializer?
I tried:
module.addDeserializer(ParentClass.class, new MyObjectDeserializer());
but it does not work.
I want to skip doing (what is working):
module.addDeserializer(ChildA.class, new MyObjectDeserializer(ChildA.class));
module.addDeserializer(ChildB.class, new MyObjectDeserializer(ChildB.class));
module.addDeserializer(ChildC.class, new MyObjectDeserializer(ChildC.class));
//etc......
Class type should be known, as I am use Jackson for spring #RequestBody method, what have defined class name there.
Any ideas how this can be done?
As far as I know, I don't think there is a mechanism in jackson that will address your exact needs.
However, there are a couple alternatives you can try.
Deserializing polymorphic types with Jackson describes one such alternative, however, you would still need to explicitly define all of the supported subtypes.
Another alternative that would not require you to explicitly define deserialization relationships would be to change your class hierarchy from one of inheritance to that of a container.
For example, converting your abstract parent class to a container like so:
public class DataContainer<T> {
String commonString;
Integer commonInteger;
T subData;
}
Would allow you to simply define in your controller input function as
public String controllerFunction(DataContainer<ClassA> classA);
without a need to define all these subclass deserializations.
Late to the party but I had a similar problem which I solved by registering a custom Deserializers to my SimpleModule. The code is in Kotlin but it should be easy to port it to Java.
The class itself:
class UseBaseClassSimpleDeserializers(
private val baseClass: Class<*>,
private val baseClassDeserializer: JsonDeserializer<*>
) : SimpleDeserializers() {
#Throws(JsonMappingException::class)
override fun findBeanDeserializer(
type: JavaType?,
config: DeserializationConfig?,
beanDesc: BeanDescription?
): JsonDeserializer<*>? {
val beanDeserializer = super.findBeanDeserializer(type, config, beanDesc)
return if (beanDeserializer == null && baseClass.isAssignableFrom(type!!.rawClass)) {
baseClassDeserializer
} else {
beanDeserializer
}
}
}
How to register the custom Deserializers class to a SimpleModule:
val simpleModule = SimpleModule()
simpleModule.setDeserializers(UseBaseClassSimpleDeserializers(ParentClass::class.java, ParentClassDeserializer()))

Get Custom Attributes applied to generated entities via MetadataType attribute

In our application, we are using EF4.0 and POCO Entity generator to generate Entities from the database. To apply data annotation, we are creating Interfaces and implementing those interfaces on the partial classes we have created to match the partial class generated by using EF.
/*Entity Generated Type*/
public partial class SomeEntity : EntityBase
{
public virtual string SomeProperty
{
get {...}
set {...}
}
}
/*Interface containing metadata*/
public interface ISomeEntityMetadata
{
[SomeCustomAttribute]
string SomeProperty { get; set; }
}
/*Partial matching class for interface*/
[MetadataType(typeof(ISomeEntityMetadata))]
public partial class SomeEntity : ISomeEntityMetadata
{
}
Now, using reflection, when we try to get if 'SomeCustomAttribute' is applied on 'SomeEntity.SomeProperty', it returns that the attribute is not applied.
If we edit the generated code and apply the Attribute directly, it works.
If we check for the partial classes merging to form a single type, it does.
If we check for the MetadataType attribute to be applied on the type (using reflection), it is.
Also, when the entity is bound to any WPF's UI-Element, the validations work as they should but using reflection we are unable to find the Validation Attributes and/or Custom Attributes applied on the property.
Any help or pointers would save a soul.
But interface / class marked with MetadataType attribute will never add those attributes to your original class so you can never find them on the class with reflection. That is not a purpose of MetadataType attribute. If you want to use reflection you must first find MetadataType attribute, check the type passed to that attribute and search for your custom attribute in that type. That is how validation uses MetadataType.

Best way for extending a entity in doctrine

I have a User Entity in a small Framework made by me. Now i want to use this User Entity in several projects.
But in some projects I want to add a few fields to the User Entity without modifying the file.
What I tried so far:
I created a new DefaultUser Entity in a DefaultUser Bundle and made the User Entity a mappedsuperclass. But now I can't make a association in other entities like
/*
* #ORM\ManyToOne(targetEntity="User", inversedBy="jobs")
* #ORM\JoinColumn(name="user", referencedColumnName="id")
*/
private $user;
Because Doctrine can't find the id column in the user entity. This only works if I specify the DefaultUser Entity. According to the doctrine documentation this only works on many to many associations if only one leaf exists.
Then I tried Single Table Inheritance. This works fine but I have to modify the DiscriminatorMap if I want to extend my user entity which is shared acros multiple projects...
So whats the best way to extend the UserEntity?
I have precisely the same problem - I have just switched from RedBean to Doctrine (for a project using the Zend Framework), and the structure of my classes did not take into account this issue. The core problem is that maps in Doctrine have a one to one relationship with classes, as far as I can work out. What we are looking for is a way to have one concrete class (the UserEntity) that uses a map from an abstract class (the DefaultUser). My solution, which may be something of a hack (I've only been using Doctrine for a couple of days), works for YAML at least:
Create a new mapping driver extending the YAML driver, and override the _loadMappingFile method with something like this:
class MyLibrary_Doctrine_Mapping_Driver_YamlExtended extends MyLibrary_Doctrine_Mapping_Driver_YamlExtended
{
protected $_basicEntityFolder;
protected function _loadMappingFile($file)
{
$entMaps = parent::_loadMappingFile($file);
//merge this with any extensions if defined
foreach($entMaps as $ent => $map)
{ //load the relevant map
if (!isset($map['extendEntity'])) {
continue;
}
$fileName = $this->_basicEntityFolder . DIRECTORY_SEPARATOR . str_replace('\\', '.', $map['extendEntity']) . $this->_fileExtension;
$extendedMaps = $this->_loadMappingFile($fileName);
if (!is_array($extendedMaps[$map['extendEntity']])) {
throw new MyProject_Doctrine_Exception("Entity to extend from could not be found.");
}
//merge so that the file lower in the class hierachy always overrides the higher
$map = array_merge($extendedMaps[$map['extendEntity']], $map);
//clear the extendEntity value
unset($map['extendEntity']);
$entMaps[$ent] = $map;
}
return $entMaps;
}
public function setExtendedEntitiesFolder($path)
{
$this->_basicEntityFolder = $path;
}
}
I then have two yaml files, in different folders, like this:
#MyApplication/Entities/Maps/Entities.User.dcm.yml
Entities\User:
extendEntity: LibraryEntities\User
That is the file in the application. Then in the library I have
#Library/Entities/Maps/ExtendedEntities/LibraryEntities.User.dcm.yml
LibraryEntities\User:
type: entity
table: user
fields:
username:
type: text
nullable: true
password:
type: text
nullable: true
defaultProfile:
type: text
nullable: true
column: default_profile
The reason it is in an ExtendedEntities folder is so I can define mappedSuperclasses in the library using a normal namespace, and Doctrine will load those automatically when a class extends them, but these extendedentities are outside of the path for Doctrine's usual class inheritance loading (if they were all in the normal folder structure then for eg "class ApplicationUser extends LibraryUser" Doctrine would try to load the config for LibraryUser because it would find it, and then cause the same error you have already encountered).
Then when I set up my $em I provide it with my driver:
$driverImpl = new MyLibrary_Doctrine_Mapping_Driver_YamlExtended(array(APPLICATION_PATH . '/entities/maps',
LIBRARY_PATH . '/Entities/Maps'));
$driverImpl->setExtendedEntitiesFolder(LIBRARY_PATH . '/Entities/Maps/ExtendedEntities');
Notice that this solution allows inheritance chains defined by 'extendEntity' (because the _loadMappingFile method is recursive). Also that any configuration file lower down the chain can overwrite any property already defined, so even if in your library yaml you had:
username:
type: text
Say you had a project in which usernames where integers you can simply override it in your application config with
username:
type: int
or whatever.
And therefore this solves the problem of defining Doctrine style inheritance on the base class. In every project you can define the DiscriminatorMap however you like.
In principle the same solution could be applied to annotations, though extending the annotation driver is a little more complicated, because it doesn't simply read metadata by reading one file in one go and converting it to an array, but makes numerous requests to the annotation reader, which means implementing this structure would be trickier.
I'd be very interested to know how other people have solved this problem.

Create a Composite Property in an Entity Framework Model?

I have two properties ("FIRST_NAME" and "LAST_NAME") I need to access as a single property (e.g. "FULL_NAME"). Is there a way for me to add a property to my entity model that contains the combine value of FIRST_NAME and LAST_NAME?
Since the model classes which are created by EF4 are usually partial classes you have the option to extend the classes in a separate file with your additional properties and methods. There you could add a readonly property with only a Getter to return your combined full name:
public partial class Person
{
public string FullName
{
get
{
return string.Concat(FirstName, " ", LastName);
}
}
}
This is a property which is only in your model class but not mapped to the database and it doesn't exist as a column in the database. Because you create this part of the partial class in a separate file it is not touched and overwritten by the model designer if you should change the model.

Resources