Laravel: জটিল ডাটাবেজ কাজ সহজ করার দমদার ট্রিক — Eloquent Query Scopes | Part 14

Abu Sayed
4 min readFeb 22, 2024

--

Eloquent Query Scopes হল লারাভেলের (Laravel) এক অসাধারণ ফিচার যা আপনার ডাটাবেজ অপারেশন সহজ ও রীতিমত করে তোলে। নতুন বৈশিষ্ট্য কাজে লাগিয়ে শিখে নিন সমস্যা সমাধানের নানা ট্রিক এবং দমদার কৌশল।

Laravel: Database Advanced — জটিল ডাটাবেজ কাজ সহজ করার দমদার ট্রিক — Eloquent Query Scopes — ব্যাখ্যা ও ব্যবহার (উদাহরণসহ)

ওহে গিঞ্জি মাথারা, কেমন আছেন? আশা করি সবাই ভালোই আছেন। আমার আজকের টপিকটা হল Eloquent Query Scopes, যা লারাভেলের ডাটাবেজ অপারেশন অনেক সহজ করে দেয়। বেশি কথা না বলি, নিচে ঠান্ডা মাথায় ধৈর্য ধরে পড়ে যান।

Query Scopes — এমন একটা বিশেষ জিনিস যা আপনার জীবন সহজ করবে

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

কোডে দেখালে কিছুটা এরকম হবে:

// পুরানো পদ্ধতি
$posts = Post::where('is_approved', 1)
->where('published_at', '>=', now()->subWeek())
->get();
// আরেকটা জায়গায় 
$popularPosts = Post::where('is_approved', 1)
->where('published_at', '>=', now()->subWeek())
->popular()
->get();

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

এখানেই আসে Query Scopes এর ব্যবহার। এর মাধ্যমে আপনি ক্যুয়েরির অংশগুলো আলাদা ফাংশন হিসেবে লিখে রাখতে পারবেন। তারপর যেখানে প্রয়োজন সেখানে সহজেই কল করতে পারবেন শর্তগুলো।

Local Query Scope লিখার নিয়ম

আসুন প্রথমে দেখি কিভাবে Local Query Scope লেখা যায়। এগুলো থাকবে আপনার মডেলের মধ্যে। যেমন:

// Post.php মডেলে
public function scopePublished($query)
{
return $query->where('is_approved', 1)
->where('published_at', '>=', now()->subWeek());
}

এখন সেই scopePublished মেথডটিকে আপনি যেখানে খুশি সেখানে কল করতে পারবেন। উদাহরণস্বরূপ:

$posts = Post::published()->get();
$popularPosts = Post::published()->popular()->get();

অনেক সুবিধা পাচ্ছেন তো? আর শর্তগুলো লিখতে হচ্ছে না বারবার। সহজেই published() মেথডটি কল করলেই হবে। দারুন একটা ট্রিক, না?

আরেকটু করে দেখি। ধরুন আপনি চান শুধু draft পোস্টগুলোই দেখতে। একটা নতুন স্কোপ লিখে ফেলুন:

public function scopeDraft($query)
{
return $query->where('is_approved', 0);
}

এবার আপনি Post::draft()->get() করলেই হবে ড্রাফট পোস্টগুলো পেতে।

Global Query Scopes — সব জায়গায় শর্ত যোগ করার ট্রিক

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

// Post.php মডেলে
public static function booted()
{
static::addGlobalScope('active', function(Builder $builder) {
$builder->where('is_deleted', 0);
});
}

এই addGlobalScope মেথডটি ব্যবহার করে আপনি একটি শর্ত দিতে পারবেন। এখন থেকে যে কোন Post কুয়েরিতে এই ক্লোজারটি রান হবে। অর্থাৎ ডিলেটেড পোস্টগুলো আর কখনো দেখতে পাবেন না।

দুর্দান্ত, তাই না? এভাবে অনেক সাধারণ এবং জরুরি শর্তগুলোকে আপনি গ্লোবালি যোগ করে ফেলতে পারবেন।

