ExpressJS Lanjutan #6 | One to Many Relationship

Ahmad Nurul Laiq
4 min readApr 25, 2024

Pengantar

Dalam artikel ini, kita akan membahas bagaimana mengimplementasikan relasi one-to-many (satu-ke-banyak) antara dua model menggunakan ExpressJS dan Sequelize. Relasi ini memungkinkan satu entitas (model) memiliki banyak entitas lain yang terkait dengannya.

Update model Category dan Product

Untuk membangun relasi one-to-many, kita perlu memperbarui definisi model Category dan Product dalam file model Sequelize. Pada model Category, kita mendefinisikan bahwa satu kategori dapat memiliki banyak produk menggunakan metode hasMany. Pada model Product, kita mendefinisikan bahwa satu produk hanya dapat berasal dari satu kategori menggunakan metode belongsTo.

'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Category extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
Category.hasMany(models.Product, {
foreignKey: 'categoryId'
});
}
}
Category.init({
name: {
type: DataTypes.STRING,
allowNull: false,
unique: {
args: true,
msg: "Nama kategori sudah ada, silahkan masukkan kategori lain"
},
validate: {
notEmpty: {
msg: "Inputan data nama kategori tidak boleh kosong"
}
}
},
description:
{
type: DataTypes.TEXT,
allowNull: true
}
}, {
sequelize,
modelName: 'Category',
});
return Category;
};
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Product extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
Product.belongsTo(models.Category, {
foreignKey: 'categoryId',
});
}
}
Product.init({
id: {
type: DataTypes.UUID,
primaryKey: true,
defaultValue: DataTypes.UUIDV4
},
name: {
type: DataTypes.STRING,
allowNull: false,
unique: {
args: true,
msg: 'Nama sudah digunakan'
},
validate: {
notNull: {
msg: 'Nama tidak boleh kosong'
}
}
},
description: DataTypes.STRING,
price: {
type: DataTypes.INTEGER,
allowNull: false,
validate: {
notNull: {
msg: 'Harga tidak boleh kosong'
},
isNumeric: {
msg: 'Harga harus berupa angka'
},
min: {
args: [1],
msg: 'Harga minimal 1'
}
}
},
categoryId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'Categories',
key: 'id'
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
validate: {
notNull: {
msg: 'Kategori tidak boleh kosong'
},
isExist(value) {
return sequelize.models.Category.findByPk(value).then((el) => {
if (!el) {
throw new Error('Kategori tidak ditemukan')
}
})
}
}
},
image: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notNull: {
msg: 'Image tidak boleh kosong'
}
}
},
stock: {
type: DataTypes.INTEGER,
defaultValue: 0,
},
countReview: {
type: DataTypes.INTEGER,
defaultValue: 0,
}
}, {
sequelize,
modelName: 'Product',
});
return Product;
};

Refactory categoryController dan productController

Setelah mendefinisikan relasi pada model, kita perlu membuat controller untuk mengelola data kategori dan produk. Pada controller kategori, kita dapat mengambil data kategori beserta produk-produk yang terkait dengannya menggunakan opsi include pada metode findByPk. Pada controller produk, kita dapat melakukan pencarian produk dengan menggunakan opsi include untuk mengambil data kategori yang terkait.

exports.getCategoryByID = asyncHandle(async (req, res) => {
try {
const {
id
} = req.params;
const category = await Category.findByPk(id, {
include: [{
model: Product,
attributes: {
exclude: ['categoryId']
}
}]

});
if (!category) {
return res.status(404).json({
status: "fail",
message: "Category not found"
});
}
res.status(200).json({
status: "success",
data: category
});
} catch (error) {
res.status(500).json({
status: "fail",
error: error.message
});
}
});
// Route for reading products with optional search, limit, and page
exports.getProducts = asyncHandle(async (req, res) => {
const {
search,
limit,
page
} = req.query; // Dapatkan nilai dari query string

let productsData = ""; // Variable untuk menyimpan data produk

if (search || limit || page) {
const pageData = parseInt(page) || 1; // Konversi ke integer, default 1 jika kosong
const limitData = parseInt(limit) || 100; // Konversi ke integer, default 100 jika kosong
const offsetData = (pageData - 1) * limitData;
const searchData = search || "";

const products = await Product.findAndCountAll({
limit: limitData,
offset: offsetData,
where: {
name: {
[Op.like]: `%${searchData}%` // Gunakan % sebelum dan setelah searchData
}
},
include: [
{
model: Category,
attributes: {
exclude: ['createdAt', 'updatedAt', 'description']
}
}
]
});
productsData = products;
} else {
const products = await Product.findAndCountAll({
include: [
{
model: Category,
attributes: {
exclude: ['createdAt', 'updatedAt', 'description']
}
}
]
});
productsData = products;
}

return res.status(200).json({
data: productsData
});
});

// Route for getting details of a single product by ID
exports.getProductById = asyncHandle(async (req, res) => {
const id = req.params.id;
const productData = await Product.findByPk(id, {
include: [
{
model: Category,
attributes: {
exclude: ['createdAt', 'updatedAt', 'description']
}
}
]
});

if (!productData) {
return res.status(404).json({
error: "Product not found"
});
}

return res.status(200).json({
data: productData
});
});

Kesimpulan

Dalam pengembangan aplikasi dengan ExpressJS dan Sequelize, relasi one-to-many memungkinkan kita untuk memodelkan hubungan antara dua entitas di mana satu entitas dapat memiliki banyak entitas lain yang terkait dengannya. Dengan mengimplementasikan relasi ini, kita dapat mengambil data dari entitas terkait secara efisien dan mengelola operasi CRUD dengan baik.

Jika Anda ingin melihat contoh kode yang lengkap, Anda dapat mengunjungi repositori GitHub saya di sini.

Referensi:

  • https://sequelize.org/docs/v6/core-concepts/assocs/#one-to-many-relationships

--

--

Ahmad Nurul Laiq

full stack developer an have experience for 2+ years. I am always eager to learn and stay abreast of the latest technology in the world of software development.