jcc-eloquent
v1.0.0
Published
express Jcc eloquent ORM
Downloads
346
Readme
JCC Eloquent is a Node.js ORM (Object-Relational Mapping) framework inspired by Laravel's Eloquent. It facilitates interaction with databases through models, migrations, and a powerful query builder.
Features
- Fluent API for building SQL queries
- Support for
SELECT
,INSERT
,UPDATE
, andDELETE
operations - Query methods including
where
,orWhere
,orderBy
,groupBy
,having
,join
,innerJoin
,leftJoin
,rightJoin
- Pagination with
limit
andoffset
- Support for relations and eager loading
- Method chaining
- Model class with Eloquent-like methods
Installation
npm install jcc-eloquent
Usage
Importing the ORM
import { Model } from "jcc-eloquent";
export class User extends Model {
// Attributes hidden from JSON serialization
protected static hidden: string[] = ["password"];
// Attributes allowed for mass assignment
protected static fillable: string[] = ["name"];
// Attributes excluded from mass assignment
protected static guarded: string[] = ["role_id"];
// Enables soft delete functionality
protected static softDelete: boolean = true;
// Cast attributes with custom transformations
protected static casts = {
created_at: "date",
getName: this.getName,
setName: this.setName,
};
// Attribute getter - retrieves name in lowercase
public static getName(value: any, attribute: any) {
return `${value}`.toLowerCase();
}
// Attribute setter - sets name in uppercase
public static setName(value: any, attribute: any) {
return `${value}`.toUpperCase();
}
}
- Purpose: Represents a user in the application.
- Properties:
hidden
: An array of attribute names (likepassword
) that should be excluded from the model's JSON representation.softDelete
: A boolean flag that enables soft deletes for the model, allowing records to be "deleted" without being permanently removed from the database.casts
: An object that specifies how certain attributes should be cast to specific types when accessed.
Post Model
import { Model } from "jcc-eloquent";
export class Post extends Model {
// Attributes casting definitions
protected static casts = {
created_at: "now", // Example of default casting
};
// Relationship to the User model
author() {
return this.belongsTo("User", "user_id"); // Defines a belongs-to relationship
}
// Relationship to comments (polymorphic)
comments() {
return this.morphyMany("comments"); // Defines a polymorphic relationship
}
// Event hook for the creating event
static booted(): void {
this.creating((data) => {
// Custom logic before creating a post (e.g., setting defaults)
});
this.created(() => {
// Custom logic after creating a post (e.g., setting defaults)
});
this.updating(() => {
// Custom logic before updating a post (e.g., setting defaults)
});
this.updated(() => {
// Custom logic after updating a post (e.g., setting defaults)
});
this.deleting(() => {
// Custom logic before deleting a post (e.g., setting defaults)
});
}
}
Model.all()
Retrieves all records from the database table associated with the current model.
const users = await User.all();
console.log(users);
Model.find(id)
Retrieves a single record from the database table associated with the current model by its ID.
const user = await User.find(1);
console.log(user);
Model.create(data)
Creates one or more records in the database table associated with the current model.
const user = await User.create({
name: "John Doe",
email: "[email protected]",
age: 30,
});
console.log(user);
const users = await User.create([
{ name: "Jane Doe", email: "[email protected]", age: 28 },
{ name: "John Smith", email: "[email protected]", age: 35 },
]);
console.log(users);
save()
Saves the current instance to the database. If the instance has an id, it performs an update; otherwise, it performs an insert.
const user = new User();
user.name = "Abdou";
await user.save();
Relationship Definitions
hasOne(modelName, foreignKey = null, localKey = "id")
Defines a one-to-one relationship between the current model and another model.hasMany(model, foreignKey = null, localKey = "id")
Defines a one-to-many relationship between the current model and another model.belongsTo(modelName, foreignKey = null, localKey = "id")
Defines a belongs-to relationship between the current model and another model.
Using Relationships
import { QueryBuilder } from "jcc-eloquent/QueryBuilder";
const user = await User.with("posts").get();
const post = await Post.with({ author(query:QueryBuilder) => query.where('status', 'active') } , 'comments').get();
Query Builder Methods
The following methods are available in the query builder:
select(...columns)
distinct()
from(tableName)
where(column, operator, value)
whereLike(column, searchValue)
orWhere(column, operator, value)
orderBy(column, direction)
limit(value)
take(value)
offset(count)
groupBy(...columns)
having(column, operator, value)
join(table, firstColumn, operator, secondColumn)
innerJoin(table, firstColumn, operator, secondColumn)
leftJoin(table, firstColumn, operator, secondColumn)
rightJoin(table, firstColumn, operator, secondColumn)
insert(data)
get()
update(data)
delete(id)
latest(column)
oldest(column)
with(...relations)
each(callback)
map(callback)
value(field)
exists()
doesntExist()
count()
max(column)
min(column)
sum(column)
avg(column)
paginate(request, perPage)
resetQuery()
onlyTrashed()
withTrashed()
restore()
Query Instance Methods
Once you've retrieved a model instance from the database, you can interact with it using the following methods:
1. save()
Description: Saves the current instance to the database. If it's a new instance, it will perform an INSERT
; if it's an existing instance, it will perform an UPDATE
.
Returns: Promise<any>
Example:
const user = await User.find(1);
user.name = "Updated Name";
await user.save(); // Updates the existing record
2. saveQuietly()
Description: Similar to save()
, but suppresses any events that would normally be triggered during the save operation.
Returns: Promise<any>
Example:
import { Model } from "jcc-eloquent";
class Post extends Model {
protected static booted() {
this.created(async (data) => {
data.slug = "slug";
await data.saveQuietly(); // Saves without triggering any events
});
}
}
3. load(...relations: Array<any>)
Description: Eager loads relationships on the model instance.
Returns: Promise<any>
Example:
const user = await User.find(1);
await user.load("posts", "comments"); // Loads posts and comments for the user
4. update()
Description: Updates the current instance in the database with the new attribute values.
Returns: Promise<any>
Example:
const user = await User.find(1);
user.email = "[email protected]";
await user.update(); // Updates the email field in the database
5. delete()
Description: Deletes the current instance from the database.
Returns: Promise<boolean>
Example:
const post = await Post.find(1);
await post.delete(); // Deletes the post from the database
Query Instance Methods
Query Instance Methods Overview
The following methods are part of the Query Instance used for interacting with the database:
//Query Instance Methods
save() ;
saveQuietly();
load(...relations: Array<any>): ; // Eager loads relationships
update(); // Updates the current instance in the database
delete(); // Deletes the current instance from the database
- Methods:
save()
: Persists the current instance to the database.saveQuietly()
: Saves the instance without firing any events (e.g., hooks).load()
: Loads specified relationships for the instance, eager loading them for optimization.update()
: Updates the current instance with new data.delete()
: Deletes the instance from the database, returning a boolean indicating success.
DB
It provides a fluent interface for building and executing SQL queries.
- Purpose: Represents a blog post in the application.
- Properties:
casts
: Specifies how attributes should be cast. Here,created_at
is set to a default value.
- Methods:
author()
: Defines a relationship to theUser
model indicating that a post belongs to a user.comments()
: Defines a polymorphic relationship to comments.booted()
: A lifecycle hook that runs custom logic before creating a post.
Migrations
CreateUsersTable Migration
import { Schema } from "jcc-eloquent";
export class Migration {
async up() {
return Schema.create("users", (table) => {
table.id(); // Adds an auto-incrementing ID column
table.string("name"); // Adds a string column for the user's name
table.string("email").unique(); // Adds a unique string column for the user's email
table.string("password"); // Adds a string column for the user's password
table.timestamps(); // Adds created_at and updated_at timestamp columns
table.softDeletes(); // Adds a deleted_at column for soft deletes
});
}
async down() {
return Schema.dropTable("users"); // Drops the users table
}
}
- Purpose: Manages the creation and deletion of the
users
table in the database. - Methods:
up()
: Defines the schema for creating theusers
table with various columns and constraints.down()
: Defines the logic to drop theusers
table if the migration is rolled back.
CreatePostsTable Migration
import { Schema } from "jcc-eloquent";
export class CreatePostsTable {
async up() {
return Schema.create("posts", (table) => {
table.id(); // Adds an auto-incrementing ID column
table.unsignedBigInteger("user_id"); // Adds a user_id column as a foreign key
table.text("body"); // Adds a text column for the post content
table.foreign("user_id").references("id").on("users"); // Sets a foreign key constraint
table.timestamps(); // Adds created_at and updated_at timestamp columns
table.softDeletes(); // Adds a deleted_at column for soft deletes
});
}
async down() {
return Schema.dropTable("posts"); // Drops the posts table
}
}
- Purpose: Manages the creation and deletion of the
posts
table in the database. - Methods:
up()
: Defines the schema for creating theposts
table, including foreign key constraints to theusers
table.down()
: Defines the logic to drop theposts
table if the migration is rolled back.
AddSlugToPostsTable Migration
import { Schema } from "jcc-eloquent";
export class Migration {
async up() {
return Schema.table("posts", (table) => {
table.string("slug"); // Adds a slug column to the posts table
});
}
async down() {
return Schema.table("posts", (table) => {
table.dropColumns("slug"); // Drops the slug column if rolled back
});
}
}
- Purpose: Modifies the
posts
table to add a newslug
column. - Methods:
up()
: Adds theslug
column to theposts
table.down()
: Defines the logic to remove theslug
column if the migration is rolled back.
Blueprint Class Overview
The Blueprint
class is used to define the structure of database tables within migrations. Below is a list of its methods:
export declare class Blueprint {
id(): this; // Adds an ID column
unsignedBigInteger(column: string): any; // Adds an unsigned BIGINT column
default(value: string): this; // Sets a default value for the last column
foreign(column: string): this; // Defines a foreign key constraint
references(column: string): this; // Specifies the referenced column
cascade(): this; // Specifies cascade behavior on delete
on(table: string): this; // Specifies the referenced table for foreign key
string(column: string, length?: number | string): this; // Adds a VARCHAR column
integer(column: string): this; // Adds an INT column
bigInteger(column: string): this; // Adds a BIGINT column
tinyInteger(column: string, size?: Number | string): this; // Adds a TINYINT column
char(column: string, length?: number | string): this; // Adds a CHAR column
text(column: string): this; // Adds a TEXT column
mediumText(column: string): this; // Adds a MEDIUMTEXT column
longText(column: string): this; // Adds a LONGTEXT column
tinyText(column: string, length?: string | number): this; // Adds a TINYTEXT column
boolean(column: string): number; // Adds a BOOL column
binary(column: string, size: string | number): this; // Adds a BINARY column
nullable(): this; // Sets the last added column to allow NULL values
unique(): this; // Sets the last added column to have a UNIQUE constraint
dateTime(column: string): this; // Adds a DATETIME column
date(column: string): this; // Adds a DATE column
enum(column: string, types?: Array<string>): this; // Adds an ENUM column
morphs(column: string): void; // Adds columns for a polymorphic relationship
comment(text: string): this; // Adds a comment to the last column
timestamps(): any; // Adds created_at and updated_at columns
dropColumns(...columns: Array<string>): number; // Drops specified columns
renameColumn(from: string, to: string): number; // Renames a column
change(): any; // Modifies the last added column's definition
after(column: string): this; // Specifies position to add the column after
softDeletes(): number; // Adds a deleted_at column for soft deletes}
- Methods:
- Each method is used to define a specific type of column or constraint within the schema, enabling fluent method chaining for ease of use.