doctrine2 get info about updated field - doctrine-orm

HI, I want somehow to figure out which field was updated in the controller, I didn't found anything that might sound like what I'm trying to achieve in API doc for UnitOfWork.
For example, I want to add on #PreUpdate code similar to this
/** #PreUpdate */
function updateAllIsDefaultFields(){
//only if $this->isDefault propery for this entity is changed to 1 from its previous state, then update all other entities so they all have isDefault to 0
//else, means that this field is not changed, don't do anything
}
Or this must be done from the controller on every action?
I know that I can do it every time just if $this->isDefault is set to 1, but I would like to avoid it for performance and I might find it useful for some other scenarios as well.
Thanks in advance

You will have to write an Event Subscriber. Probably using an onFlush event, you can retrieve an Entities ChangeSet from the UnitOfWork to determine if a certain field has been updated and then do what ever you want.

There are some good behaviors for Doctrine2. Here is example of similar to your case.

Related

Not possible to use shorthand route handlers if RestSerializer is used? (ember-cli-mirage)

I set up a simple Ember Twiddle to show you my error that is occurring when trying to update a model.
It's considerable that I'm using ember-cli-mirage for mocking the data.
According to the docs, I created a shorthand route that should handle the PUT request.
It does, but with the error: Your handler for the url /api/shops/1 threw an error: Cannot convert undefined or null to object
When using the JSONAPISerializer, everything is working with shorthands (mirage/config.js) and I'm able to update models, but in my case I have to use the RESTSerializer with serialized IDs in the responses.
The request payload when I'm sending the model's attrs are without Id at the end of the property name, f.e.:
// attrs object in PUT request
{
name: "Shop 1",
city: "1" // belongsTo relationship,
}
Now Mirage is trying to find those properties on the respective database model that has to be updated, but cannot find it, because in the database it's cityId and not just city...
I also found this issue report and it’s working, but I was hoping I could avoid something like this. As far as I can remember, in previous versions of ember-cli-mirage (v0.1.x) it was also not needed to override the normalize method in the serializer to be able to make use of the RestSerializer with serializedIds…
My question is:
Is there a way to stick to shorthand route handlers only, or do I really have to write a helper or other custom solution only because I have to use the RestSerializer?
That would be really sad, but at least I would know then.
Thanks for your support!
Short answer: it looks like you need the custom serializer for now until the bug fix for it is merged.
Long answer: that issue looks to be an issue that occurred in the 0.2 -> 0.3 upgrade for Mirage, likely because of underlying DB changes made in Mirage. It'll probably get fixed, but for now you'll need to work around it.

Can a Custom DataProvider class expose Custom Templates?

I am currently in the process of writing a custom DataProvider. Using the Intergrate External Data documentation.
I've managed to show the external data in the Sitecore back end. However whenever I try to view the data in the items I created, I am getting an error
Null ids are not allowed. <br> Parameter name: displayName
There seems to be precious little on the subject on how to create a custom DataProvider on the Sitecore Developer Network.
The example on their website seems to only show how to import a SINGLE item into a static database. However I am simply trying to merge some items into the hierarchy and I can't find any useful documentation.
It seems that one of your methods that should return an ID doesn't. It might be GetChildIds and/or GetParentId.
Nick Wesselman wrote a good article about it gathering all the information including an example on the Marketplace. I think that is your best start. You can read it here.
Turns out I needed to include at the very least, the Fields->Section->Template in the GetParent method. To be on the safe side I included the Fields/Sections/Templates into my implementations of
GetChildIDs
GetItemDefinition
GetParentID
It wasn't obvious that this was the case, since I had in fact implemented the GetTemplates method correctly, and I had expected that should be enough.

How to update just 1 record in Ember.js with Ember-data? Currently save() and commit() on 1 record actually updates all records of model