Removing Global Scopes নিজেদের খেয়ালে

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

public function getWithoutGlobalScopes(array $scopes = null) 
{
$model = $this->withoutGlobalScopes($scopes);
return $model->get();
}
public function getAllWithoutGlobalScopes()
{
return $this->withoutGlobalScopes()->get();
}

এখন আপনি যখন Post::getWithoutGlobalScopes(['active'])->get() করবেন, তখন active স্কোপটি বাদ দিয়ে বাকি স্কোপগুলো কাজ করবে।

আর getAllWithoutGlobalScopes() মেথডটি আপনাকে সব গ্লোবাল স্কোপ বাদ দিয়ে ডাটা দিবে।

এটা লারাভেলের ফ্লেক্সিবিলিটির একটা চমৎকার উদাহরণ। আপনি যেভাবে চান সেভাবে কাজ করতে পারবেন।

একটু প্রোগ্রামিং ঝামেলা?

অনেক হল না? থামি একটু। এবার একটা প্রোগ্রাম নিয়ে আলোচনা করা যাক। ধরুন, আপনার ওয়েবসাইটে একটা বিজ্ঞাপন মডিউল আছে। আপনার ডাটাবেজে বিভিন্ন এডগুলো থাকছে এবং একটা Ad মডেল আছে।

আপনাকে এই কাজগুলো করতে হবে:

  1. সক্রিয় এডগুলোই সব সময় দেখাবেন (হ্যাঁ, একটা গ্লোবাল স্কোপ লাগবে)
  2. ফিচারড এডগুলোকে আলাদা একটা কুয়েরি স্কোপ দিয়ে বাছাই করবেন
  3. লোকালস্কোপ দিয়ে শুধু প্রিমিয়াম এডগুলো বাছাই করা যাবে
  4. যে কোন এডের যে রেটিং আছে, তা সহজেই বের করা যাবে

এই প্রোগ্রামটি করতে হবে এই কাজগুলো। আপনি কি করবেন?

// Ad.php Model
public static function booted()
{
static::addGlobalScope('active', function(Builder $builder) {
$builder->where('is_active', 1);
});
}
public function scopeFeatured($query)
{
return $query->where('is_featured', 1);
}
public function scopePremium($query)
{
return $query->where('is_premium', 1);
}
public function getRating()
{
return $this->rating;
}

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

এখন এই কোডগুলোকে আপনি কিভাবে ব্যবহার করবেন?

// সব অ্যাক্টিভ এড 
$ads = Ad::all();
// শুধু ফিচারড এডগুলো 
$featuredAds = Ad::featured()->get();
// প্রিমিয়াম এডগুলোর রেটিং দেখা
foreach (Ad::premium()->get() as $ad) {
echo $ad->getRating();
}

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

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

সারাংশ

তো Eloquent Query Scopes হল একটা দারুণ টপিক, আশা করি আজকে আপনার ধারণাটা পরিষ্কার হয়েছে। এগুলোর মূল কাজ হল ক্যুয়েরিগুলোকে সংগঠিত ও রিইউসেবল করা। এছাড়াও আরও কিছু বৈশিষ্ট্য আছে যেমন:

  • Query Constraints — কিছু শর্ত সহজেই লাগানো যায়
  • Reusability — একবারে লিখে সবখানে কল করা যায়
  • Better Organization — আপনার কোডগুলো নিখুঁত সাজানো থাকবে
  • Global Scopes — যেখানে চাই সেখানে গ্লোবাল শর্ত লাগাবেন

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

আশা করি এই টপিকটা আপনাদের ভাল লেগেছে।

--

--

Abu Sayed

Bangladeshi Full Stack Web Dev, Sys Admin & DevOps Engineer. Skills: Data analysis, SQL, Kubernetes. Python, PHP & Laravel. Me on: bd.linkedin.com/in/imabusayed