বাংলা আর্টিকেল:{Many To Many Prisma CRUD} Learn MERN Stack By Create Real-Estate Project -Part_7

বাংলা আর্টিকেল:{Many To Many Prisma CRUD} Learn MERN Stack By Create Real-Estate Project -Part_7

(Many To Many Relation in Prisma ) ->Full-Stack Project To Learn React, NodeJs, MongoDB, Prisma And More Web Tools.

নিজেকে informed রাখা খুব ভাল ,তবে তা খুব বেশি নয়। অনেক সময় খুব বেশি ইনফরমেশন আমাদের এমন ফীল দিতে পারে ,like আমি তো জানি এই ব্যাপারটা ,কিন্তু জানা এবং বোঝা বা জ্ঞান অর্জন করা এক ব্যাপার না।

প্রোগ্রামিং এ যে বিষয়টা সব থেকে গুরুত্বপূর্ণ ,তুমি কিছু ইনফরমেশন জান এবং সেই ইনফরমেশন গুলোকে ব্যবহার করে কিছু করতে পারছ। জ্ঞান অর্জনের জন্য তোমাকে অবশ্যইএফোর্ড ও সময় দিতে হবে।

কোন একটা ভিডিও বা আর্টিকেল দেখে দ্রুত নিজেকে informed রাখতে পারি ,তবে programming শেখার লম্বা দৌড়ে নিজেকে এগিয়ে রাখতে, তোমাকে সত্যি সত্যি নিজের হাত নোংরা করতে হবে। ছোট খাটো প্রজেক্ট করা,কিছু প্রব্লেম সল্ভ করা বা একটা বাচ্চা web application তৈরী করা , এ সব কিছু জ্ঞানের দরজা খুলে দিবে ,ভাবতে শেখাবে সমস্যা নিয়ে। প্রজেক্ট করতে গিয়ে আটকে গেলে তা সল্ভ করতে যে এফোর্ড আর সময় তুমি দিবে তাই অ্যাকচুয়াল লার্নিং।

আজকাল হচ্ছে তথ্য বহুল দুনিয়া ,আমরা তথ্য জানি ,ইনস্ট্যান্ড কিছু সময় সেটা নিয়ে ভাবি তার পরে ভুলে যাই। একটা বিশাল সমুদ্র তোমার কাছে পারিদেয়া অসম্ভব যতক্ষণ, তুমি সেই সমুদ্রে ঝাঁপ দিয়ে সাঁতার না কাট। প্রোগ্রামিং হচ্ছে সেই সমুদ্র ,এখানে জানার শেষ নেই ,তবে সেই জানা কে জ্ঞান এ পরিণত করতে হলে চাই, লেগে থাকা ,ধার্য আর আমি হাল ছাড়বো না সেই অদম্য ইচ্ছা।

মনেরাখবে ,information তোমায় কিছু বিষয় সম্পর্কে সজাগ করবে ,তবে knowledge তোমায় সমস্যার সমাধান ও ডিসিশন make করতে সাহায্য করবে।

#Hey_Programmers,

এক সপ্তাহের একটা লম্বা জার্নির পরে তোমাদের সাথে আবারো নতুন একটা বাংলা প্রোগ্রামিং এর আর্টিকেল নিয়ে হাজির হলাম।

বিগত এপিসোড এ আমরা part-6 শেষ করেছি। দেখেছি one to many রিলেসন এর crud বা বলাযেতে পারে পরিশ্মা crud ।

তারই ধারাবাহিকতায় ,আজকে আমরা জানব Many-to-many (m-n) relations। যে ,সম্পর্ক গড়ে ওঠে অনেকের সাথে অনেকের। তুমি, যখন স্কুল ,কলেজ বা ইউনিভার্সিটি পড়তে তখন ,তোমার অনেক অনেক সাবজেক্ট ছিল আবার সেই প্রত্যেক সাবজেক্ট এর অনেক অনেক স্টুডেন্ট বা তোমার সহপাঠী ছিল like vice versa। এটাই মূলত Many-to-many (m-n) relations ।

তোমায় স্বাগতম ,বাংলা আর্টিকেল:{Many To Many Prisma CRUD} Learn MERN Stack By Create Real-Estate Project -Part_7 এ।

let's elaborate a bit further to provide a clearer picture:

#Many_ToMany(m_n)_Relations:

MongoDB ডাটাবেস যেহেতু ডকুমেন্ট টাইপ তাই many-to-many relationships এর ক্ষেত্রে prisma কিছু rules outline ডিফাইন করে দিয়েছে । ডাটা ভেলিডেশন করা ,এফিসিয়েন্ট ভাবে ডাটা রিট্রিভ করা এবং স্টোর করার সময় আমাদের এসব রুলস গুলি ফলো করতে হবে।

