I'm trying to use a virtual domain model property in TYPO3 9.5.x that doesn't have a database field representation but I can't get it to work.
My model looks like this
class Project extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
/**
* participants
*
* #var string
*/
protected $participants;
...
/**
* Returns the participants
*
* #return string $participants
*/
public function getParticipants()
{
$this->participants = "foo";
return $this->participants;
}
}
I do see the property when I debug the model but it's always null as if it doesn't even recognise the getter method getParticipants().
Any idea what I might be doing wrong?
Already added a database field to ext_tables.sql and the TCA, but it didn't seem to make a difference.
The property is null because that's the state when the Extbase debugger inspects it. Notice that the Extbase debugger knows nothing about getters and also does not call them.
So if you want to initialize your property you must do this at the declaration time:
protected $participants = 'foo';
You can debug this property by simpy accessing it.
In Fluid, if you use <f:debug>{myModel}</f:debug>, you will see NULL for your property.
But if you directly use <f:debug>{myModel.participants}</f:debug>, you will see 'foo'.
I'm using MongoDB with Doctrine for a Symfony project.
For a document (I call it class Product) I need to define a field (e.g. name), but I want to act doctrine in a way that the document cannot be saved without defining a it. Meaning, whenever creating or updating a new Product, the property $name has to be defined.
For doctrine and MySql this is quite simple. There you have to say:
class Product {
...
/** #ORM\Column(type="string", nullable=false) */
private $name;
...
}
Is there a similar way in MongoDb?
The following code (which I found in a similar thread) doesn't work:
class Product {
...
/**
* #MongoDB\Field(type="string")
* #Constraints\NotBlank
*/
protected $name;
...
}
EclipsLink doesnt' seem to detect or fire JSR303 annotation constraints in a base class that is the mapped super class of an entity during a persist() operation.
For example:
public Base
{
#NotNull
private Integer id;
private String recordName;
//other stuff (getters etc)
}
and then
public class MyObject
extends Base
{
//stuff...
}
and then:
<mapped-superclass class="Base">
<attributes>
<basic name="recordName">
<column name = "NAME" />
</basic>
</attributes>
</mapped-superclass>
and finally:
<entity class="MyObject">
<table name="TheTable"/>
<attributes>
<id name="id">
<column name="recordId" />
</id>
</attributes>
</entity>
Some other relevant parameters:
using jpa 2.1 -- specifically eclipslink 2.6.2 and 2.6.3
I am integration testing - so java se (and spock)
JDK 1.8.77
I do have hibernate validator in my classpath (org.hibernate:hibernate-validator:5.2.4.Final)
If I write a test fixture and use validitor.validate() directly (no jpa or persist) hibernate validator works as expected.
I do NOT use JPA annotations and only use ORM xml to declare entity mappings.
I do use JSR303 annotations to mark attrs and props with constraints.
persistence.xml is marked with validation "AUTO" and many variations of properties like javax.persistence.validation.group.pre-persist with FQDN of marker interfaces have been tried.
As mentioned, calling em.persist(myObjectInst) will not fire any 303 annotations added to class 'Base'.
* Is there some tuning parameter or switch I can tinker with that will make this work? *
Note: I did a deep-dive debug on this and can see that org.eclipse.persistence.internal.jpa.metadata.beanvalidation.BeanValidationHelper.detectConstraints() does NOT look at any parent classes for JSR303 annotations. It seems to only want to look at the specific entity class. I'd hazard to guess that if I moved my JSR303 constraints to the concrete (or entity class); it may just work. But then I would loose the extension and mapped super class stuff. So what fun is that?
UPDATE
Looks like issue in EclipseLink 2.6.x. See here ( https://www.eclipse.org/forums/index.php?t=msg&th=1077658&goto=1732842&#msg_1732842 ) for more details.
From what I can see, eclipse link 2.6.X up to 2.6.4 seems to have a Massive bug in terms of upholding its contract of triggering JSR 303 bean validations.
Right now, eclipselink 2.6.4 only triggers these validations if your child entity is right-out flagged with constraints.
I have integration tests that work perfectly under JEE 6 library versions (e.g. eclipselink 2.4.x).
When I upgrade libraries to JEE 7 verions, in the particular case of ecliselink this means versions: 2.6.1 up to 2.6.4, they all manifest the same bug.
The broken unit tests I have analyzed so far, are validating that ConstraintViolationExceptions, such as not null, must get triggered.
So if you take an Entity A that extends abstract entity B. And abstract entity B is a #MappedSuperClass.
You will have problems if your #NotNull or any other such constraints is found on your abstract entity B ...
In this case, things will not go well.
No constraint violation gets triggered by eclipselink.
Instead, it is the DB that stops you if you do issue the commit() or flush() in the test.
Eclipse-link will rollback on the db exception.
However, as soon as you go to entity A and you pump into it a dummy field:
#NotNull
private String dummy;
This is sufficient to make the Validator (e.g. hibernate validator) get called.
In this case, my the test still fails because now I get twotwo #NotNull constraint validations instead of one.
In the following snippet I illustrate the relevant chucnk of stack trace on eclipselink 2.6.1.
Caused by: javax.validation.ConstraintViolationException:
Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'prePersist'.
Please refer to embedded ConstraintViolations for details.
at org.eclipse.persistence.internal.jpa.metadata.listeners.BeanValidationListener.validateOnCallbackEvent(BeanValidationListener.java:108)
at org.eclipse.persistence.internal.jpa.metadata.listeners.BeanValidationListener.prePersist(BeanValidationListener.java:77)
at org.eclipse.persistence.descriptors.DescriptorEventManager.notifyListener(DescriptorEventManager.java:748)
at org.eclipse.persistence.descriptors.DescriptorEventManager.notifyEJB30Listeners(DescriptorEventManager.java:691)
at org.eclipse.persistence.descriptors.DescriptorEventManager.executeEvent(DescriptorEventManager.java:229)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.registerNewObjectClone(UnitOfWorkImpl.java:4314)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.registerNotRegisteredNewObjectForPersist(UnitOfWorkImpl.java:4291)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.registerNotRegisteredNewObjectForPersist(RepeatableWriteUnitOfWork.java:521)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.registerNewObjectForPersist(UnitOfWorkImpl.java:4233)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.persist(EntityManagerImpl.java:507)
at TEST_THAT_IS_PROBLEMATIC
... 25 more
In the stack trace above, you have the unit test doing an em.persist() on entity A, and this case entity A has the dummy #NotNull field. So the validation gets called.
The bug in eclipselink seems to be when the BeanValidationListener asks the BeanValidationHelper if a class is constrained or not:
The code from Eclipselink is as follows:
private void validateOnCallbackEvent(DescriptorEvent event, String callbackEventName, Class[] validationGroup) {
Object source = event.getSource();
boolean noOptimization = "true".equalsIgnoreCase((String) event.getSession().getProperty(PersistenceUnitProperties.BEAN_VALIDATION_NO_OPTIMISATION));
boolean shouldValidate = noOptimization || beanValidationHelper.isConstrained(source.getClass());
if (shouldValidate) {
Set<ConstraintViolation<Object>> constraintViolations = getValidator(event).validate(source, validationGroup);
if (constraintViolations.size() > 0) {
// There were errors while call to validate above.
// Throw a ConstrainViolationException as required by the spec.
// The transaction would be rolled back automatically
// TODO need to I18N this.
throw new ConstraintViolationException(
"Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'" +
callbackEventName + "'. Please refer to embedded ConstraintViolations for details.",
(Set<ConstraintViolation<?>>) (Object) constraintViolations); /* Do not remove the explicit
cast. This issue is related to capture#a not being instance of capture#b. */
}
}
}
And the problem is that the query:
beanValidationHelper.isConstrained(source.getClass());
Returns false, this is completely wrong.
Finally, if you check the implemation of the BeanValidationHelper, the intial part of the code looks as follows:
private Boolean detectConstraints(Class<?> clazz) {
for (Field f : ReflectionUtils.getDeclaredFields(clazz)) {
for (Annotation a : f.getDeclaredAnnotations()) {
final Class<? extends Annotation> type = a.annotationType();
if (KNOWN_CONSTRAINTS.contains(type.getName())){
return true;
}
// Check for custom annotations on the field (+ check inheritance on class annotations).
// Custom bean validation annotation is defined by having #Constraint annotation on its class.
for (Annotation typesClassAnnotation : type.getAnnotations()) {
final Class<? extends Annotation> classAnnotationType = typesClassAnnotation.annotationType();
if (Constraint.class == classAnnotationType) {
KNOWN_CONSTRAINTS.add(type.getName());
return true;
}
}
}
}
The implementation above is clearly wrong, because the method as whole is non recursive and the apis it uses from reflection are themselves non recursive.
They look only at the current instance.
If you see the following stack overflow thread:
What is the difference between getFields and getDeclaredFields in Java reflection
It is clearly explained by the top ranked answer that:
Field f : ReflectionUtils.getDeclaredFields(clazz)
Only returns you the fields for the current class but not the parents.
What I am seing myself force to do in the mean time is to put in place this workaround to froce the broken algorithm in the BeanValidationHelper to detect the class as one needing to be validated:
#Transient
#NotNull
private final char waitForEclipseLinkToFixTheVersion264 = 'a';
By doing as above, you have your code clearly flagged with a chunk that you can in the future remove.
And since the field is transient ... hey, it does not change your DB.
Please note as well that the eclipselink forum now has additional information.
The bug goes deeper than than just the improper tracking of when "beanValidation" is needed in the BeanValidationListner.class.
The bug has second depth.
The BeanValidationListner.class provided with eclipse-link also does not register any implementation for the:
PreWriteEvent and for the DescriptorEventManager.PreInsertEvent.
So when the "DeferredCachedDetectionPolocy.class is calculatinChanges(), if your entity A has JSR fields and it still does not get JSR 303 validation.
This is most likely happening to you because your enitya was:
T0: Persisted and validatioons wen through ok
T1: you modify the peristed entity in the same transaction, and when the calculateChanges invokes the event litenters.
The BeanValidationListner.class does not care about a preInsertEvent.
It just assumes the validation was done a prePersist and does not invoke the validation at all.
The work-around for this, I am not yet sure.
I will be looking at how to register an event listner during PreInserPhase, that does the same as the BeanValidationListner.
Or, I will be locally patching the BeanValidationListner.class to subscribe to the PreINsert event.
I hade modifying code of libraries maintained by others, so I will go first for the approach of our own eventListner as a temprorary workarund for this bug.
Adding repository that allows to verify both bugs.
https://github.com/99sono/EclipseLink_2_6_4_JSR_303Bug
For bug number 2 the following EventListner can server a a temporary work-around, until eclipse link 2.6.4 fixes their bean validation orchestration logic.
package jpa.eclipselink.test.bug2workaround;
import java.util.Map;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.descriptors.DescriptorEventAdapter;
import org.eclipse.persistence.descriptors.changetracking.DeferredChangeDetectionPolicy;
import org.eclipse.persistence.internal.jpa.deployment.BeanValidationInitializationHelper;
import org.eclipse.persistence.internal.jpa.metadata.listeners.BeanValidationListener;
/**
* Temporary work-around for JSR 303 bean validation flow in eclipselink.
*
* <P>
* Problem: <br>
* The
* {#link DeferredChangeDetectionPolicy#calculateChanges(Object, Object, boolean, org.eclipse.persistence.internal.sessions.UnitOfWorkChangeSet, org.eclipse.persistence.internal.sessions.UnitOfWorkImpl, org.eclipse.persistence.descriptors.ClassDescriptor, boolean)}
* during a flush will do one of the following: <br>
* {#code descriptor.getEventManager().executeEvent(new DescriptorEvent(DescriptorEventManager.PreInsertEvent, writeQuery)); }
* or <br>
*
* {#code descriptor.getEventManager().executeEvent(new DescriptorEvent(DescriptorEventManager.PreUpdateEvent, writeQuery)); }
*
* <P>
* WHe it does
* {#code descriptor.getEventManager().executeEvent(new DescriptorEvent(DescriptorEventManager.PreInsertEvent, writeQuery)); }
* the {#link BeanValidationListener} will not do anything. We want it to do bean validation.
*/
public class ForceBeanManagerValidationOnPreInsert extends DescriptorEventAdapter {
private static final Class[] DUMMY_GROUP_PARAMETER = null;
/**
* This is is the EJB validator that eclipselink uses to do JSR 303 validations during pre-update, pre-delete,
* pre-persist, but not pre-insert.
*
* Do not access this field directly. Use the {#link #getBeanValidationListener(DescriptorEvent)} api to get it, as
* this api will initialize the tool if necessary.
*/
BeanValidationListener beanValidationListener = null;
final Object beanValidationListenerLock = new Object();
/**
*
*/
public ForceBeanManagerValidationOnPreInsert() {
super();
}
/**
* As a work-around we want to do bean validation that the container is currently not doing.
*/
#Override
public void preInsert(DescriptorEvent event) {
// (a) get for ourselves an instances of the eclipse link " Step 4 - Notify internal listeners."
// that knows how to run JSR 303 validations on beans associated to descriptor events
BeanValidationListener eclipseLinkBeanValidationListenerTool = getBeanValidationListener(event);
// (b) let the validation listener run its pre-update logic on a preInsert it serves our purpose
eclipseLinkBeanValidationListenerTool.preUpdate(event);
}
/**
* Returns the BeanValidationListener that knows how to do JSR 303 validation. Creates a new instance if needed,
* otherwise return the already created listener.
*
* <P>
* We can only initialize our {#link BeanValidationListener} during runtime, to get access to the JPA persistence
* unit properties. (e.g. to the validation factory).
*
* #param event
* This event describes an ongoing insert, updetae, delete event on an entity and for which we may want
* to force eclipselink to kill the transaction if a JSR bean validation fails.
* #return the BeanValidationListener that knows how to do JSR 303 validation.
*/
protected BeanValidationListener getBeanValidationListener(DescriptorEvent event) {
synchronized (beanValidationListenerLock) {
// (a) initializae our BeanValidationListener if needed
boolean initializationNeeded = beanValidationListener == null;
if (initializationNeeded) {
beanValidationListener = createBeanValidationListener(event);
}
// (b) return the validation listerner that is normally used by eclipse link
// for pre-persist, pre-update and pre-delete so that we can force it run on pre-insert
return beanValidationListener;
}
}
/**
* Creates a new instance of the {#link BeanValidationListener} that comes with eclipse link.
*
* #param event
* the ongoing db event (e.g. pre-insert) where we want to trigger JSR 303 bean validation.
*
* #return A new a new instance of the {#link BeanValidationListener} .
*/
protected BeanValidationListener createBeanValidationListener(DescriptorEvent event) {
Map peristenceUnitProperties = event.getSession().getProperties();
ValidatorFactory validatorFactory = getValidatorFactory(peristenceUnitProperties);
return new BeanValidationListener(validatorFactory, DUMMY_GROUP_PARAMETER, DUMMY_GROUP_PARAMETER,
DUMMY_GROUP_PARAMETER);
}
/**
* Snippet of code taken out of {#link BeanValidationInitializationHelper}
*
* #param puProperties
* the persistence unit properties that may be specifying the JSR 303 validation factory.
* #return the validation factory that can check if a bean is violating business rules. Almost everyone uses
* hirbernate JSR 303 validation.
*/
protected ValidatorFactory getValidatorFactory(Map puProperties) {
ValidatorFactory validatorFactory = (ValidatorFactory) puProperties
.get(PersistenceUnitProperties.VALIDATOR_FACTORY);
if (validatorFactory == null) {
validatorFactory = Validation.buildDefaultValidatorFactory();
}
return validatorFactory;
}
}
Simply add this bean validator to the class, preferably a base abstract class to ensure JSR 303 validation will happen in pre-insert.
This should work-around the hole that allows us to commit dirtyu entities violating business rules to the db.
Here is an example of an entity with the work-around in place.
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "DESCRIMINATOR", length = 32)
#DiscriminatorValue("Bug2WorkAround")
#Entity
#EntityListeners({ ForceBeanManagerValidationOnPreInsert.class })
public class Bug2Entity2WithWorkAround extends GenericEntity {
Kind regards.
I have a Sphinx search engine running on MySQL protocol and I use Phalcon\Db\Adapter\Pdo\Mysql to connect to it. Sphinx tables are implemented as models.
When I try to select (using SpinxQL) I, obviously, get an error when database adapter attempts to extract table metadata running queries against tables which are not supported and not present respectively in SpinxQL. There is a workaround in the documentation showing how to manually assign metadata... But being to lazy by nature I want to try to automate metadata generation.
I assume that metadata is produced by the database adapter, probably as a result of calling getColumnsList() on the instance following getColumnDefinition() or something else (???). Is this my assumption correct? I want is to extend Phalcon\Db\Adapter\Pdo\Mysql and override those methods to be compatible with Sphinx.
Thanks in advance for your suggestions!
Ok, you need to override at least two methods to make this work, the following class would work:
<?php
class SphinxQlAdapter extends Phalcon\Db\Adapter\Pdo\Mysql implements Phalcon\Db\AdapterInterface
{
/**
* This method checks if a table exists
*
* #param string $table
* #param string $schema
* #return boolean
*/
public function tableExists($table, $schema=null)
{
}
/**
* This method describe the table's columns returning an array of
* Phalcon\Db\Column
*
* #param string $table
* #param string $schema
* #return Phalcon\Db\ColumnInterface[]
*/
public function describeColumns($table, $schema=null)
{
}
}
Then in your connection, you use the new adapter:
$di->set('db', function(){
return new SphinxQlAdapter(
//...
);
});
Can anybody tell me whether its posible to override doctrine2 persistentobject magic getters\setters? i'd like to do the below:-
public function setDob($dob)
{
$this->dob= new \Date($date);
}
however my entity is defined as:-
use Doctrine\Common\Persistence\PersistentObject;
use Doctrine\ORM\Mapping as ORM;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="Ajfit\Repository\User")
* #ORM\HasLifecycleCallbacks
*/
class User extends \Doctrine\Common\Persistence\PersistentObject
{
/**
* #var date $dob
*
* #ORM\Column(name="dob", type="date")
*/
protected $dob;
}
the public function setDob does not get called when I create the entity using:-
public function getNewRecord() {
return $this->metadata->newInstance();
}
I get the below error:-
Notice:- array to string conversion ...Doctrine\DBAL\Statement.php on line 98
Any help would be much apprieciated.
Thanks
Andrew
__call of PersistentObject#__call will not be called if you defined the setDob method.
What you're doing there is creating a new instance via metadata. What you are doing there is probably assuming that __construct or any setter/getter should be called by the ORM. Doctrine avoids to call any methods on your object when generating it via metadata/hydration (check ClassMetadataInfo#newInstance to see how it is done) as it does only know it's fields.
This allows you to be completely independent from Doctrine's logic.
About the notice, that is a completely different issue coming from Doctrine\DBAL\Statement, which suggests me that you have probably some wrong parameter binding in a query. That should be handled separately.