Сервис без работы с какой либо базой данных — не сервис. В моем случае, в качестве СУБД была выбрана mongodb. Я посмотрел существующие решения ORM для mongodb, и мне показалось более удобным пользоваться знакомыми инструментами, тем более они же будут использоваться и на клиенте, поэтому решено было попробовать применить для моделей класс Backbone.Model и заодно проверить, как все это можно кастомизировать под mongodb.
Итак, задача. Есть некий сервис, использующий базу mongodb. Необходимо иметь возможность создавать новые объекты и сохранять их в базу, из определенной коллекции доставать необходимые объекты. После изменения объекта должна быть возможность сохранять и удалять его из базы.
Backbone.js предоставляет нам 2 базовых объекта — модель и коллекцию. Мы напишем прослойку, от которой будем в дальнейшем наследовать все наши будущие классы. Сразу оговорюсь, что код соединения с базой данных и получения коллекции мы оставим в стороне и будем считать, что с ним все хорошо. Также пренебрегаем обработкой ошибок. Практически везде я использую Deferred. Поэтому, кому неизвестно, что это за зверь, можно почитать про него тут в подробностях. Запросы к mongodb делаются через этот node.js модуль — что является совсем не принципиальным. Код местами упрощен, поэтому если вы будете использовать его в своем проекте, кое-что может быть не совсем верно.
Базовая модель
Нам понадобится каким-то образом доставать одиночную модель из базы, думаю, этот метод хорошо вписывается в статическую часть класса:
var BaseModel = Backbone.Model.extend({ idAttribute: "_id", },{ db_collection: null, //какую коллекцию использует данная модель fetchFromDb: function(filter, options){ var deferred = new Deferred(); if (typeof options === 'undefined') options = {}; db.getCollection(this.db_collection, function(err, collection){ collection.find(filter).toArray(function(err, items) { ret = new this(_.defaults(items[0], options)); } deferred.resolve(ret); }.bind(this)); ); return deferred.promise; } });
Из кода также видно, что мы переопределили базовое поле класса Backbone.Model, которое определяет название ключевого поля в списке полей объекта, а в статическом поле db_collection класса мы определили коллекцию, к которой объект будет относиться.
После этого, если мы, например, определим класс пользователя:
var User = BaseModel.extend({},{ db_collection:"users"});
мы можем получить конкретного пользователя, например, так:
User.fetchFromDb({_id:1});
Теперь нужно научиться сохранять созданную модель. Для этого в backbone.js предусмотрен специальный метод sync, сигнатура которого выглядит следующим образом:
function sync(method, model, options)
где:
- method — метод сохранения, в зависимости от ситуации может возвращаться:
- create — создаем новую модель в базе;
- update — сохраняем данные существующей модели в базе;
- read — обновляем существующую модель из базы;
- delete — удаляем объект из базы.
- model — наш объект для сохранения;
- options – то, что мы передали дополнительными параметрами в момент сохранения.
sync: function(method, model, options){ var fltr = {}; var deferred = new Deferred(); db.getCollection(this.constructor.db_collection, collectionReady.bind(this)); return deferred.promise; function collectionReady(err, collection){ function done(err, result){ if (options.success) options.success(result); //таким образом мы сообщаем backbone.js об успешно выполненной операции deferred.resolve({context: this, result: true}); } fltr[this.idAttribute] = this.get(this.idAttribute); switch (method){ case "update": collection.update( fltr, this.toJSON(), {multi:false, upsert: true}, done.bind(this) ); break; case "create": collection.insert( this.toJSON(), {safe:true}, done.bind(this) ); break; case "read": collection.find(fltr).toArray(function(err, items) { done.call(this, false, items[0]); } }.bind(this)); break; case "delete": collection.findAndModify(fltr, [], {}, {remove:true}, function(err, items) { deferred.resolve(); }); } } },
Тут тоже ничего сложного. На каждый конкретный метод изменения модели мы вешаем обработчики, умеющие делать это действие в самой базе.
Теперь, после создания объекта user, например, так:
var user = new User({name:"Danil"});
мы можем спокойно его сохранить:
user.save()
а также удалить после сохранения:
user.delete()
Базовая коллекция
Работать с одиночными моделями достаточно скучно, поэтому реализуем коллекции, тем-более базовый класс в backbone.js для этого есть. По аналогии с моделью переопределим базовый класс коллекции. Все наши будущие коллекции будут наследоваться от него. Для начала напишем извлечение списка «сырых данных» из базы:
var BaseCollection = Backbone.Collection.extend({ },{ db_collection: null, __fetchCollection:function(filter, collection){ var deferred = new Deferred(); function collectionReady(err, collection){ collection.find(filter).toArray(function(err, items) { deferred.resolve(items); }.bind(this)); } db.getCollection(collection_db, collectionReady, this); return deferred.promise; } });
Здесь мы создаем статический метод класса, который позволит нам извлекать список «сырых данных» из базы, а также добавляем статическое поле db_collection, которое наследники будут переопределять, указывая, к какой коллекции в mongodb относится этот класс.
Осталось добавить инициализацию нужных моделей, добавляем еще один статический метод в наш базовый класс коллекции:
fetchFromDb:function(filter){ var deferred = new Deferred(); this.__fetchCollection(filter, this.db_collection).then( function(models){ deferred.resolve(new this(models)); }.bind(this) ); return deferred.promise; },
Здесь мы просто получаем «сырые данные» и инициализируем модели, связанные с этой коллекцией, данными из базы. Конечно, само название коллекции в mongodb мы можем получать от связанной модели, но будем считать это упрощением.
Теперь, определив коллекцию пользователей, мы можем делать запросы в базу, получая объекты с пользователями:
var Users = BaseCollection.extend({model:User}, {db_collection: "users"}); Users.fetchFromDb({name:"Danil"}).then(function(users){ _.each(users,function(user){console.log(user.id)}); });
В итоге мы получили весь необходимый минимум для работы с mongodb и дальше можем пользоваться другими возможностями, которые нам дает backbone.js.
ссылка на оригинал статьи http://habrahabr.ru/post/158569/
Добавить комментарий