সাধারণত আমরা যখন রিলেশনাল ডাটাবেস নিয়ে কাজ করি যেমন ,MySQL ,PostGresql এসব হচ্ছে টেবিল বেজ ট্রাডিশনাল ডাটাবেস তাই এখানে প্রায়ই দেখা যায় many-to-many relationships এ Implicit মানে রিলেসন মেইনটেইন করার জন্য আলাদা একটা টেবিল নিয়ে, সেখানে সম্পর্ক ঘটান হয়।

কিন্তু, মঙ্গো ডিবি তেমন ভাবে কাজ করে না । MongoDB এর ক্ষেত্রে বলাহয় expalicit রিলেসন মেইনটেইন করে । মানে যে যার কালেকশন এর মধ্যে রিলেসন ঘটায় according to রিলেসন ব্রিজ।

  1. রিলেসন এর দুই দিকের মডেল (যেমন, "categories" and "posts") অবশ্যই array বা list type হতে হবে , এর মানে ,মডেল গুল multiple items store করতে পারবে।

  2. রিলেসন ডিফাইন করার সময় অবশ্যই দুই দিকে @relation attribute ব্যবহার করতে হবে , এবং "fields" , "references" এসব arguments মেনসন করতে হবে স্পেসিফিক ভাবে।

  3. "fields" যা @relation attribute এর argument অবশ্যই একটা মাত্র ফিল্ড উল্লেখ থাকবে , এবং সেই ফিল্ড হতে হবে list বা array type (যেমনঃ [categoryIDs] or [postIDs])।

  4. @relation attribute এর "references" argument অবশ্যই একটা ফিল্ড ডিফাইন করা থাকবে । ডিফাইন করা referenced অবশ্যই model এ থাকতে হবে (যেমনঃ , "id") এবং "fields" argument এর মত একই ডাটা টাইপ হতে হবে , কিন্তু singular ফরমেট এ (list বা array না )। (it typically stores it as an object with a special structure called an ObjectId. This ObjectId is a unique identifier for the document)

  5. "references" ফিল্ড এ must @id attribute থাকতে হবে , indicating it's the primary identifier for the referenced model.

  6. No referential actions (e.g., cascade delete) are allowed in the @relation.

  7. ট্রাডিশনাল relational databases এ ইমপ্লিসিট many-to-many relations হয় তবে, MongoDB Implicit many-to-many relations সাপোর্ট করে না । আমরা mongodb তে এক্সপ্লিসিড many-to-many relation ব্যাবহার করব।

সুতরাং ,আমরা যখন স্কিমা ডিফাইন করব এই বিষয় গুলো মাথায় রেখেই ডিফাইন করব।

let's see the schema :

আমাদের আগের স্কিমার কথা মনে আছে ?

আমরা সেখানে আরো কিছু মডেল এবং ফিল্ডস অ্যাড করব যা ,আমাদের many to many রিলেসন কে এক্সপ্লেইন করবে :

model Post {
  id          String @id @default(auto()) @map("_id") @db.ObjectId
  title       String
  authorEmail String
  author      User   @relation(fields: [authorEmail], references: [email])
  categoryIDs String[] @db.ObjectId
  categories Category[] @relation(fields: [categoryIDs],references: [id])
}

model Category {
  id String @id @default(auto()) @map("_id") @db.ObjectId
  name String @unique
  postIDs String[] @db.ObjectId
  posts Post[] @relation(fields: [postIDs],references: [id])
}

কোড খেয়াল করলে দেখতে পাবে সব আগের মতোই ,শুধু এবারে আমরা রিলেসন দুইটা মডেল, মানে Post এবং Category দুইদিক থেকেই কানেক্ট করেছি। আর কানেকশন ব্রিজ হিসেবে categoryIDs ও postIDs নিয়েছি। মাল্টিপল ডাটা স্টোর করতে হবে বলে আমরা array নিয়েছি যার ডাটা টাইপ হচ্ছে array অফ string ।

references এর দিকে খেয়াল করলে দেখতে পাবে Category মডেল এর id নিয়েছে যার ডাটা টাইপ হচ্ছে স্ট্রিং ,Post এর ক্ষেত্রেও same ।

আমরা বলেছিলাম references এবং fields আর্গুমেন্ট এর ডাটা টাইপ হবে একই।

যেমন ধর ,
\>fields : এর ডাটা সেট হচ্ছে array কিন্তু array এর মধ্যে ডাটা টাইপ স্ট্রিং।

