A library for creating eloquent models in firestore inspired heavily by Laravel's Eloquent ORM.
npm:
npm install firestore-eloquent
yarn:
yarn add firestore-eloquent
Note: Firebase must be initialized before using these models.
Example:
const app = firebase.initializeApp({
apiKey: "AIza....",
appId: "1:27992087142:web:ce....",
projectId: "my-firebase-project",
authDomain: "YOUR_APP.firebaseapp.com",
databaseURL: "https://YOUR_APP.firebaseio.com",
storageBucket: "YOUR_APP.appspot.com",
messagingSenderId: "123456789",
measurementId: "G-12345",
});
If you have an already existing firestore instance:
import { setFirestore } from 'firestore-eloquent';
const firestore = app.firestore();
setFirestore(firestore);
You can also get the firestore instance that is being used:
import { getFirestore } from 'firestore-eloquent';
// returns firebase.firestore.Firestore
const firestore = getFirestore();
Making Models:
Using and creating models should be similar to using Laravel Eloquent Models.
Notes:
- The Authenticatable class for user models is still under development.
- It's possible to not override the constructor of the class, the library will try to infer the collection name according to the name of the class (ex: model
Post
will have a collection name ofpost
). But this is not recommended because building a production version of an app that is using this library will most likely rename these classes due to minification of the code from webpack or other module bundlers.
import { Model, ModelData } from 'firestore-eloquent';
// Optional
export interface PostData extends ModelData {
title: string;
description: string;
}
export class Post extends Model<PostData> {
type = Post;
constructor(data?: Partial<PostData>) {
super(data);
// explicitly set collection name
this.name = 'posts';
}
protected fillable() {
return ['title', 'description'];
}
}
CRUD Examples:
import { Post } from 'path/to/models';
const post = new Post({
title: 'My Title',
description: 'My Description',
});
// You can also use fill() or forceFill()
post.fill({
title: 'My Title',
description: 'My Description',
});
// create
await post.create(); /* or */ post.create(data);
// update
await post.update(); /* or */ post.update(data);
// delete
await post.delete();
// save
await post.save(); /* or */ post.save(data);
// fetching
await post.all();
// get one
await post.first();
// find one by id
await post.findOne(id);
// Reading data
// get a specific key
console.log(post.get('title')) // My Title
// get all data
console.log(post.getData()) // { title: 'My Title', description: 'My Description' }
// set data
post.set('title', 'My New Title');
await post.save();
Events are also supported.
Example:
export class Post extends Model<PostData> {
type = Post;
constructor(data?: Partial<PostData>) {
super(data);
this.name = 'posts';
}
protected fillable() {
return ['title', 'description'];
}
// register events here
protected booted() {
this.creating((post) => {});
this.created((post) => {});
this.updating((post) => {});
this.updated((post) => {});
this.deleting((post) => {});
this.deleted((post) => {});
this.saving((post) => {});
this.saved((post) => {});
}
}
Currently, only hasOne
, hasMany
and belongsTo
are supported.
Example:
export class Post extends Model<PostData> {
type = Post;
constructor(data?: Partial<PostData>) {
super(data);
this.name = 'posts';
}
protected fillable() {
return ['title', 'description'];
}
comments() {
return this.hasMany(Comment);
}
}
export class Comment extends Model<CommentData> {
type = Comment;
constructor(data?: Partial<CommentData>) {
super(data);
this.name = 'comments';
}
protected fillable() {
return ['body'];
}
post() {
return this.belongsTo(Post);
}
}
const post = new Post(data);
await post.save();
// saving
post.comments().save(new Comment({
body: 'My comment',
}));
// creating
const comment = await post.comments().create(data);
// fetching
const comments = await post.comments().getAll();
// loading
await post.load(['comments']);
Collections extend the Array
class and has several methods added to it. Model.all()
and hasMany
relationships all return Collections of that model. Some of the methods are overriden to improve their functionality such as collection.includes(model)
and collection.indexOf(model)
.
const posts = await new Post().all(); // Collection<Post>
// call methods from `Array`
const post = posts.pop();
post.set('title', 'Another Title');
// pushes the post if it does not exist, otherwise it replaces with it's index
posts.set(post);
// or do this instead
posts.replace(post)
// find by id
const newPost = posts.get(id);
// load relationships of all posts
await posts.load(['comments']);
const comments = posts.map(post => post.get('comments')); // CommentData[]
// turn into a normal `Array`
const array = posts.toArray();
Calling JSON.stringify
on models will automatically call getData()
on them, this applies to collections as well.
There are wrapper methods available to make use of firestore's onSnapshot method.
import { listen, addListener, removeListener, clearListeners } from 'firestore-eloquent';
// add a listener
const handle = addListener(Post, (posts) => {
console.log(posts) // Post[]
});
// remove a listener
removeListener(Post, handle);
// tell the library to actually start listening to onSnapshot methods
listen(Post)
// remove all registered listeners
clearListeners(Post)
const [posts, setPosts] = useState<Post[]>([]);
useEffect(() => {
const handle = addListener(Post, setPosts);
listen(Post);
return () => {
removeListener(Post, handle);
};
}, []);
Contributions are welcome! Just fork the project and make a pull request.
This library is open-sourced software licensed under the MIT license.