Doctrine 2, Need to execute code pre-persist/post-persist - persistence

I am using Doctrine 2 entities. We have some entities which have to update related items when they are saved to the database. For example, when a user record is modified, we save it as a new record, with the "inactive" field set to 'false'. However, we have to set the the 'inactive' field for all previous record for that user to 'true'. This is done to keep an audit history. It is a Legacy database, so changing the structure is not an option.
Since Doctrine saves objects by passing them to a persister object (persist::($thisObj)), rather than the object having a save method ($thisObj->save()), we can't just extend a 'save' method from a parent object. The only option I see here is to try to extend the 'persist' object, but that sounds like a goose gaggle, just waiting to happen.
I found some information on events, but do not see how to add them to make events fire a particular function when a particular entity is persisted.
How do I add pre-save/post-save functionality to some of my entities ?

So, you probably already know http://www.doctrine-project.org/docs/orm/2.1/en/reference/events.html right?
You add an annotation that the entity contains callbacks and then create specific functions (which need to be public) on that entity and also annotate them with #PrePersist or #PostPersist or whatever.
The other way is creating an event subscriber, register that one with the doctrine event manager and implement methods called prePersist, postPersist etc. They get passed an EventArguments object which contains the entity relevant for the occurred event.
I know this is a very general answer to your question, but you need to be a bit more specific where your problem lies.
Please dont exend the entity manager and overwrite the persist method, there are way cleaner methods for doing what you need as far as I can tell.

It's actually quite simple to do what you want to do. It does not require dorking with the event manager, or anything complex like that. You use something called "Lifecycle callbacks". These are functions that Doctrine automatically runs during the "lifecycle" of the entity, ie: prePersist, postPersist, preUpdate, postUpdate, etc. You can find the complete list here: http://www.doctrine-project.org/docs/orm/2.0/en/reference/events.html
The process of adding this functionality to your entities is very simple.
In the Annotations section of your entity, include the following tag: "#HasLifecycleCallbacks". This tells Doctrine that it should search the entity for functions to run upon various events
Write a public function in your entity that you would like to fire upon a specific event.
Put an annotation above the function indicating which event it should be used to handle.
For example, look at the following code:
/** #PostPersist */
public function doSPostPersist() {
$this->tester = 'Value changed by post-persist';
}
I have found that sometimes the events simply refuse to fire, and I don't yet know why. But when they do fire, they will fire reliably.