\>references : একই ভাবে আমাদের সিঙ্গুলার ফরমেট ব্যবহার করেছি কিন্তু এর ডাটা টাইপ স্ট্রিং।

এছাড়া আমরা Category মডেল এ name ইউনিক রেখেছি কারণ আমরা সবসময় চাইব প্রত্যেকটা ক্যাটেগরি নাম আলাদা আলাদা হোক। dulpicate না হোক ।

আমরা মডেল এর উপর ভিত্তি করে প্রত্যেক post কে categories করতে পারি আবার প্রত্যেক ক্যাটাগরিতে মাল্টিপল posts রাখতে পারি।

Note: আমাদের মডেল ডিফাইন হয়ে গেলে ডাটাবেস কে জানাতে হবে যে নতুন কালেকশন অ্যাড হবে তাই আমরা টার্মিনাল এ npx prisma db push কমান্ড দিয়ে আপডেট করে নিব।

crud অপারেশন চালানোর পরে আমাদের আউটপুট দেখতে কিছুটা এমন হবে :

//Output Category Callection
{
  "findCategories": [
    {
      "id": "64ffb843b7f95687ef77a982",
      "name": "server",
      "postIDs": []
    },
    {
      "id": "64ffc2addc269072a04d1e17",
      "name": "All",
      "postIDs": [
        "64ffc57955306788381da3c2",
        "64ffc58655306788381da3c3"
      ]
    }
  ]
}

//Post Collection
{
  "message": "Get The All Posts",
  "findPost": [
    {
      "id": "64ffc481d53c4685dd482413",
      "title": "2this Post Is From Create Post",
      "authorEmail": "2@gmail.com",
      "categoryIDs": []
    },
    {
      "id": "64ffc57955306788381da3c2",
      "title": "2this Post Is From Create Post",
      "authorEmail": "2@gmail.com",
      "categoryIDs": [
        "64ffc2addc269072a04d1e17"
      ]
    },

  ]
}

যদি ক্যাটাগরি বা পোস্ট id না থাকে তবে এম্পটি array হবে আর থাকলে id মেনশন থাকবে।

#Implementing_Category_Crud:

আমরা অলরেডি জানি কিভাবে crud অপারেশন চালাতে হয় তাই code এক্সপ্লেইন প্রয়োজন হয়তো হবে না ,প্রোগ্রামিং এর মেইন কনসেপ্ট DRY ফলো করছি জাস্ট কোড দিয়ে দিচ্ছি তুমি বুঝে নিতে পারবে আশাকরি :

#create_routes/categoriesRoute.js:

পোস্ট এবং ইউসার এর মত ক্যাটাগরি create ,read ,update এবং delete করার জন্য route তৈরী করব :

//routes/categoriesRoute.js
import express from "express";
import {
  deleteCategories,
  getCategory,
  postCategory,
  updateCategories,
} from "../controllers/categorisController.js";

const router = express.Router();

router.post("/category", postCategory);
router.get("/category", getCategory);
router.put("/category/:categoryId", updateCategories);
router.delete("/category/:categoryId", deleteCategories);

export { router as categoryRouter };

#create_controllers/categorisController.js:

এবারে কন্ট্রোলার তৈরী করছি আমাদের ক্যাটেগরি crud কে কন্ট্রোল করার জন্য :

//controllers/categorisController.js

import prisma from "../config/prismaClient.js";
// Get All Category
export const getCategory = async (req, res, next) => {
  try {
    const findCategories = await prisma.category.findMany({});
    if (findCategories <= 0) {
      res.json("No Category Found"); 
      return;
    }
    res.json({
      findCategories,
    });
  } catch (err) {
    res.status(500).json(err);
  }
};

// create category
export const postCategory = async (req, res, next) => {
  try {
    const { name } = req.body;

    const findCategoris = await prisma.category.findFirst({
      where: {
        name: name,
      },
    });

    if (!findCategoris) {
      const newCategory = await prisma.category.create({
        data: {
          name: name,
        },
      });
      res.json({
        message: "Category Is SucessFully Created",
        newCategory,
      });
      return;
    }
    res.json({
      message: "Category Already Exsist",
      category: findCategoris,
    });
  } catch (err) {
    res.status(500).json(err);
  }
};
// delete category
export const deleteCategories = async (req, res, next) => {};

//update category
export const updateCategories = async (req, res, next) => {};

