Permalink
Browse files

Add a `findModel` method to allow customization of the matching for c…

…reation/updates by `findOrCreate`.
  • Loading branch information...
PaulUithol committed Jan 15, 2014
1 parent a4f3774 commit 8371089ac8bb92a00c2de38822fea7fa9f555b04
Showing with 88 additions and 16 deletions.
  1. +20 −9 backbone-relational.js
  2. +68 −7 index.html
View
@@ -395,8 +395,8 @@
* @param {String|Number|Object|Backbone.RelationalModel} item
*/
find: function( type, item ) {
var id = this.resolveIdForItem( type, item );
var coll = this.getCollection( type );
var id = this.resolveIdForItem( type, item ),
coll = this.getCollection( type );
// Because the found object could be of any of the type's superModel
// types, only return it if it's actually of the type asked for.
@@ -1306,7 +1306,7 @@
// Find (or create) a model for each one that is to be fetched
var created = [];
models = _.map( idsToFetch, function( id ) {
var model = Backbone.Relational.store.find( rel.relatedModel, id );
var model = rel.relatedModel.findModel( id );
if ( !model ) {
var attrs = {};
@@ -1677,7 +1677,7 @@
this._superModel.inheritRelations();
if ( this._superModel.prototype.relations ) {
// Find relations that exist on the '_superModel', but not yet on this model.
var inheritedRelations = _.select( this._superModel.prototype.relations || [], function( superRel ) {
var inheritedRelations = _.filter( this._superModel.prototype.relations || [], function( superRel ) {
return !_.any( this.prototype.relations || [], function( rel ) {
return superRel.relatedModel === rel.relatedModel && superRel.key === rel.key;
}, this );
@@ -1695,9 +1695,9 @@
/**
* Find an instance of `this` type in 'Backbone.Relational.store'.
* - If `attributes` is a string or a number, `findOrCreate` will just query the `store` and return a model if found.
* A new model is created with `attributes` (unless `options.create` is explicitly set to `false`) if no match is found.
* - If `attributes` is a string or a number, `findOrCreate` will query the `store` and return a model if found.
* - If `attributes` is an object and is found in the store, the model will be updated with `attributes` unless `options.update` is `false`.
* Otherwise, a new model is created with `attributes` (unless `options.create` is explicitly set to `false`).
* @param {Object|String|Number} attributes Either a model's id, or the attributes used to create or update a model.
* @param {Object} [options]
* @param {Boolean} [options.create=true]
@@ -1710,8 +1710,9 @@
var parsedAttributes = ( _.isObject( attributes ) && options.parse && this.prototype.parse ) ?
this.prototype.parse( _.clone( attributes ) ) : attributes;
// Try to find an instance of 'this' model type in the store
var model = Backbone.Relational.store.find( this, parsedAttributes );
// If specified, use a custom `find` function to match up existing models to the given attributes.
// Otherwise, try to find an instance of 'this' model type in the store
var model = this.findModel( parsedAttributes );
// If we found an instance, update it with the data in 'item' (unless 'options.merge' is false).
// If not, create an instance (unless 'options.create' is false).
@@ -1733,7 +1734,7 @@
/**
* Find an instance of `this` type in 'Backbone.Relational.store'.
* - If `attributes` is a string or a number, `find` will just query the `store` and return a model if found.
* - If `attributes` is a string or a number, `find` will query the `store` and return a model if found.
* - If `attributes` is an object and is found in the store, the model will be updated with `attributes` unless `options.update` is `false`.
* @param {Object|String|Number} attributes Either a model's id, or the attributes used to create or update a model.
* @param {Object} [options]
@@ -1745,6 +1746,16 @@
options || ( options = {} );
options.create = false;
return this.findOrCreate( attributes, options );
},
/**
* A hook to override the matching when updating (or creating) a model.
* The default implementation is to look up the model by id in the store.
* @param {Object} attributes
* @returns {Backbone.RelationalModel}
*/
findModel: function( attributes ) {
return Backbone.Relational.store.find( this, attributes );
}
});
_.extend( Backbone.RelationalModel.prototype, Backbone.Semaphore );
View
@@ -81,6 +81,7 @@
<li><a href="#RelationalModel-build">build</a></li>
<li><a href="#RelationalModel-findOrCreate">findOrCreate</a></li>
<li><a href="#RelationalModel-find">find</a></li>
<li><a href="#RelationalModel-findModel">findModel</a></li>
</ul>
<ul>
<li><a href="#RelationalModel-events"><strong>Catalog of Events</strong></a></li>
@@ -140,13 +141,18 @@ <h1>
Backbone-relational is hosted on <a href="https://github.com/PaulUithol/Backbone-relational">GitHub</a>,
and is available under the <a href="https://github.com/PaulUithol/Backbone-relational/blob/master/LICENSE.txt">MIT license</a>.
</p>
<p>
Thanks go out to <a href="http://progressiveplanning.com">Progressive Planning</a> for allowing me the time to
work on Backbone-relational!
</p>
</section>
<section id="downloads">
<h2>
Downloads &amp; Dependencies
<span style="padding-left: 7px; font-size:11px; font-weight: normal;" class="interface">(Right-click, and use
"Save As")</span>
<span style="padding-left: 7px; font-size:11px; font-weight: normal;" class="interface">
(Right-click, and use "Save As")
</span>
</h2>
<table>
@@ -854,7 +860,7 @@ <h4 class="code">
In this case, you should call <q>setup</q> manually after defining your subclass CoffeeScript-style. For example:
</p>
<p class="warning">
Note: this is a static method. It operate on the model type itself, not on an instance.
Note: this is a static method. It operates on the model type itself, not on an instance of it.
</p>
</section>
@@ -878,7 +884,7 @@ <h4 class="code">
Create an instance of a model, taking into account what submodels have been defined.
</p>
<p class="warning">
Note: this is a static method. It operate on the model type itself, not on an instance.
Note: this is a static method. It operates on the model type itself, not on an instance.
</p>
</section>
@@ -907,7 +913,7 @@ <h4 class="code">
as a function (<q>this</q> will not point to a model), instead of as a method.</dd>
</dl>
<p class="warning">
Note: this is a static method. It operate on the model type itself, not on an instance.
Note: this is a static method. It operates on the model type itself, not on an instance of it.
</p>
</section>
@@ -924,7 +930,62 @@ <h4 class="code">
Accepts the same options as <q>findOrCreate</q> (except for <q>create</q>).
</p>
<p class="warning">
Note: this is a static method. It operate on the model type itself, not on an instance.
Note: this is a static method. It operates on the model type itself, not on an instance of it.
</p>
</section>
<section id="RelationalModel-findModel">
<h4 class="code">
findModel
<code>relationalModel.findModel(attributes&lt;string|number|object&gt;)</code>
</h4>
<p>
Returns: <q>Backbone.RelationalModel</q> A model instance.
</p>
<p>
A hook to override the matching when updating (or creating) a model.
The default implementation is to look up the model by id in the store:
<q>return Backbone.Relational.store.find( this, attributes );</q>
</p>
<p>
Custom behavior is useful in cases where (a collection of) nested data gets saved to the server.
Consider saving the following model:
</p>
<pre class="language-javascript"><code class="language-javascript runnable" data-setup="#example-zoo"><!--
-->var zoo = new Zoo( { id: 1, name: 'Artis', animals: [
{ species: 'Giraffe' },
{ species: 'Camel' }
] } );
alert( JSON.stringify( zoo.toJSON(), null, 4 ) );
</code></pre>
<p>
Normally, whatever you use as server-side logic will respond by creating two animals, and assigning them
an id. The response will be used by Backbone-relational to update existing models.
However, updating a model starts by looking up the local model with the same id; and in this case,
Backbone-relational does not know which local models corresponds to which created animal with an id.
A simple fix in this case would be to add a fallback option for the matching by using the animal's
species. Do note you'd want to use a more robust method usually, such as using a new model's <q>cid</q>.
</p>
<pre class="language-javascript"><code class="language-javascript runnable" data-setup="#example-zoo"><!--
-->Zoo.findModel = function( attributes ) {
// Try to find an instance of 'this' model type in the store
var model = Backbone.Relational.store.find( this, attributes );
if ( !model && _.isObject( attributes ) ) {
var coll = Backbone.Relational.store.getCollection( this );
model = coll.find( function( m ) {
return m.species === attributes.species;
});
}
return model;
};
</code></pre>
<p class="warning">
Note: this is a static method. It operates on the model type itself, not on an instance of it.
</p>
</section>
@@ -1182,7 +1243,7 @@ <h4>0.8.6
</li>
<li>
<a href="https://github.com/PaulUithol/Backbone-relational/pull/380"><q>#380</q></a>:
AFix <q>pop</q> on an empty collection.
Fix <q>pop</q> on an empty collection.
</li>
<li>
<a href="https://github.com/PaulUithol/Backbone-relational/pull/370"><q>#370</q></a>:

0 comments on commit 8371089

Please sign in to comment.