I have an web application in which I generate POJOs from my domain objects. One of my domain objects contain a map and JAXB generates the following schema:
<xs:element name="persons">
<xs:complexType>
<xs:sequence>
<xs:element name="entry" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="key" minOccurs="0" type="xs:string"/>
<xs:element name="value" minOccurs="0" type="person"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
This is generated from HashMap<String, Person> persons:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "personConfiguration", propOrder = {
"persons",
})
#XmlRootElement(name = "personConfiguration")
public class PersonConfiguration
{
#XmlElement(required = true)
protected PersonConfiguration.Persons persons;
/**
* Gets the value of the persons property.
*
* #return
* possible object is
* {#link PersonConfiguration.Persons }
*
*/
public PersonConfiguration.Persons getPersons() {
return persons;
}
/**
* Sets the value of the persons property.
*
* #param value
* allowed object is
* {#link PersonConfiguration.Persons }
*
*/
public void setPersons(PersonConfiguration.Persons value) {
this.persons = value;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"entry"
})
public static class Persons
{
protected List<PersonConfiguration.Persons.Entry> entry;
/**
* Gets the value of the entry property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the entry property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getEntry().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {#link PersonConfiguration.Persons.Entry }
*
*
*/
public List<PersonConfiguration.Persons.Entry> getEntry() {
if (entry == null) {
entry = new ArrayList<PersonConfiguration.Persons.Entry>();
}
return this.entry;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"key",
"value"
})
public static class Entry
{
protected String key;
protected Person value;
/**
* Gets the value of the key property.
*
* #return
* possible object is
* {#link String }
*
*/
public String getKey() {
return key;
}
/**
* Sets the value of the key property.
*
* #param value
* allowed object is
* {#link String }
*
*/
public void setKey(String value) {
this.key = value;
}
/**
* Gets the value of the value property.
*
* #return
* possible object is
* {#link Person }
*
*/
public Person getValue() {
return value;
}
/**
* Sets the value of the value property.
*
* #param value
* allowed object is
* {#link Person }
*
*/
public void setValue(Person value) {
this.value = value;
}
}
}
}
As one can see JAXB added this extra level of indirection entry -> key, value. Other pieces of the puzzle are Spring MVC, REST call using JSON objects.
Now XML based REST Calls work fine with object schema above but when send the same call with JSON message with the same schema I get a JSONMappingException.
Any ideas of why this might be happening?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
When using different XML and JSON-binding providers it is difficult to keep the XML and JSON representations consistent. Below is and example of how this can be simplified using MOXy as both your XML and JSON provider with all mapping information provided as JAXB annotations.
Root
Below is a sample domain object in which the persons field would generate the XML schema fragment from your question.
package forum13784163;
import java.util.Map;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
Map<String, Person> persons;
}
Person
Below is a sample of what your Person class might look like. Note how I have mapped the id field to an XML attribute.
package forum13784163;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Person {
#XmlAttribute
int id;
String name;
int age;
}
jaxb.properties
To use MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
input.json
Below is what the JSON representation would look like if MOXy is used as your JSON binding provider.
{
"persons" : {
"entry" : [ {
"key" : "Jane",
"value" : {
"id" : 123,
"name" : "Jane",
"age" : 30
}
} ]
}
}
Demo
In the demo code below the JSON is unmarshalled into objects and then those same objects are marshalled to XML. This is done from one JAXBContext that contains one set of metadata.
package forum13784163;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.UnmarshallerProperties;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json");
unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);
StreamSource json = new StreamSource("src/forum13784163/input.json");
Root root = unmarshaller.unmarshal(json, Root.class).getValue();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
Below is the resulting XML.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<persons>
<entry>
<key>Jane</key>
<value id="123">
<name>Jane</name>
<age>30</age>
</value>
</entry>
</persons>
</root>
For More Information
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html
Related
How to map avro schema to a LocalDate / LocaldateTime?
The spec says we can use logicalType. But DOES NOT CARE TO SHOW AN EXAMPLE of how it maps to LocalDate in Java. Why is a good example not part of the spec? that is for the specwriters.
Anyay
e.g.
{"namespace": "example.avro",
"type": "record",
"name": "User",
"fields": [
{"name": "date", "type": "int", "logicalType": "date"}
]
}
This maps to int and not to LocalDate e.g,
How to map in avro schema LocalDate?
#org.apache.avro.specific.AvroGenerated
public class User extends org.apache.avro.specific.SpecificRecordBase implements
org.apache.avro.specific.SpecificRecord {
private int date;// This should be LocalDate
}
{
"namespace": "example.avro",
"type": "record",
"name": "User",
"fields": [
{
"name": "date",
"type": {
"type": "int",
"logicalType": "date"
}
}
]
}
//this will generate following mapping to private java.time.LocalDate date;as below with maven-avro-plugin.
/**
* Autogenerated by Avro
*
* DO NOT EDIT DIRECTLY
*/
package example.avro;
import org.apache.avro.generic.GenericArray;
import org.apache.avro.specific.SpecificData;
import org.apache.avro.util.Utf8;
import org.apache.avro.message.BinaryMessageEncoder;
import org.apache.avro.message.BinaryMessageDecoder;
import org.apache.avro.message.SchemaStore;
#org.apache.avro.specific.AvroGenerated
public class User extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord {
private static final long serialVersionUID = -2875816324033601436L;
public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"User\",\"namespace\":\"example.avro\",\"fields\":[{\"name\":\"date\",\"type\":{\"type\":\"int\",\"logicalType\":\"date\"}}]}");
public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; }
private static SpecificData MODEL$ = new SpecificData();
static {
MODEL$.addLogicalTypeConversion(new org.apache.avro.data.TimeConversions.DateConversion());
}
private static final BinaryMessageEncoder<User> ENCODER =
new BinaryMessageEncoder<User>(MODEL$, SCHEMA$);
private static final BinaryMessageDecoder<User> DECODER =
new BinaryMessageDecoder<User>(MODEL$, SCHEMA$);
/**
* Return the BinaryMessageEncoder instance used by this class.
* #return the message encoder used by this class
*/
public static BinaryMessageEncoder<User> getEncoder() {
return ENCODER;
}
/**
* Return the BinaryMessageDecoder instance used by this class.
* #return the message decoder used by this class
*/
public static BinaryMessageDecoder<User> getDecoder() {
return DECODER;
}
/**
* Create a new BinaryMessageDecoder instance for this class that uses the specified {#link SchemaStore}.
* #param resolver a {#link SchemaStore} used to find schemas by fingerprint
* #return a BinaryMessageDecoder instance for this class backed by the given SchemaStore
*/
public static BinaryMessageDecoder<User> createDecoder(SchemaStore resolver) {
return new BinaryMessageDecoder<User>(MODEL$, SCHEMA$, resolver);
}
/**
* Serializes this User to a ByteBuffer.
* #return a buffer holding the serialized data for this instance
* #throws java.io.IOException if this instance could not be serialized
*/
public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException {
return ENCODER.encode(this);
}
/**
* Deserializes a User from a ByteBuffer.
* #param b a byte buffer holding serialized data for an instance of this class
* #return a User instance decoded from the given buffer
* #throws java.io.IOException if the given bytes could not be deserialized into an instance of this class
*/
public static User fromByteBuffer(
java.nio.ByteBuffer b) throws java.io.IOException {
return DECODER.decode(b);
}
private java.time.LocalDate date;
/**
* Default constructor. Note that this does not initialize fields
* to their default values from the schema. If that is desired then
* one should use <code>newBuilder()</code>.
*/
public User() {}
/**
* All-args constructor.
* #param date The new value for date
*/
public User(java.time.LocalDate date) {
this.date = date;
}
public org.apache.avro.specific.SpecificData getSpecificData() { return MODEL$; }
public org.apache.avro.Schema getSchema() { return SCHEMA$; }
// Used by DatumWriter. Applications should not call.
public java.lang.Object get(int field$) {
switch (field$) {
case 0: return date;
default: throw new IndexOutOfBoundsException("Invalid index: " + field$);
}
}
private static final org.apache.avro.Conversion<?>[] conversions =
new org.apache.avro.Conversion<?>[] {
new org.apache.avro.data.TimeConversions.DateConversion(),
null
};
#Override
public org.apache.avro.Conversion<?> getConversion(int field) {
return conversions[field];
}
// Used by DatumReader. Applications should not call.
#SuppressWarnings(value="unchecked")
public void put(int field$, java.lang.Object value$) {
switch (field$) {
case 0: date = (java.time.LocalDate)value$; break;
default: throw new IndexOutOfBoundsException("Invalid index: " + field$);
}
}
/**
* Gets the value of the 'date' field.
* #return The value of the 'date' field.
*/
public java.time.LocalDate getDate() {
return date;
}
/**
* Sets the value of the 'date' field.
* #param value the value to set.
*/
public void setDate(java.time.LocalDate value) {
this.date = value;
}
/**
* Creates a new User RecordBuilder.
* #return A new User RecordBuilder
*/
public static example.avro.User.Builder newBuilder() {
return new example.avro.User.Builder();
}
/**
* Creates a new User RecordBuilder by copying an existing Builder.
* #param other The existing builder to copy.
* #return A new User RecordBuilder
*/
public static example.avro.User.Builder newBuilder(example.avro.User.Builder other) {
if (other == null) {
return new example.avro.User.Builder();
} else {
return new example.avro.User.Builder(other);
}
}
/**
* Creates a new User RecordBuilder by copying an existing User instance.
* #param other The existing instance to copy.
* #return A new User RecordBuilder
*/
public static example.avro.User.Builder newBuilder(example.avro.User other) {
if (other == null) {
return new example.avro.User.Builder();
} else {
return new example.avro.User.Builder(other);
}
}
/**
* RecordBuilder for User instances.
*/
#org.apache.avro.specific.AvroGenerated
public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase<User>
implements org.apache.avro.data.RecordBuilder<User> {
private java.time.LocalDate date;
/** Creates a new Builder */
private Builder() {
super(SCHEMA$);
}
/**
* Creates a Builder by copying an existing Builder.
* #param other The existing Builder to copy.
*/
private Builder(example.avro.User.Builder other) {
super(other);
if (isValidValue(fields()[0], other.date)) {
this.date = data().deepCopy(fields()[0].schema(), other.date);
fieldSetFlags()[0] = other.fieldSetFlags()[0];
}
}
/**
* Creates a Builder by copying an existing User instance
* #param other The existing instance to copy.
*/
private Builder(example.avro.User other) {
super(SCHEMA$);
if (isValidValue(fields()[0], other.date)) {
this.date = data().deepCopy(fields()[0].schema(), other.date);
fieldSetFlags()[0] = true;
}
}
/**
* Gets the value of the 'date' field.
* #return The value.
*/
public java.time.LocalDate getDate() {
return date;
}
/**
* Sets the value of the 'date' field.
* #param value The value of 'date'.
* #return This builder.
*/
public example.avro.User.Builder setDate(java.time.LocalDate value) {
validate(fields()[0], value);
this.date = value;
fieldSetFlags()[0] = true;
return this;
}
/**
* Checks whether the 'date' field has been set.
* #return True if the 'date' field has been set, false otherwise.
*/
public boolean hasDate() {
return fieldSetFlags()[0];
}
/**
* Clears the value of the 'date' field.
* #return This builder.
*/
public example.avro.User.Builder clearDate() {
fieldSetFlags()[0] = false;
return this;
}
#Override
#SuppressWarnings("unchecked")
public User build() {
try {
User record = new User();
record.date = fieldSetFlags()[0] ? this.date : (java.time.LocalDate) defaultValue(fields()[0]);
return record;
} catch (org.apache.avro.AvroMissingFieldException e) {
throw e;
} catch (java.lang.Exception e) {
throw new org.apache.avro.AvroRuntimeException(e);
}
}
}
#SuppressWarnings("unchecked")
private static final org.apache.avro.io.DatumWriter<User>
WRITER$ = (org.apache.avro.io.DatumWriter<User>)MODEL$.createDatumWriter(SCHEMA$);
#Override public void writeExternal(java.io.ObjectOutput out)
throws java.io.IOException {
WRITER$.write(this, SpecificData.getEncoder(out));
}
#SuppressWarnings("unchecked")
private static final org.apache.avro.io.DatumReader<User>
READER$ = (org.apache.avro.io.DatumReader<User>)MODEL$.createDatumReader(SCHEMA$);
#Override public void readExternal(java.io.ObjectInput in)
throws java.io.IOException {
READER$.read(this, SpecificData.getDecoder(in));
}
}
While trying from soap UI, Iam getting null values in request object even when I send values in req object. what would be the reason? Please help
Below is my endpoint
#Endpoint
public class SummaryEndPoint {
#PayloadRoot(namespace = "http://online.mysite.no", localPart ="getSummary")
public #ResponsePayload JAXBElement<EInvoicesObjects> getSummary(#RequestPayload SummaryObject summaryObject) {
//Here summaryObject.getzDocId() returns null.
return null;
}
}
Soap request:
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:onl="http://online.mysite.no">
<soapenv:Header/>
<soapenv:Body>
<onl:getSummary soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<in0 xsi:type="onl:SummaryObject">
<ZDocId xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">asdasdsa</ZDocId>
<amountDue xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">sadadsadsa</amountDue>
</in0>
</onl:getSummary>
</soapenv:Body>
</soapenv:Envelope>
Request Payload Object:
package com.nets.online2adapter.endpoints;
import javax.xml.bind.annotation.*;
import org.xmlsoap.schemas.soap.encoding.String;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "SummaryObject", propOrder = {
"zDocId",
"amountDue"
},namespace="http://online.mysite.no")
public class SummaryObject {
#XmlElement(name = "ZDocId", required = true, nillable = true)
protected String zDocId;
#XmlElement(required = true, nillable = true)
protected String amountDue;
/**
* Gets the value of the zDocId property.
*
* #return
* possible object is
* {#link String }
*
*/
public String getZDocId() {
return zDocId;
}
/**
* Sets the value of the zDocId property.
*
* #param value
* allowed object is
* {#link String }
*
*/
public void setZDocId(String value) {
this.zDocId = value;
}
/**
* Gets the value of the amountDue property.
*
* #return
* possible object is
* {#link String }
*
*/
public String getAmountDue() {
return amountDue;
}
/**
* Sets the value of the amountDue property.
*
* #param value
* allowed object is
* {#link String }
*
*/
public void setAmountDue(String value) {
this.amountDue = value;
}
}
I had the same issue. After deep analysing my code I found that it's because namespaces. (See the example at https://www.baeldung.com/spring-boot-soap-web-service).
In your example, the parameters <ZDocID> and <amountDue> should have been included in their parent's namespace, as like: <onl:ZDocId> and <onl:amountDue>.
Your request class should have 'request' in the name.
Please rename your request class and try with 'SummaryObjectRequest'.
I am having issues figuring out the logic of how to create nested forms for the three entities I have: Films, Actors and Locations. I generated my Symfony entities (+orm.xml) from my database following the instructions in the symfony docs here.
My ultimate goal would to be have one page where the user can perform any of the following actions:
Create a new Films object
Select Films from a dropdown menu, and then create a new Actors object to associate to it
Select Films from a dropdown menu, and then create a new Locations object to associate to it
(Actors and Locations both have a 1-to-many join with the Films table)
However, I've been struggling with the concept of nested forms in Symfony for a long time and in order to "walk before I can run" I'm just trying to put each of the above into separate routes with separate forms:
/newfilm
/newactor
/newlocation
/New-film I can get working without problem. However, with either of the other two, anything I try doesn't seem to work. The below is my code, if someone can explain the "theory" of nested forms in Symfony to avoid keep hitting this wall would be very much appreciated...!
As my problem is the same for both Actor and Location, I'm only putting the code for for Actors (and Films) as I realise it's quite a lot already:
~~~~~Controller~~~~~
It is this second route (/newactor) which has the embedded/nested formType:
class DefaultController extends FOSRestController
{
/**
* #Route("/newfilm", name="new_film")
*/
public function newFilmAction(Request $request)
{
$film = new Films();
$form = $this->CreateFormBuilder($film)
->add('film_title','text',array('label'=>'Film title'))
->add('Save','submit',array('label'=>'Add new film'))
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($film);
$em->flush();
return $this->redirectToRoute('success_addFilm');
}
return $this->render('AppBundle:Default:newfilm.form.html.twig', array(
'form' => $form->createView(),
));
}
/**
* #Route("/newactor", name="new_actor")
*/
public function newActorAction(Request $request)
{
$actor = new Actors();
$form = $this->createForm(new ActorType(), $actor);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($actor);
$em->flush();
return $this->redirectToRoute('success_addActor');
}
return $this->render('AppBundle:Default:newactor.form.html.twig', array(
'form' => $form->createView(),
));
}
}
~~~~~Films~~~~~
Films.php
/**
* Films
*/
class Films
{
/**
* #var integer
*/
private $filmid;
/**
* #var string
*/
private $film_title;
/**
* #var \AppBundle\Entity\Actors
*/
private $actor;
/**
* Get filmid
* #return integer
*/
public function getFilmid()
{
return $this->filmid;
}
/**
* Get film_title
*
* #return string
*/
public function getFilm_title()
{
return $this->film_title;
}
/**
* Set film_title
* #param string $film_title
* #return Films
*/
public function setFilm_title($film_title)
{
$this->film_title = $film_title;
return $this;
}
/**
* Set actor
*
* #param \AppBundle\Entity\Actors $actor
*
* #return Actors
*/
public function setActor(\AppBundle\Entity\Actors $actor = null)
{
$this->actor = $actor;
return $this;
}
/**
* Get actor
*
* #return \AppBundle\Entity\Actors
*/
public function getActor()
{
return $this->actor;
}
}
Films.orm.xml
<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="AppBundle\Entity\Films" table="Films">
<indexes>
<index name="fk_Films_actors1_idx" columns="actor_id"/>
</indexes>
<id name="filmid" type="integer" column="filmid">
<generator strategy="IDENTITY"/>
</id>
<field name="film_title" type="text" column="film_title" length="65535" nullable="false">
<options>
<option name="fixed"/>
</options>
</field>
<many-to-one field="actor" target-entity="Actors" fetch="LAZY">
<join-columns>
<join-column name="actor_id" referenced-column-name="actorid"/>
</join-columns>
</many-to-one>
</entity>
</doctrine-mapping>
FilmType.php
class FilmType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('film_title');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class'=>'AppBundle\Entity\Films'
));
}
public function getName()
{
return 'film';
}
}
~~~~~Actors~~~~~
Actors.php
/**
* Entries
*/
class Entries
{
/**
* #var integer
*/
private $actorid;
/**
* #var string
*/
private $actorName;
/**
* Set actorid
*
* #param integer $actorid
*
* #return Actors
*/
public function setActorid($actorid)
{
$this->actorid = $actorid;
return $this;
}
/**
* Get actorid
*
* #return integer
*/
public function getActorid()
{
return $this->actorid;
}
/**
* Set actorName
*
* #param string $actorName
*
* #return Actors
*/
public function setActorName($actorName)
{
$this->actorName = $actorName;
return $this;
}
/**
* Get actorName
*
* #return string
*/
public function getActorName()
{
return $this->actorName;
}
}
Actors.orm.xml
<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="AppBundle\Entity\Actors" table="Actors">
<id name="actorid" type="integer" column="actorid">
<generator strategy="IDENTITY"/>
</id>
<field name="actorName" type="text" column="actorName" length="65535" nullable="true">
<options>
<option name="fixed"/>
</options>
</field>
</entity>
</doctrine-mapping>
ActorType
class ActorType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('actorName')
->add('film','entity',array(
'class'=>'AppBundle:Films',
'query_builder'=>function(EntityRepository $er) {
return $er->createQueryBuilder('f')
->orderBy('f.film_title','ASC');
}
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class'=>'\AppBundle\Entity\Actors'
));
}
public function getName()
{
return 'actor';
}
}
My current error message is:
Catchable Fatal Error: Object of class AppBundle\Entity\Films could not be converted to string
500 Internal Server Error - ContextErrorException
I have read answers that say to add in the function to my Films.php:
public function __toString() {
return $this->name;
}
However, when I do that, I then get the error:
Error: Method AppBundle\Entity\Films::__toString() must not throw an exception
Other possible ideas I've come across online (but unfortunately with no success) are:
Setting the Forms as services
Data transformers
Add 'choice_label' => 'film_title', to your form builder. That or you can implement a __toString() function within your Film entity that returns the film title.
Solution:
$builder
->add('actorName')
->add('film','entity',array(
'class'=>'AppBundle:Films',
'choice_label' => 'film_title',
'query_builder'=>function(EntityRepository $er) {
return $er->createQueryBuilder('f')
->orderBy('f.film_title','ASC');
}
));
Also, you may want to keep your entity names singular (eg: Film, not Films; Actor not Actors) as this may cause unnecessary issues when dealing with x-to-many entity relationships.
I'm new to Symfony2 (or Symfony3) and I can't find how to set doctrine (with annotations config) to automatically save it in my entities when 'created' or 'modified' fields.
Here my solution after this time ...
You just need to put this directly into your entity class :
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class MyEntity {
//....
public function __construct() {
// we set up "created"+"modified"
$this->setCreated(new \DateTime());
if ($this->getModified() == null) {
$this->setModified(new \DateTime());
}
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function updateModifiedDatetime() {
// update the modified time
$this->setModified(new \DateTime());
}
//....
}
It works well actually
You can use StofDoctrineExtensionsBundle. This describes in symfony cookbook. It contains Timestampable behavior.
/**
* #var datetime $created
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(type="datetime")
*/
private $created;
/**
* #var datetime $updated
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(type="datetime")
*/
private $updated;
/**
*
* #ORM\PrePersist
* #ORM\PreUpdate
*/
public function updatedTimestamps()
{
$this->setModifiedAt(new \DateTime(date('Y-m-d H:i:s')));
if($this->getCreatedAt() == null)
{
$this->setCreatedAt(new \DateTime(date('Y-m-d H:i:s')));
}
}
You dont need to call in __constructor anything. Just create getter and setter properties created, modified and that is all.
If you set first setCreated() on every update you will update also created colum. So put first setModifedAt()
Two more examples (if you're using Yaml or Xml mapping):
Entity\Product:
type: entity
table: products
id:
id:
type: integer
generator:
strategy: AUTO
fields:
name:
type: string
length: 32
created_at:
type: date
gedmo:
timestampable:
on: create
updated_at:
type: datetime
gedmo:
timestampable:
on: update
And xml:
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
<entity name="Mapping\Fixture\Xml\Timestampable" table="timestampables">
<id name="id" type="integer" column="id">
<generator strategy="AUTO"/>
</id>
<field name="created_at" type="datetime">
<gedmo:timestampable on="create"/>
</field>
<field name="updated_at" type="datetime">
<gedmo:timestampable on="update"/>
</field>
</entity>
</doctrine-mapping>
The other answers suggest using if statements (which means repeating your property names) and having property-setting logic in the constructor that might never be used.
Alternatively, you could have onAdd and onUpdate methods that are called when needed:
/**
* #ORM\PrePersist
*/
public function onAdd()
{
$this->setAdded(new DateTime('now'));
}
/**
* #ORM\PrePersist
* #ORM\PreUpdate
*/
public function onUpdate()
{
$this->setUpdated(new DateTime('now'));
}
I'm having troubles displaying a dropdown list with the proper values. I'm using the <spring-form:select>, <spring-form:options> and <spring-form:option> tags, and I just can't get it to display the correct options. With the following code, I should only be listing "Option 2", "Option 7", and "Option 8".
*Note - I do NOT want to display every possible Enum value, but for some reason Spring seems to want to display them all. It appears to be completely ignoring the list that is provided to the <spring-form:options> tag.
JSP Tags
<spring-form:select path="selectOptions">
<spring-form:option value="" label="*** Select Option ***" />
<spring-form:options path="${availableOptions}" />
</spring-form:select>
Enum
public enum SelectOptions {
// CHECKSTYLE_OFF: LineLength
/**
* Option 1.
*/
OPTION_1(1, "Option 1"),
/**
* Option 2.
*/
OPTION_2(2, "Option 2"),
/**
* Option 3.
*/
OPTION_3(3, "Option 3"),
/**
* Option 4.
*/
OPTION_4(4, "Option 4"),
/**
* Option 5.
*/
OPTION_5(5, "Option 5"),
/**
* Option 6.
*/
OPTION_6(6, "Option 6"),
/**
* Option 7.
*/
OPTION_7(7, "Option 7"),
/**
* Option 8.
*/
OPTION_8(8, "Option 8"),
/**
* Option 9.
*/
OPTION_9(9, "Option 9"),
/**
* Option 10.
*/
OPTION_10(10, "Option 10");
private int id;
private String description;
/**
* Constructor.
*
* #param id the id
* #param description the description
*/
private SelectOptions(final int id, final String description) {
this.id = id;
this.description = description;
}
/**
* Retrieves the {#link SelectOptions} associated with the passed in id.
*
* #param id the id associated with the SelectOptions
* #return the SelectOptions
*/
public static SelectOptions getInstance(final int id) {
for (final SelectOptions selectOptions : SelectOptions.values()) {
if (selectOptions.id == id) {
return selectOptions;
}
}
throw new IllegalArgumentException("SelectOptions could not be determined with id [" + id + "]");
}
/**
* Retrieves the {#link SelectOptions} associated with the passed in description.
*
* #param description the description associated with the SelectOptions
* #return the SelectOptions
*/
public static SelectOptions getInstance(final String description) {
for (final SelectOptions selectOptions : SelectOptions.values()) {
if (selectOptions.description == description) {
return selectOptions;
}
}
throw new IllegalArgumentException("SelectOptions could not be determined with description [" + description + "]");
}
/**
* Simple Getter.
*
* #return the id
*/
public int getId() {
return this.id;
}
/**
* Simple Setter.
*
* #param id the id to set
*/
public void setId(final int id) {
this.id = id;
}
/**
* Simple Getter.
*
* #return the description
*/
public String getDescription() {
return this.description;
}
/**
* Simple Setter.
*
* #param description the description to set
*/
public void setDescription(final String description) {
this.description = description;
}
}
Controller
public class SpringController implements SpringControllerInterface {
/**
* /WEB-INF/jsp/myJSP.jsp.
*/
private static final String PAGE = "/WEB-INF/jsp/myJSP.jsp";
/**
* {#inheritDoc}
*/
#Override
public ModelAndView load(final Model model) {
final ModelAndView mav = new ModelAndView(PAGE);
final List<SelectOptions> availableOptions = this.getAvailableOptions();
mav.addObject("availableOptions", availableOptions);
return mav;
}
/**
* {#inheritDoc}
*/
#Override
public ModelAndView save(final Model model) {
final ModelAndView mav = new ModelAndView(PAGE);
// TODO
return mav;
}
/**
* {#inheritDoc}
*/
#Override
public Model getModel() {
return new ModelImpl();
}
/**
* #return a list of available options
*/
public List<SelectOptions> getAvailableOptions() {
final List<SelectOptions> availableOptions = Lists.newArrayList();
availableOptions.add(SelectOptions.OPTION_1);
availableOptions.add(SelectOptions.OPTION_7);
availableOptions.add(SelectOptions.OPTION_8);
return availableOptions;
}
}
Model
public class ModelImpl implements Model {
private SelectOptions selectOptions;
/**
* Simple Getter.
*
* #return the selectOptions
*/
#Override
public SelectOptions getSelectOptions() {
return this.selectOptions;
}
/**
* Simple Setter.
*
* #param selectOptions the selectOptions to set
*/
#Override
public void setSelectOptions(final SelectOptions selectOptions) {
this.selectOptions = selectOptions;
}
}
If you created a spring controller and you want to pass an enum to your jsp page, you can do it like this:
Enum example:
public enum Coin {
HEADS("Heads", "heads"),
TAILS("Tails", "tails");
private final String fullName;
private final String shortName;
private Coin(String fullName, String shortName) {
this.fullName = fullName;
this.shortName = shortName;
}
public String getFullName() {
return fullName;
}
public String getShortName() {
return shortName;
}
}
Passing this enum to your model:
model.addObject("coins", Coin.values());
Using it in your jsp page with form:
<form:select path="selection">
<form:options items="${coins}" itemValue="shortName" itemLabel="fullName" />
</form:select>
It looks like the solution to this problem was that I was using the attribute "path" in the <spring-form:options> tag. It should have been "items", not "path".
the corrected JSP fragment:
<spring-form:select path="selectOptions">
<spring-form:option value="" label="*** Select Option ***" />
<spring-form:options items="${availableOptions}" />
</spring-form:select>
You do not even need to use items attribute if you are using <spring-form:options> tag.
The options tag:
Renders a list of HTML 'option' tags. Sets 'selected' as appropriate based on bound value.
References Spring form tld.
So for your need I guess falling back to JSTL core with <spring-form:option/> will suffice.