get ও create ফাঙ্কশন আমাদের ডাটা দেখানো ও ক্যাটাগরি তৈরী করতে দিবে লজিক অনুযায়ী। এর সাথে আপডেট এবং ডিলিট এর কাজ একই ভাবে আগের crud দেখে প্রাকটিস করে ফেলতে পারো।

#implimenting_route_in_index.js:

এবার সব শেষে আমাদের index মানে, সার্ভার কে জানিয়ে দেয়ার পালা আমাদের categoryRouter নামে route create হয়েছে :

//index.js
app.use("/api",categoryRouter)

#check_Api_by_restClient:

তুমি apiTest.http এর মধ্যে তোমার তৈরী করা api গুলো টেস্ট করতে পার :

যেমনটা আগেও করেছ:

// apiTest.http 
//-------------for API endpoint
### Get Categoris 
GET http://localhost:3000/api/category

### Create Category
POST http://localhost:3000/api/category
Content-Type: application/json

{
    "name":"All"
}

### Delete Category
DELETE http://localhost:3000/api/category/:categoryId

### Update Category
PUT http://localhost:3000/api/category/:categoryId
Content-Type: application/json

{
    "name":"web"
}

তুমি ক্যাটাগরি get মানে, সব ক্যাটাগরি দেখতে url রিকোয়েস্ট করলে http://localhost:3000/api/category দেখবে আউটপুট আসবে "No Category Found" । কারণ আমাদের কোন ক্যাটাগরি create করা হয়নি।

সুতরাং আমাদেরকে প্রথমে ক্যাটাগরি create করতে হবে ।

তার জন্য post মেথড এর মাধ্যমে আমরা http://localhost:3000/api/category url এ হিট করে body তে json আকারে All নামের ক্যাটাগরি বলে দিতে পারি।

আউটপুট আসবে :

//Request 
POST http://localhost:3000/api/category
Content-Type: application/json

{
    "name":"All"
}

// Response
{
  "message": "Category Is SucessFully Created",
  "newCategory": {
    "id": "65026e30eeba469facf2498d",
    "name": "All",
    "postIDs": []
  }
}

যেহেতু কোন পোস্ট এর আন্ডার এ এই ক্যাটাগরি নাই তাই postId একটা empty array আসছে।

এবারে তুমি যদি আবার গেট রিকোয়েস্ট চালায় তবে :

আউটপুট আসবে :

//Request
GET http://localhost:3000/api/category

//Response
{
  "findCategories": [
    {
      "id": "65026e30eeba469facf2498d",
      "name": "All",
      "postIDs": []
    }
  ]
}

আমরা একই ভাবে আরো একটা category create করে ফেলতে পারি server নামে, বা তুমি চাইলে যে কোন নাম দিতে পার ।

//Request
POST http://localhost:3000/api/category
Content-Type: application/json

{
    "name":"Server"
}

//Response 
{
  "message": "Category Is SucessFully Created",
  "newCategory": {
    "id": "65026f3ceeba469facf2498e",
    "name": "Server",
    "postIDs": []
  }
}

নতুন একটা ক্যাটাগরি আমাদের তৈরী হল ।

আমরা আমাদের সব গুলি ক্যাটাগরি get রিকোয়েস্ট এর মাধ্যমে দেখলে :

আউটপুট আসবে :

//Request

GET http://localhost:3000/api/category

//Response
{
  "findCategories": [
    {
      "id": "65026e30eeba469facf2498d",
      "name": "All",
      "postIDs": []
    },
    {
      "id": "65026f3ceeba469facf2498e",
      "name": "Server",
      "postIDs": []
    }
  ]
}

একদম পারফেক্টলি আমাদের স্কিমা অনুযায়ী কোয়েরি এর মাধ্যমে আউটপুট দিচ্ছে।

#Add_Common_Category_in_Posts:

আমরা এর আগে posts তৈরী করেছি ,প্রত্যেক টি পোস্ট এর জন্য আমাদের category মডেল এ postIds ডিফাইন করা আছে কিন্তু প্রিভিয়াস code আমরা যখন পোস্ট তৈরী করেছি তখন এমন কোন লজিক নেই যেখানে বলা আছে আমাদের ক্যাটাগরি কি হবে।

এবারে আমাদের privious post কন্ট্রোলার এ বলে দিতে হবে । প্রথমে আমরা আমাদের পোস্ট এর একটা ডিফল্ট ক্যাটাগরি মেনশন করে দিব "All" । এতে করে আমাদের পরবর্তীতে স্পেসিফিক ক্যাটেগরি অনুযায়ী ফিল্টার করতে সুবিধা হবে ।

#controllers/postController.js>userPost:

//controllers/postController.js>fn> userPost
// create User related posts