Premise: My question is based on my research of Ember-data, which may or may not be correct. So please correct me if I have any misunderstanding. The examples are running with the latest ember as of July 2, 2013.
To edit a record of my model, just 1 record, you need to call this.get('store').commit() or this.get('model').save(). However, downstream of either of these functions actually have to update all of the records of the model, even those left untouched. So this is quite inefficient, especially if the model has numerous records.
What's the best way to update one and only one record when I'm saving the edits of one record?
UPDATE: this problem only occurs for the local-storage-adapter, not the RESTAdapter.
UPDATE #2: I did have a huge misunderstanding. Everything is okay, save() and commit() both update just 1 record, I've been fooled by local storage adapter _saveData's JSON.stringify(this._data) which printed out all records. I assumed that whatever it printed out was the data that is changed, but turns out in _saveData's callers the records in updateRecords and _didSaveRecords were just the single record I was changing. The statements below about different objects containing "all records of the model" can no longer be duplicated. I guess I misread the debugging information.
It makes sense because _saveData uses localstorage, which currently can only setItem for an entire object, which in my case is the model containing all the records. Since localstorage can't update individual entries of that object, the JSON must contain all the records.
Details:
Running Examples:
this.get('store').commit() is used in doneEditing of updating a post this jsbin.
this.get('model').save() is used in acceptChanges of updating a todo this jsbin.
If you turn on Chrome debug and walk into the above two functions, you'll see something similar to below:
Downstream, there is currentTransaction or defaultTransaction, and both have all records of the model inside.
In the case of get('store').commit(), it eventually calls DS.Store's commit, which in turn calls: (see github)
get(this, 'defaultTransaction').commit();
In the case of case of get('model').save(), it eventualy calls DS.Store's scheduleSave and flushSavedRecords, which call: (see github)
get(this, 'currentTransaction').add(record);
get(this, 'currentTransaction').commit();
Note at the end a commit() is called on xxxTransaction, this is DS.Transaction's commit().
DS.Transactionscommit()has acommitDetails, which is based on xxxTransaction, socommitDetails` also has all the records of the data. (github)
Then DS.Adapter's commit and save are called and indeed every single record is updated (github):
this.groupByType(commitDetails.updated).forEach(function(type, set) {
this.updateRecords(store, type, filter(set));
}, this);
(minor side note/question: when was commitDetails set to "updated"?)
I know that DS.Adapter can be customized, but clearly the problem of saving more than one data (i.e. all of the model entries) are set from DS.Store's commitDefaultTransaction and currentTransaction.
Somehow I feel it would be a bad idea to tinker with DS.Store or anything upstream of DS.Adapter, like making my own version of save() and commit(). Basically I am reluctant to customize anything I'm told not to, since there might be ugly side effects.
So what should I do if I want to use Ember data but can only afford to update one record only at a time?
You can create a new transaction just for managing that record, using transaction() method of the store. This transaction has the same api as the defaultTransaction.
var transaction = this.get('store').transaction();
transaction.add(model)
transaction.commit();
Committing this transaction won't affect other changes. See this blog post for further ideas.

How to manually set an object state to clean (saved) using ember-data

Explanation:
I'm using ember-data for a project of mine and I have a question that revolves around the possibility of dirtying an object and then setting its state to clean again on purpose - without commiting the changes. The scenario is this:
Say I've fetched an object via banana = App.Fruit.find('banana'); and it has a description of "Yellow fruit!". Using XHR long-polling (or WebSockets), I may receive an updated version of the object because of another user having changed the description to "A tasty yellow fruit!" at any given point in time after I fetched the original object.
Then, what I would like to do is to update the object to reflect the newly received data. For this, I've tried different approaches:
I've tried calling App.Store.load(App.Fruit, new_data);. First of all, this approach doesn't work and secondly, this is not really what I want. I could've made uncommitted changes to the object myself and in this case, it would be undesirable to just discard those (assuming the load() call would overwrite them).
I've tried looping through the new data, calling .set() - like so: banana.set('description', new_data.description); - in order to update the object properties with the new data (where applicable = not dirty). This works but it leaves the object in a dirtied state.
In order to make the object clean/updated again - and not have the adapter commit the changes! - I've taken a look at the states the object travels through. These are (at least):
Step 1: Initially, the object is in the rootState.loaded.saved state.
Step 2: Calling .set() on a property pushes it to the rootState.loaded.updated.uncommitted state.
Step 3: Calling App.store.commit(); returns the object to the rootState.loaded.saved state.
Therefore, I've tried to manually set the object state to saved after step 2 like so: banana.get('stateManager').goToState('saved');.
However, this doesn't work. The next time the store commits for any other reason, this maneuver produces an inFlightDirtyReasons is undefined error.
Question:
My question is: how can I manually change the state of a dirtied object back to clean (saved) again?
Solution for Ember Data 1.0.0-beta.7:
// changing to loaded.updated.inFlight, which has "didCommit"
record.send('willCommit');
// clear array of changed (dirty) model attributes
record.set('_attributes', {});
// changing to loaded.saved (hooks didCommit event in "inFlight" state)
record.send('didCommit');
I've searched the source code of Ember-data and I've found that loaded.saved state has a setup function that checks whether a model is clean, before setting "saved" state. If it is not clean, then it rejects a request to change state and returns to loaded.updated.uncommitted.
So you have to clean model._attributes array, which keeps attributes names and Ember will let you change state manually.
I know it isn't very good solution, because is needed to set private property of a model, but I've not found any other solutions yet.
Looking at ember-data the uncommitted state has a 'becameClean' event which consequently sets the record as loaded.saved.
This should do the trick
record.get('stateManager').send('becameClean');
Solution for Ember Data 2.6.1
record.send('pushedData');
set dirty record as loaded and saved
https://github.com/emberjs/data/blob/fec260a38c3f7227ffe17a3af09973ce2718acca/addon/-private/system/model/states.js#L250
It's an update to #Kamil-j's solution.
For Ember Data 2.0 which I am currently using I have to do the following:
record._internalModel.send('willCommit');
record._internalModel._attributes = {};
record._internalModel.send('didCommit');
As of 1.0.0.rc6.2....
This will move a model into the state of a model that has been saved.
record.get('stateManager').transitionTo('loaded.saved')
This will moves a model to a the state of a new model that has not been committed. Think new dirty model.
record.get('stateManager').transitionTo('loaded.created.uncommitted')
This will move a model into the sate of an old model that has been updated, think old dirty model:
record.get('stateManager').transitionTo('loaded.updated')
As of ember-data 1.0.0-beta.12:
record.transitionTo('loaded.saved');
It seems that record.get('stateManager') is not required anymore.
Here's what seems to work for Ember Data 1.0.0-beta.10:
record.set('currentState.stateName', 'root.loaded.saved');
record.adapterWillCommit();
record.adapterDidCommit();
record.set('currentState.isDirty', false);
Not sure if all those lines are required but just following what others have done prior to this.
Ember 2.9.1
record.set('currentState.isDirty', false);
Tested on Ember Data 2.9
pushedData action is the way to go but besides that the "originalValues" need to be reset as well.
Ember.assign(record.data, record._internalModel._attributes);
Ember.assign(record._internalModel._data, record._internalModel._attributes);
record.send('pushedData');
It looks like with newer versions everything methioned here got broken.
This worked for me with ember-data 1.0.0.beta4:
record.adapterWillCommit();
record.adapterDidCommit();
Another method that worked for me when using Ember Data 1.0.0-beta.18:
record.rollback()
This reversed the dirty attributes and returned the record to a clean state.
Seems like this may have been since deprecated in favor of record.rollbackAttributes: http://emberjs.com/api/data/classes/DS.Model.html#method_rollbackAttributes
I work on Ember data 1.13 so I used the following solution (which seems a mix between the one provided by #Martin Malinda and the other by #Serge):
// Ensure you have the changes inside the record
Object.assign(record.data, record._internalModel._attributes);
Object.assign(record._internalModel._data,record._internalModel._attributes);
// Using the DS.State you can first simulate the record is going to be saved
record.get('_internalModel').send('willCommit');
// Cleaning the prevous dirty attributes
record.get('_internalModel')._attributes = {};
// Mark the record as saved (root.loaded.created.uncommitted) even if it isn't for real
record.get('_internalModel').send('didCommit');
In this way, if we will call a further rollbackAttributes() on this record, if we will have some dirty attributes, the record will be reset to this last state (instead of having the original properties) which was exactly what I was looking for in my use case.
If we won't have any dirty attributes, nothing will change and we will keep the last attributes set using this code without having them rolled back to the original ones. Hope it helps.
Tested on Ember Data 3.8.0
Just an update to Martin Malinda's answer:
// Clear changed attributes list
record._internalModel._recordData._attributes = {};
// Trigger transition to 'loaded.saved' state
record.send('pushedData');
In my case I also needed to override serializer's normalize method.

SOAP - Why do I need to query for the original values for an update?

I'm taking over a project and wanted to understand if this is common practice using SOAP. The process that is currently in place I have to query all the values before I do an update cause I need to pass back all the values that are not being updated. Does this sound right?
Example Values:
fname=phill
lname=pafford
address=123 main
phone:222-555-1212
So if I just wanted to update the phone number I need to query for the record, get all the values and submit these values for an update.
Example Update Values:
fname=phill
lname=pafford
address=123 main
phone:111-555-1212
I just want to know if this is common practice or should I change the functionality of this?
This is not specific to SOAP. It may simply be how the service is designed. In general, there will be fields that can only be updated if you have the original value: you can't add one to a field unless you know the original value, for instance. The service seems to have been designed for the general case.
I don't think that it is a very "common" practice. However I've seen cases where the old values are posted together with the new values, in order to validate that noone else has updated the values in the meantime.