Assign non persisted object to view using extbase - typo3

While trying to assign an object to a view in my controller action I get the following message because this object is not persisted:
Could not serialize Domain Object Vendor\Extension\Domain\Model\Object. It is neither an Entity with identity properties set, nor a Value Object.
Is there any possibility to add this object to the view without creating a databaseentry?

The exception [InvalidArgumentValueException('Could not serialize Domain Object $className. It is neither an Entity with identity properties set, nor a Value Object.', 1260881688)][1] is thrown in the UriBuilder, thus when a model shall be used as argument for creating a link.
The instance of Vendor\Extension\Domain\Model\Object must either fulfill these requirements:
can be represented as array (is array or implements Iterator interface) OR
extends TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject AND one of
extends TYPO3\CMS\Extbase\DomainObject\AbstractValueObject OR
having a valid uid, not null
Thus, if you instantiated the object directly in the controller, the uid property is not defined yet. This property is assigned if domain objects are fetched or added with a repository.
TypeConverters
TypeConverters allow to convert from a given identifier (some string representation, hash-value, ...) to a proper domain object. The following links show how to do that for the concept of an IBAN (International Bank Account Identifier).
IBAN model
TypeConverter to create object from string
TypeConverters have to registerd in ext_localconf.php like this:
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter(
\H4ck3r31\BankAccountExample\Domain\Property\TypeConverter\IbanTypeConverter::class
);
The Iban object can be used then in your controller:
public function someAction(Iban $iban) { ... }
Use array representation of your object
Another alternative could be to assign an array representation of the domain object to the view and use that to fill the link arguments:
$this->view->assign('myObject', $object->toArray());
When invoking a controller action, the object is reconstituted from the submitted array keys and are used as properties - thus array keys and properties must have the same naming, or a persistence column mapping is defined.
public function someAction(MyObject $object) { ... }
In my previously mentioned bank account example it looks like this:
BankDto model
Controller action invocation
The term "Dto" is the abbreviation for "Data Transfer Object", thus it's not a real domain entity, does not have a proper UID and is just used to encapsulate information in a domain object when passing that to different components.

Related

Creating a Domain Class with Some Smarts