Don't forget to enable Lifecycle Callbacks in your class annotation :
/**
* Report\MainBundle\Entity\Serveur
* #ORM\HasLifecycleCallbacks
*/
class Serveur {

Related

What and when can "Target" be instead of an Entity and should I check its logical name when working on a single entity type?

I'm a newbie in developing in CRM, so I want to get some things clear. And you first you need to know the reason why you have to do something in order to fully understand it. So lets get to the question.
I know you have to do this when making a plugin:
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
if (context.InputParameters.Contains("Target") && context.InputParameters.["Target"] is Entity)
{
var entity = (Entity)context.InputParameters["Target"];
if(entity.LogicalName == "myEntity")
{
//Do something with your entity
}
}
Now, in the PluginRegistration Tool, you can set up that your plugin will be fired on the defined Message and which entities (and specific attributes from them) will be affected by it, besides other stuff.
I can see the validations are very useful when manipulating several entities with a single plugin.
Now, let's suppose you only are updating a single entity with your plugin. Why should I check if the entity that's on "Target" is the entity I want to work on, if I already know because I set it up for that entity in particular? What can an Entity be otherwise in that scenario?
Also, in what cases "Target" is NOT an Entity (in the current context)?
Thanks in advance, and sorry if this is a silly question.
See this answer: Is Target always an Entity or can it be EntityReference?
Per the SDK (https://msdn.microsoft.com/en-us/library/gg309673.aspx):
Note that not all requests contain a Target property that is of type
Entity, so you have to look at each request or response. For example,
DeleteRequest has a Target property, but its type is
EntityReference.
The bottom line is that you need to look at the request (all plugin's fire on an OrganizationRequest) of which there are many derived types (https://msdn.microsoft.com/en-us/library/microsoft.xrm.sdk.organizationrequest.aspx) to determine the type for the Target property.
As Nicknow said, the Input Parameters will change depending on the Message being executed.
You can get that information from the MSDN (every request will list the Input Parameters under the Properties section, for instance the CreateRequest) or using the ParameterBrowser.
There's also a really nice alternative to deal with this in a type-safe approach (intellisense included) described in the following blog article: mktange's blog.

Storing a complex detached object graph in EF6

I have the current scenario:
I'm using EF6 Code first, and have created a data-model something like this:
public class MainObject{
..some properties
IList<SubObject> SubObjects{get;set;}
}
public class SubObject{
..some properties
IList<SubSubObject> SubSubObjects{get;set;}
}
public class SubObject{
..some properties
IList<SubObject> SubObjects{get;set;}
}
So basically I have a main object, that has 0 to many subobjects, and there is a many to many relationship between the subobject and the subsubobjects.
I am creating a MVC application, so my normal program flow is that the user request a page that basically uses a MainObject as it's data model. Then the user interacts with the page and changes, adds or removes subobjects and subsubobjects as he wishes, and then clicks save. On save, the objectgraph is sent back to the controller and it looks correct according to the changes done by the user on the client side. Now my problem is the following:
How to store this back into the database in a good fashion.
I need to attach my object back into the context, but I have no clue which objects are new, modified or deleted.
I have written some code that partially works now, but it's getting so ugly that I really don't want to go down that path. Would it be possible somehow to fetch the relevant object graph from the database, and have EF compare the two graphs toward eachother, and then save the relevant changes to the database?
Any help to make this smoother would be greatly appreciated.
I ended up using GraphDiff to solve this for me, and it works just great! This really should be built into EF, but untill it does, this is a great substitute.
To solve the example given in my question above, this will make sure that the detached graph gets saved properly (given I have a MainObject I want to save called main):
context.UpdateGraph(main, map =>map
.AssociatedCollection( m => m.SubObjects, with => with
.AssociatedCollection( s => s.SubSubObjects)
)
);
context.SaveChanges();

Does symfony have a built-in method for comparing tainted values to the original?

I am using symfony 1.4 with Doctrine. I have built a form which uses a table that has the Versionable behaviour. As expected, Versionable creates a new version of the row every time the form is submitted and saved. My problem is that I would like to prevent it doing so if the actual values submitted are not any different from the original values put into the form via the edit action.
I know that I can do this with javascript relatively easily. I'm just curious as to whether symfony or Doctrine have this functionality already, and how it is used if so. It just seems like something that symfony would have a method for, which could be checked right before $form->save() is called. Am I dreaming or perhaps missing something obvious?
You can use the DoctrineRecord::getModified() function which returns an array of the modified fields and associated values from an overridden save() function or in a listener (preSave would be the best I guess).
If the new values are not any different, you can bypass the actual call to save(), so no new version is created.
The comment for the save() method of the Doctrine_Record is
/**
* applies the changes made to this object into database
* this method is smart enough to know if any changes are made
* and whether to use INSERT or UPDATE statement
*
* this method also saves the related components
*
* #param Doctrine_Connection $conn optional connection parameter
* #throws Exception if record is not valid and validation is active
* #return void
*/
so first, you should check whether it does not already work.
If not, Doctrine_Record has a isModified() method you could use. If the bind() method of the form object modifies the object in the form which should at first contain the default values, then this method should return true.
If you don't want to override save() method or implement a listener as jaudette suggested you can instead stay with form binding:
$form->bind($values);
if ($form->isValid()) {
$form->updateObject();
$changes = $form->getObject()->getModified();
// save to database if desired
$form->save();
}
The object will not be saved to database by calling $form->updateObject(), but the actual php object is changed.
Also note that you might have to call getModified() on each related object if you have embedded subforms.

Business Logic when Saving in Entity Framework

If I want to perform some action when an entity is saved, I can do something as described here.
However, suppose I retrieve an object from the database. This object has a list of items within it. If I instantiate a new item and add it to this list and then save all changes, the item in the list is not part of the "GetObjectStateEntries".
The problem for my situation, I believe, has been resolved. There appears to be a bug, in my opinion, in the ObjectContext.SaveChanges(SaveOptions) method. Even though this method will call DetectChanges (depending on the saveOptions), the OnSavingChanges method is called FIRST. This, I think, is a problem.
The solution to this is to call ObjectContext.DetectChanges() prior to calling SaveChanges().

Entity Framework Validation & usage

I'm aware there is an AssociationChanged event, however, this event fires after the association is made. There is no AssociationChanging event. So, if I want to throw an exception for some validation reason, how do I do this and get back to my original value?
Also, I would like to default values for my entity based on information from other entities but do this only when I know the entitiy is instanced for insertion into the database. How do I tell the difference between that and the object getting instanced because it is about to be populated based on existing data? Am I supposed to know? Is that considiered business logic that should be outside of my entity business logic?
If that's the case, then should I be designing controller classes to wrap all these entities? My concern is that if I deliver back an entity, I want the client to get access to the properties, but I want to retain tight control over validations on how they are set, defaulted, etc. Every example I've seen references context, which is outside of my enity partial class validation, right?
BTW, I looked at the EFPocoAdapter and for the life of me cannot determine how to populate lists of from within my POCO class... anyone know how I get to the context from a EFPoco Class?
This is in reply to a comment I left. Hopefully this answers your question, Shimmy. Just comment, and I will shorten it or remove it if it doesn't answer your question.
You will need both INotifyPropertyChanging and INotifyPropertyChanged interfaces to be implemented on your class (unless it is something like an entity framework object, which I believe implements these internally).
And before you set a value to this property, you will need to raise NotifyPropertyChanging.PropertyChanging event, using the name of the property in PropertyChangingEventArgs constructor.
And after you set this value you need to raise NofityPropertyChanged.PropertyChanged event, again using the name of the property this is being raised in PropertyChangedEventArgs constructor.
Then you have to handle the PropertyChanging and PropertyChanged events. In the PropertyChanging event, you need to cache the value. In the PropertyChanged event, you can compare and throw an exception.
To get the property from PropertyChanging/PropertyChanged event args, you need to use relfection.
// PropertyName is the key, and the PropertyValue is the value.
Dictionary <string, object> propertyDict = new Dictionary<object, object>();
// Convert this function prototype to C# from VBNet. I like how Handles is descriptive.
Public Sub PropertyChanging(sender As object, e As PropertyChangingEventArgs) Handles Foo.PropertyChanging
{
if (sender == null || preventRecursion)
{
return;
} // End if
Type senderType = sender.GetType();
PropertyInfo info = senderType.GetProperty(e.PropertyName);
object propertyValue = info.GetValue(sender, null);
// Change this so it checks if e.PropertyName already exists.
propertyDict.Add(e.PropertyName, propertyValue);
} // End PropertyChanging() Event
// Convert this function prototype to C# from VBNet. I like how Handles is descriptive.
Public Sub PropertyChanged(sender As object, e As PropertyChangedEventArgs) Handles Foo.PropertyChanged
{
if (sender == null || preventRecursion)
{
return;
} // End if
Type senderType = sender.GetType();
PropertyInfo info = senderType.GetProperty(e.PropertyName);
object propertyValue = info.GetValue(sender, null);
// Change this so it makes sure e.PropertyName exists.
object oldValue = propertyDict(e.PropertyName);
object newValue = propertyValue;
// No longer needed.
propertyDict.Remove(e.PropertyName);
if (/* some condition */)
{
try {
preventRecursion = true;
info.SetValue(oldValue, null);
Throw New Exception();
} finally {
preventRecursion = false;
} // End try
} // End if
} // End PropertyChanging() Event
Notice how I am using PreventRecursion, which is a boolean I forgot to add above these methods? When you reset the property back to its previous value, these events will be recalled.
tl;dr
Now you could derive a single event which inherits from INotifyPropertyChanged, but uses an argument which holds an Object representing the previous value as well as the Property Name. And that would reduce the number of events being fired down to one, have similar functionality, and have backwards compatibility with INotifyPropertyChanged.
But if you want to handle anything before the property gets set (say the property does an irreversible change or you need to setup other properties before setting that variable, otherwise an exception will be thrown) you won't be able to do that.
Overall, this method is a very old way of doing things. I would take Poker Villian's answer and have invalid data able to be entered. But disallow saving to a database.
Entity Framework has some excellent code towards validation. You add validation to your properties via attributes. And then it takes care of the work of processing those attributes. Then you can make a property called IsValid, which calls Entity Framework specific validation. It also distinguishes both field errors (like typing in the wrong characters or having a string too long), and class errors (like having missing data or conflicting keys).
Then you can bind IsValid to controls validation, and they will display a red bubble while invalid data is entered. Or you could just implement IsValid validation yourself. But If IsValid is false, SaveChanges event would need to cancel saving.
btw. The code provided will not compile and is pseudocode only (mixing vb and c#). But I believe it is much more descriptive than c# alone--showing exactly what is being handled.
Concerning your first question, I would simply implement the changes to the associations as business logic. For example, if you add a Teacher class with multiple Student, do not add students like
aTeacher.Students.Add(new Student)
instead, create a AddStudent method
public Student AddNewStudent(string name, string studentID)
{
Student s = new Student( name, studentID);
s.Teacher = this; // changes the association
return s;
}
That way you have full control on when associations are changed. Of course that what prevents another programmer from adding a student directly? On the Student side, you can set the Teacher setter to private (and change the constructor to accept a teacher or similar). On the teacher side, how to make the Students collection non-insertable? I'm not certain... maybe transforming it in a custom collection that doesn't accept inserts.
Concerning the second part of your question, you could probably use the OnVarNameChanging events. If the EntityState is 'New' then you can apply your logic that fetches the real values.
There is also an event that fires when you save changes (OnSavingChanges?) that you could use to determine which objects are new and set some values.
But maybe the simplest solution is to always set the defaults in the constructor and they will get overwritten if the data is loaded from the DB.
Good luck
Create a factory that produces instances for you depending on your need like:
getStudent(String studentName, long studentId, Teacher teacher) {
return new Student(studentName, studentId);
}
getStudentForDBInseration(String studentName, long studentId, Teacher teacher) {
Student student = getStudent(studentName, studentId);
student = teacher;
//some entity frameworks need the student to be in the teachers student list
//so you might need to add the student to the teachers student list
teacher.addStudent(student);
}
It's a serious lack not having an AssociationChanging (that inherits from CancelEventArgs) event.
It bothers me also very much, therefore I reported this to Microsoft Connect Please vote here!
And BTW, I also think this is also stupid that the PropertyChangingEventArgs doesn't inherit CancelEventArgs, since cancelling with an exception is not always the elegant solution, besides, throwing exceptions cost more performance than calling the OnPropertyChangingEvent then check for the returned e.Cancel, so does it cost less than raising the PropertyChangingEvent, which you anyway call them both.
Also an exception can be thrown at the handler anyway instead of marking e.Cancel as true, for those who insist to go the Exception way. Vote Here.
To maybe answer part of your question or expound on ADB's answer you can user ObjectStateManager.GetObjectStateEntry to find the state of the entities and write your custom default logic.
SaveChanges is the method on the context that you can use, or SavingChanges is the event that occurs before SaveChanges is called.
You can override SaveChanges and only call base.SaveChanges if you don't want to abort the change
There is also a ObjectMaterialized event for the context.
Between the two you can stick all your validation and creation code in one location, which may be appropriate if they are complex and include values of other objects etc..