چگونه با استفاده از طرحواره های Mongoose کد تمیزتر بنویسیم
اگر به ساخت برنامه های NodeJS با استفاده از Mongoose ORM عادت دارید، این مقاله برای شما مناسب است. در آن، برخی از آپشن های جالب طرحوارههای Mongoose را مورد بحث قرار میدهیم که به شما کمک میکند کدهای سازمانیافتهتر و قابل نگهداریتری بنویسید.
برای استفاده بیشتر از این راهنما، باید پیشینه ای در جاوا اسکریپت داشته باشید، نحوه عملکرد Mongoose را بدانید و اصول برنامه نویسی شی گرا را بدانید.
در اینجا چیزی است که ما پوشش خواهیم داد:
طرحواره Mongoose چیست؟
طرحواره های Mongoose روشی ساختاریافته برای مدل سازی داده ها در پایگاه داده MongoDB ارائه می دهند که به شما امکان می دهد خصوصیات و رفتار اسناد را تعریف کنید. طرحواره ها به عنوان طرحی برای سندی که در پایگاه داده ذخیره می شود عمل می کنند. آنها توسعه دهندگان را قادر می سازند یکپارچگی داده ها را اعمال کنند و با MongoDB به شیوه ای شهودی و سازماندهی شده کار کنند.
در یک مجموعه MongoDB، یک طرحواره فیلدهای اسناد، انواع دادهها، قوانین اعتبارسنجی، مقادیر پیشفرض، محدودیتها و موارد دیگر را مشخص میکند.
از نظر برنامه ریزی، طرحواره Mongoose یک شی جاوا اسکریپت است. در واقع، این یک نمونه از یک کلاس داخلی به نام Schema
در ماژول mongoose
است. به همین دلیل می توانید روش های بیشتری را به نمونه اولیه آن اضافه کنید. این به شما کمک می کند بسیاری از ویژگی ها مانند میان افزار، روش ها، استاتیک و موارد دیگر را پیاده سازی کنید. در این آموزش با برخی از آنها آشنا خواهید شد.
ویژگی هایی که می آموزید چگونه پیاده سازی کنید:
تبعیض کننده
تمایزگر ویژگی است که شما را قادر می سازد چندین مدل (زیرگروه) ایجاد کنید که از یک مدل پایه (والد) به ارث می برند. این امر با تعریف یک طرحواره پایه و سپس گسترش آن با فیلدهای اضافی خاص برای هر زیرگروه یا هر طرحواره فرزند اتفاق می افتد.
تمام اسناد، صرف نظر از مدل خاص آنها، در یک مجموعه MongoDB ذخیره می شوند. این کار داده های شما را در یک مجموعه واحد سازماندهی می کند و در عین حال امکان پرس و جو و مدیریت داده ها را فراهم می کند. همچنین، هر سند شامل یک فیلد خاص است که نوع مدل خاص خود را نشان میدهد و به Mongoose اجازه میدهد بین زیرگروههای مختلف تمایز قائل شود.
نحوه استفاده از discriminator
:
با تعریف یک طرحواره پایه شروع کنید، که دارای فیلدهای مشترک بین زیرگروه ها خواهد بود. پس از آن، یک مدل از آن ایجاد کنید.
بیشتر بخوانید
خالق دارک سولز مطمئن نیست که چگونه یا آیا سریال بدون دارک سولز 2 به راه خود ادامه می داد یا نه.
import mongoose from 'mongoose' ; const baseSchema = new mongoose.Schema({ name : { type : String , required : true }, }, { discriminatorKey : 'kind' }; // defaults to '__t'); const BaseModel = mongoose.model( 'Base' , baseSchema);
زیرشاخه هایی را ایجاد کنید که طرحواره پایه را با تعریف discriminator
برای هر یک گسترش دهند.
const catSchema = new mongoose.Schema({ meow : { type : Boolean , default : true } }); // subtype const Cat = BaseModel.discriminator( 'Cat' , catSchema); const dogSchema = new mongoose.Schema({ bark : { type : Boolean , default : true } }); // subtype const Dog = BaseModel.discriminator( 'Dog' , dogSchema);
سپس می توانید اسناد را به روش معمولی ایجاد کنید. همه اسناد در یک مجموعه ذخیره می شوند، اما هر کدام بسته به مدل فرعی خود، نوع خاص خود را دارند.
const fluffy = await Cat.create({ name : 'Fluffy' }); const rover = await Dog.create({ name : 'Rover' });
مورد استفاده discriminator
:
بیایید بگوییم که شما در حال ساختن یک برنامه وب چند کاربره تجارت الکترونیک هستید که سه نقش اصلی کاربر را در خود جای می دهد: مدیران ، مشتریان و فروشندگان . هر یک از این نقش ها نقش مهمی در اکوسیستم خرید آنلاین ایفا می کند.
اگر سعی کنید برای هر نقش یک کلاس بسازید، متوجه می شوید که هر سه فیلدها و متدهای مشترکی دارند. ممکن است تصمیم بگیرید که یک طرحواره والدین (کاربر) و برخی دیگر از طرحواره های فرزند (مشتری، فروشنده، مدیر) ایجاد کنید که از آن به ارث می برند.
برای رسیدن به این هدف می توانید از discriminator
استفاده کنید.
در فایل user.model.js
کد زیر را اضافه کنید:
import mongoose from "mongoose" ; const userSchema = mongoose.Schema( { name : String , profilePic : String , email : String , password : String , birthDate : Date , accountAcctivated : { type : Boolean , default : false }, }, { timestamps : true , discriminatorKey : "role" , } ); const User = mongoose.model( "User" , userSchema); export default User;
اکنون شما مدل پایه ( User
) را دارید که زیرگروه های دیگر از آن به ارث می برند. در این طرح والد، شما فیلدهای مشترکی را تعریف می کنید که همه کاربران صرف نظر از نقششان به اشتراک بگذارند.
در فایل client.model.js
شما:
import mongoose from "mongoose" ; import User from "./user.model.js" ; const clientSchema = mongoose.Schema( { products : Array , address : String , phone : String , } ); const Client = User.discriminator( "Client" , clientSchema); export default Client;
در فایل seller.model.js
شما:
import mongoose from "mongoose" ; import User from "./user.model.js" ; export const sellerSchema = mongoose.Schema( { rating : Number , businessType : { type : String , enum : [ "individual" , "corporation" ] }, } ); const Seller = User.discriminator( "Seller" , sellerSchema); export default Seller;
در فایل admin.model.js
شما:
import mongoose from "mongoose" ; import User from "./user.model.js" ; export const adminSchema = mongoose.Schema( { permissions : Array , assignedTasks : Array , department : String , } ); const Admin = User.discriminator( "Admin" , adminSchema); export default Admin;
زیرشاخه ها یا فرزندان Client
، Seller
و Admin
خواهند بود. در هر طرح زیرمجموعه، شما باید هر گونه فیلد یا رفتار اضافی را فقط به این نوع فرعی اضافه کنید. با ایجاد مدل فرزند با استفاده از تفکیک کننده، مدل فرزند تمام فیلدها و روش های User
مدل والد خود را به ارث می برد.
پس کد قبلی یک مجموعه user
در پایگاه داده ایجاد می کند که هر سند دارای یک فیلد role
یا Client یا Seller یا Admin است. همه اسناد اکنون فیلدهای والد ( user
) را به اشتراک می گذارند و بسته به role
هر سند، هر کدام یک فیلد اضافی دیگر دارد.
اگرچه تمام اسناد در یک مجموعه ذخیره میشوند، مدلها هنگام کدنویسی کاملاً از هم جدا میشوند. این به چه معناست؟
به عنوان مثال، اگر نیاز به بازیابی همه مشتریان از مجموعه User
دارید، باید Client.find({})
را بنویسید. این عبارت از کلید تفکیک کننده برای یافتن تمام اسنادی که role
آنها Client
است استفاده می کند. به این ترتیب، هر عملیات یا درخواستی که به یکی از مدلهای فرزند اشاره دارد، همچنان جدا از مدل والد نوشته میشود.
توجه: قبل از ورود به بخشهای بعدی، فقط به خاطر داشته باشید که هر گونه استاتیک، متد، سازنده پرس و جو یا قلاب باید قبل از ایجاد خود مدل تعریف شود (یعنی قبل از const User = mongoose.model("User", userSchema);
).
استاتیک
استاتیک برای تعریف توابعی که در سطح مدل عمل می کنند مفید است. آنها به شما اجازه می دهند تا توابع قابل استفاده مجدد را برای عملیات مربوط به کل مدل تعریف کنید. آنها به کپسوله کردن منطقی کمک می کنند که در مدل به جای اسناد جداگانه اعمال می شود و کد شما را تمیزتر، سازماندهی شده تر و قابل نگهداری تر می کند.
روشهایی مانند find
، findOne
، findById
و روشهای دیگر همگی متدهایی هستند که به مدل متصل هستند. با استفاده از خاصیت statics
طرحواره های Mongoose، می توانید روش مدل خود را بسازید.
استاتیک قدرتمند است. با استفاده از آنها، می توانید پرس و جوهای پیچیده ای را کپسوله کنید که ممکن است بخواهید دوباره از آنها استفاده کنید. همچنین، میتوانید برای عملیاتهایی که دادهها را اصلاح یا جمعآوری میکنند، استاتیک ایجاد کنید، مانند شمارش اسناد یا یافتن اسناد بر اساس معیارهای خاص.
مورد استفاده statics
استاتیک به راحتی ساخته می شود. شما با استفاده از شی statics
یک متد استاتیک را روی طرحواره خود تعریف می کنید.
در فایل user.model.js
خود، این متدهای ثابت، countUsers
و findByEmail
را اضافه کنید:
// model method userSchema.statics.countUsers = function ( ) { return this .countDocuments({}); }; // model method userSchema.statics.findByEmail = async function ( email ) { return await this .findOne({ email }); };
در داخل هر روش ایستا، this
به خود مدل اشاره دارد. در این مثال، this
در this.findOne({ email })
به مدل User
اشاره دارد.
مثال استفاده:
const user = await User.findByEmail( "foo@bar.com" ); //or const client = await Client.findByEmail( "foo@bar.com" ); //or const seller = await Seller.findByEmail( "foo@bar.com" ); //or const admin = await Admin.findByEmail( "foo@bar.com" );
وقتی متد static را روی مدل خود فراخوانی می کنید، متد فراخوانی می شود و مدلی که استاتیک را روی آن فراخوانی کرده اید this
می شود. این خط یک پرس و جو را برای یافتن یک سند در مجموعه MongoDB انجام می دهد که در آن فیلد email
با آرگومان email
ارائه شده مطابقت دارد.
روش ها
متدها توابعی هستند که میتوانید روی یک طرحواره تعریف کنید و میتوان آنها را بر روی نمونههایی از اسناد ایجاد شده از این طرح فراخوانی کرد. آنها به کپسوله کردن منطق در خود سند کمک می کنند و کد شما را تمیزتر و ماژولارتر می کنند.
با استفاده از روشهای نمونه، میتوانید به راحتی با دادههای مرتبط با اسناد خاص تعامل داشته باشید و آنها را دستکاری کنید.
methods
مورد استفاده
میتوانید با استفاده از شی methods
، متدها را روی طرحواره تعریف کنید.
در فایل user.model.js
خود، یک روش سند اضافه کنید که از طریق آن می توانید رمز عبور یک کاربر را تحلیل کنید:
// instance or document method userSchema.methods.getProfile = function ( ) { return ` ${ this .name} ( ${ this .email} )` ; }; // instance or document method userSchema.methods.checkPassword = function ( password ) { return password === this .password ? true : false ; };
در داخل هر روش سند، this
به خود سند اشاره دارد. در این مثال، this
در this.password
به سند user
اشاره می کند که در آن متد فراخوانی می شود. یعنی شما می توانید به تمام فیلدهای این سند دسترسی داشته باشید. این بسیار ارزشمند است زیرا می توانید هر چیزی را که مربوط به این سند است بازیابی، اصلاح و تحلیل کنید.
مثال استفاده:
const client = await Client.findById(...) client.checkPassword( "12345" ) //or const seller = await Seller.findById(...) seller.checkPassword( "12345" ) //or const admin = await Admin.findById(...) admin.checkPassword( "12345" )
از آنجایی که متدها توابع سطح نمونه هستند، روی اسناد فراخوانی می شوند. await Client.findById(...)
سندی را برمی گرداند که دارای تمام متدهای داخلی و همچنین روش های از پیش تعریف شده شما checkPassword
و getProfile
. پس با فراخوانی، برای مثال client.checkPassword("12345")
، this
کلمه کلیدی در تعریف تابع checkPassword
با سند client
جایگزین می شود. این به نوبه خود رمز عبور کاربر را با رمز عبور ذخیره شده قبلی در پایگاه داده مقایسه می کند.
Query Builder
سازنده پرس و جو در Mongoose یک روش سفارشی است که می توانید آن را روی شی پرس و جو تعریف کنید تا الگوهای پرس و جوی رایج را ساده و محصور کنید. این سازندگان پرس و جو به شما امکان می دهند منطق پرس و جو قابل استفاده مجدد و خواندنی ایجاد کنید و کار با داده های خود را آسان تر می کند.
یکی از با ارزش ترین کاربردهای سازندگان پرس و جو، زنجیره سازی است. آنها را می توان با سایر سازنده های پرس و جو که ساخته اید یا با روش های پرس و جوی استاندارد مانند پیدا کردن، مرتب سازی و غیره زنجیره کرد.
مورد استفاده سازنده پرس و جو
شما سازنده های پرس و جو را با گفت ن آنها به ویژگی query
یک طرحواره Mongoose تعریف می کنید.
در فایل user.model.js
خود، یک روش کمکی پرس و جو اضافه کنید که به شما امکان می دهد صفحه بندی را پیاده سازی کنید.
// query helper userSchema.query.paginate = function ( { page, limit } ) { // some code const skip = limit * (page - 1 ); return this .skip(skip).limit(limit); };
برای پیاده سازی صفحه بندی، به دو متغیر مهم نیاز دارید: اول، شماره صفحه، و دوم، تعداد مواردی که در هر صفحه بازیابی می کنید.
برای پرس و جو از پایگاه داده برای تعداد خاصی از اسناد، شما همیشه از روش های جستجوی داخلی skip
و limit
در mongoose
استفاده می کنید. skip
برای تنظیم مکان نما بعد از تعداد معینی از اسناد استفاده می شود و پس از آن پرس و جو اجرا می شود. limit
برای بازیابی تعداد خاصی از اسناد استفاده می شود.
در داخل هر روش سازنده پرس و جو، this
به خود کوئری اشاره دارد. و از آنجایی که سازندگان پرس و جو زنجیره ای هستند، می توانید هر یک از آنها را پس از یکدیگر فراخوانی کنید.
در نهایت، هر متد سازنده پرس و جو باید یک mongoose query object
را برگرداند، به همین دلیل است که باید return this.skip(skip).limit(limit)
بنویسید.
مثال استفاده:
const results = await Client.find().paginate({ page : 2 , limit : 5 }); //or const results = await Seller.find().paginate({ page : 2 , limit : 5 }); //or const results = await Admin.find().paginate({ page : 2 , limit : 5 });
سپس میتوانید آن را در هر پرس و جوی فراخوانی کنید و await Client.find().paginate({ page: 2, limit: 5 })
تابع paginate
را فراخوانی میکند و this
کلمه کلیدی را با Client.find()
با استفاده از سازنده query جایگزین میکند.
میتوانید صفحهبندی را با شرایط خاصی پیادهسازی کنید، اما همیشه skip
و limit
فراخوانی میکنید. با تعریف سازنده پرس و جو paginate
، خود را تکرار نخواهید کرد و می توانید منطق را در یک تابع واحد کپسوله کنید.
قلاب
هوک ها (همچنین به عنوان میان افزار نیز شناخته می شوند) توابعی هستند که در نقاط خاصی از چرخه عمر یک سند اجرا می شوند. آنها به شما اجازه می دهند رفتار سفارشی را قبل یا بعد از عملیات خاصی مانند ذخیره، به روز رسانی یا حذف اسناد اضافه کنید.
انواع قلاب
Pre Hooks: قبل از عملیات اجرا می شود.
Post Hooks: بعد از یک عملیات اجرا می شود.
مورد استفاده قلاب
در فایل user.model.js
خود، یک میان افزار ذخیره post
اضافه کنید که از طریق آن می توانید پس از ذخیره سند کاربر در پایگاه داده، ایمیلی برای فعال سازی حساب ارسال کنید.
// post hook userSchema.post( "save" , async function ( doc, next ) { // send email logic // if succeeded return next(); // if failed return next( new Error ( "Failed to send email!" )); });
پس از ایجاد یک کاربر از طریق model.create()
یا هر زمانی که متد save()
در سند کاربر فراخوانی کردید، تابع callback فراخوانی می شود.
در این مثال، اگر لازم است از ارسال ایمیل در ذخیره خودداری کنید، باید یک شرط بنویسید تا مطمئن شوید که این save
فقط برای یک کاربر جدید است. می توانید چیزی شبیه if (doc.createdAt.getTime() === doc.updatedAt.getTime())
بنویسید.
خلاصه
در این مرور کلی از آپشن های Mongoose، ما چهار مفهوم کلیدی را تحلیل کردهایم: تشخیصدهندهها، استاتیک، روشها و قلابها.
تفکیککنندهها به شما اجازه میدهند چندین مدل ایجاد کنید که یک طرح مشترک را به اشتراک میگذارند که انواع مختلف اسناد را قادر میسازد در یک مجموعه واحد ذخیره شوند. این امر مدیریت داده ها و پرس و جو را تسهیل می کند.
استاتیک روشهایی در سطح مدل هستند که قابلیتهای قابل استفاده مجدد را برای کل مدل ارائه میکنند. آنها پرس و جوهای پیچیده و منطق دستکاری داده ها را کپسوله می کنند و به تمیز نگه داشتن پایگاه کد شما کمک می کنند.
متدها توابع سطح نمونه هستند که بر روی نمونه های سند جداگانه عمل می کنند. آنها رفتارهای سفارشی و دستکاری داده ها را برای هر سند امکان پذیر می کنند، پس می توانید داده های سند را به روشی خاص تغییر دهید، مانند قالب بندی یا محاسبه مقادیر بر اساس فیلدهای آن.
قلابها (یا میانافزار) به شما امکان میدهند تا عملکردها را در نقاط خاصی از چرخه عمر سند اجرا کنید، مانند قبل یا بعد از ذخیره، بهروزرسانی یا حذف یک سند. این برای پیاده سازی اعتبارسنجی، ورود به سیستم یا هر گونه عوارض جانبی دیگر مربوط به عملیات پایگاه داده مفید است.
این ویژگی ها با هم، تطبیق پذیری و سازماندهی مدل های Mongoose شما را افزایش می دهند و ساخت برنامه های قوی و قابل نگهداری با MongoDB را آسان تر می کنند.
در اینجا می توانید یک مخزن پیدا کنید که در آن می توانید درباره طرحواره های Mongoose و موارد استفاده بیشتر بیاموزید.
ارسال نظر