So I have a schema that is well defined. The datastorage that backs it will allow for this request. (MongoDB).
Lets say I have a Users class:
class User
emailAddress
name
If I'm merging in data from another source (lets say a map/params, and I can properly identify the source.) My intention is to put the unused properties in a structure within the User class.
For example: If I'm importing a User from facebook, they're going to have all kinds of properties outside just the emailAddress, or the name. BUt I don't know how to deal with those yet.
My question is: How would I design a domain class so that it can handle all of this on the creation of the object? (I'm willing to put a tracer property in to signify the source) [I.e. adding [source: Facebook]]
The outputting class would look, and be serialized as such:
The info coming back from Facebook would be [name: Jim, email: bo#jim.com, friends:1000, level:42]. The resulting class would be:
class User
emailAddress : bo#jim.com
name: Jim
extraProperties: [Facebook, [friends:1000, level:42]]
What is the best way of going about this? Would it break the domain class model? Is expando something that would work here?
I think the best way to design your domain class would be to look into saving the additional user's properties (extraProperties) as a serialised 'document' type object. If you were to convert the sample Map you have into say, JSON/GSON or XML (Converters) and save this to your database as a document / large nvarchar, you then have the flexibility of different properties for each user source.
You could then add custom getters and setters to your domain object which would convert / slurp the document, and present it as a map to your controllers/services
String extraProperties
def setExtraProperties(def properties){
this.extraProperties = (properties as JSON)?.toString()
}
def getExtraPropertiesMap() {
def jsonSlurper = new JsonSlurper()
def extraProps = jsonSlurper.parseText(this.extraProperties)
return extraProps //you can then access this using map syntax, eg. extraProps.Facebook.friends
}

JAX-RS consuming a custom object list inside a custom object

What is the way to consume a list of custom objects inside another custom object in JAX-RS CXF implementation? As an example my object looks like below
#POST
#Produces({MediaType.APPLICATION_JSON})
#Path("test")
public Response myMethod(MyCustomObject myCustomObject) {
Inside MyCustomObject it has a list of another custom object which reside inside this as an inner class
public class MyCustomObject {
private List<MyInner> innerObjects;
public class MyInner {
private String property;
....
}
....
}
Request JSON object is passed as the POST body of the request. When I debug this I could get the MyCustomObject passed properly while I am sending the innerObjects list as null. But it seems its not picking this correctly when I have this array based structure there with a custom object. Additionally instead of this custom object array when I have a primitive type or a string based array the service works fine. How to deal with the above scenario.
It is probably because of the inner class.
Similar question here
Not sure what mapper you use (cxf default is jettison but it is all configurable), but the case is probably similar.
Great explanation here
non-static inner classes (including anonymous ones) have set of hidden variables added by compiler, passed via (hidden) constructor. And as a consequence, do not have zero-argument ("default") constructor

Loading related objects without Include()

Briefly, I'm loading objects that descend from a base class using a repository defined against the base class. Although my objects are created with the correct descendant classes, any descendant classes that add navigation properties not present in the base class do not have those related objects loaded, and I have no way to explicitly request them.
Here is a simple method in a repository class that loads a given calendar event assuming you know its ID value:
public CalendarEvent GetEvent(int eventId)
{
using (var context = new CalendarEventDbContext(ConnectionString))
{
var result = (from evt in context.CalendarEvents
where eventId.Equals((int)evt.EventId)
select evt).ToList();
return result.ToList()[0];
}
}
CalendarEvent is a base class from which a large number of more specific classes descend. Entity Framework correctly determines the actual class of the calendar event specified by eventId and constructs and returns that derived class. This works perfectly.
Now, however, I have a descendant of CalendarEvent called ReportIssued. This object has a reference to another object called ReportRequest (another descendant of CalendarEvent, although I don't think that's important).
My problem is that when Entity Framework creates an instance of ReportIssued on my behalf I always want it to create and load the related instance of ReportRequested, but because I am creating the event in the context of generic calendar events, although I correctly get back a ReportIssued event, I cannot specify the .Include() to get the related object. I want to do it through this generically-expressed search because I won't necessarily know the type of eventId's event and also I have several other "Get" methods that return collections of CalendarEvent descendants.
I create my mappings using the Fluent API. I guess what I'm looking for is some way to express, in the mapping, that the related object is always wanted or, failing that, some kind of decorator that expresses the same concept.
I find it odd that when saving objects Entity Framework always walks the entire graph whereas it does not do the equivalent when loading objects.

ViewModel not matching model causes modelstate to be invalid

I have a model class like this:
class Person {
string FirstName,
string LastName,
string ID
}
When I send the model to the browser via a GET, I send the data as a composite of two fields (e.g. FirstName.ToString() + LastName.ToString()) through an anonymous type.
The problem comes when I do a POST back to the server. Since the JSON is coming back as different from the model, it comes back as invalid through ModelState.IsValid() because my action method is expecting a List<Person> persons.
I really don't want to create a ModelViewModel duplicating code, because one field is causing the model to be invalid. Is there a way around this?
In this case, I would add DataAnnotations to the ViewModel class and change the Action to accept the ViewModel rather than the Model.
Once you validate that the ViewModel being passed to the Action is valid, you can parse the composite field back to into the First and Last names properly.

What is the "Func<object> modelAccessor" parameter for in MVC's DataAnnotationsModelMetadataProvider?

It's one of the parameters supplied to the CreateMetadata method (which you override if extending metadata support).
ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor, <<--THIS ONE
Type modelType,
string propertyName)
I had assumed that it allowed you to access the model object itself (e.g. for setting metadata based on model values), however when I try to use it to cast to my model object I just get null.
Entity ent = (Entity)modelAccessor(); // = Null
If I've missunderstood, can anyone explain what it's purpose is? Or alternatively, how to properly use it?
Thanks
We originally had that as "object model", rather than "Func modelAccessor". We had to change it late in MVC 2's ship cycle.
The purpose is to delay retrieving the actual value of the model until such point as you know you're going to need it (that is, until you call ModelMetadata.Model).
The problem it solves is actually a rather esoteric one related to model binding against a LINQ to SQL class that has a foreign key reference in it. The problem is, if you've retrieved the child object which is represented by a foreign key relationship (which usually means a delay load of that object), then you're no longer allowed to choose a new child object by setting the foreign key ID property. It's very common to model bind the foreign key ID (and not the whole foreign key entity) when model binding, but if we'd retrieved the foreign key entity object (for the purposes of populating the ModelMetadata class) then that binding would no longer be legal, and actually throw an exception. Since ModelMetadata is used for both directions of models -- inbound, via model binding, and outbound, via HTML generation -- we needed to introduce the layer of indirection to protect your ability to use it in both scenarios without disrupting LINQ to SQL's rules.
The modelAccessor parameter does not point to an instance of the object, but rather it is a function that will access some attribute of your object. The Func "encapsulates a method that has no parameters and returns a value of the type specified by the TResult parameter." For example, if we have following class:
public class Bar(){
[DisplayName("I am Foo.")]
public string Foo{get;}
}
When the CreateMetaData is called, it will be to create meta data for the Foo property and the modelAccessor will be a function that returns the value of Foo.
I did a little digging and found a way to get to the instance of the object, but it requires using reflection. You can do the following to get the Bar class in my example:
if (modelAccessor != null)
{
//Use reflection to get the private field that holds the Bar object.
FieldInfo container = modelAccessor.Target.GetType().GetField("container");
//Invoke field on the modelAccessor target to get the instance of the Bar object.
Bar myObject = (Bar)container.GetValue(modelAccessor.Target);
}
I've only run this against a simple test case, so your mileage may vary, but hopefully this will help clarify what is going on.