const userPost = async (req, res, next) => {
  try {
    const { email, title } = req.body;
    const findUser = await prisma.user.findFirst({
      where: {
        email: email,
      },
    });

    if (!findUser) {
      res.json({
        message: "User Not In Database!",
        user: email,
      });
      return;
    }

    const createPost = await prisma.post.create({
      data: {
        authorEmail: email,
        title,
        categories:{
          connect:{
            name:"All"
          }
        }
      },
    });

    res.json({
      message: "Find The User in Database",
      user: createPost,
    });
  } catch (error) {
    res.status(500).json(error);
  }
};

তেমন কোন চেঞ্জ নাই কোড ,যখন আমরা পোস্ট create করছি তখন data: { authorEmail: email, title, categories:{ connect:{ name:"All" } } }, });
অবজেক্ট এ আগে থেকেই আমরা ইমেইল ,টাইটেল এসব বলেদিচ্ছি এবারে তার সাথে ক্যাটাগরি মডেল থেকে categories connect করে দিব যার নাম হবে "All" । এই ক্যাটাগরি যেহেতু ডিফল্ট আকারে সব পোস্ট এর সাথে এসোসিয়েট থাকবে তাই স্ট্যাটিক ভাবে বলে দিচ্ছি। এবং ক্যাটাগরি নাম যেহেতু ইউনিক আমাদের কালেকশন এ তাই স্পেসিফিক id এর সাথে কানেক্ট করবে।

Note: Connect Query: This term refers to a way of establishing a connection between two pieces of data in a database. Specifically, it involves connecting a record to another record that is already related to it. This connection is made by specifying the unique identifier, often an ID, of the related record.

আউটপুট :

// Request
POST http://localhost:3000/api/user/post
Content-Type: application/json

{
    "email":"1dev@gmail.com",
    "title":"This Is 3nd Blog in Dev Mehedi"
}
//Response
{
  "message": "Find The User in Database",
  "user": {
    "id": "6503b4e65e690c15583f120e",
    "title": "This Is 3nd Blog in Dev Mehedi",
    "authorEmail": "1dev@gmail.com",
    "categoryIDs": [
      "65026e30eeba469facf2498d"
    ]
  }
}

খেয়াল করতে দেখতে পাবে আমরা যখন নতুন পোস্ট create করেছি categoryIDs এ আমাদের All নামের ক্যাটেগরির id এসোসিয়েট হয়ে গেছে।
এবং

তুমি যদি ক্যাটাগরি ডাটা কালেকশন এর আউটপুট দেখো :

//Reques 
GET http://localhost:3000/api/category

//Response 
{
  "findCategories": [
    {
      "id": "65026e30eeba469facf2498d",
      "name": "All",
      "postIDs": [
        "6503b4e65e690c15583f120e"
      ]
    },
    {
      "id": "65026f3ceeba469facf2498e",
      "name": "Server",
      "postIDs": []
    }
  ]
}

দেখতে পাবে all নামের ক্যাটাগরি তে postIDs স্পেসিফিক post এর id নিয়ে নিয়েছে।

আমরা ড্রপ ডাউন এর মাধ্যমে ইউসার কে বলে ক্যাটাগরি সিলেক্ট করার অপসন দিব কালেকশন ক্যাটেগরি নামের উপর ভিত্তি করে। তবে frontend এ কাজ করার সময় আমরা তা দেখে ফেলব।

#Conclusion:

এই পর্বে আমরা দেখে নিয়েছি Many To Many রিলেসন কিভাবে হয়। কিছু কোড রেফ্যাক্টর করেছি এবং একই সাথে আমরা ক্যাটাগরি তৈরী করা দেখেছি।

মোটামুটি কাজ চালানোর মতো করে ডাটাবেস এর প্রধান তিনটা রিলেসন আমাদের দেখা হয়ে গিয়েছে। পরিশ্মা কোয়ারি গুলো দেখেছে এবং ইমপ্লিমেন্ট করেছি।

যেহেতু , আমরা ব্যাকএন্ড কে স্ট্রং করছি তাই পরবর্তী চ্যাপ্টার এ অথেন্টিকেশন নিয়ে কিছু কথা বলা আবশ্যক।

Note: Try creating your own counting function in JavaScript, and experiment with it in different scenarios. If you have any questions or thoughts, feel free to share them in the comments below.

💡
শেয়ার ও কমেন্ট করে আমায় ফিডব্যাক দিতে ভুলনা

তাহলে আজকের মত বিদায়।

#devTj #tanvir_mehedi #Prisma #mernstack #javascript