Laravel Database Eloquent ORM
Replicating and Comparing Models, Query Scopes, Events and Observers
Replicating Models
লারাভেলে Eloquent Models এ Replicating models হচ্ছে এমন একটি ফীচার যা আপনাকে বিদ্যমান instance মতো একই attributes এবং relationship গুলো সহ একটি মডেলের একটি নতুন instance তৈরি করতে দেয়, তবে ডেটাবেসে সংরক্ষণ করার আগে সেই attributes গুলির যে কোনও একটিকে modify করার option এর সুযোগ থাকে।
আপনি যখন একটি মডেলের instance কে replicate করেন, তখন লারাভেল একই মডেল class এর একটি নতুন instance তৈরি করে যার সমস্ত attributes এবং relationships গুলি original instance এর value গুলির সাথে pre-populated হয়। তারপরে আপনি ডাটাবেসে নতুন instance সংরক্ষণ করার আগে যে কোনও attributes বা relationships পরিবর্তন করতে পারেন।
`replicate()` method টি লারাভেলের Eloquent মডেলগুলিতে বিদ্যমান থাকে। এবং একই model class এ একটি নতুন instance প্রদান করে। ডিফল্টরূপে, নতুন instance হল একটি “shallow copy”, যার অর্থ কোনো related মডেল কপি করা হয় না। তবে, আপনি চাইলে একটি “deep copy” তৈরি করতে `replicate()` method এ `true` পাস করতে পারেন যাতে সংশ্লিষ্ট মডেলগুলিও অন্তর্ভুক্ত থাকে।
মডেলগুলি Replicating করা বিভিন্ন পরিস্থিতিতে উপযোগী হতে পারে, যেমন যখন একই attributes সহ আপনাকে একই মডেলের একাধিক instances তৈরি করতে হবে, বা কিছু পরিবর্তন সহ যখন আপনি একটি বিদ্যমান instance এর উপর ভিত্তি করে একটি নতুন instance তৈরি করতে চান তখন।
ধরে নিই যে আপনার কাছে একটি বিদ্যমান মডেলের instance রয়েছে, ধরা যাক এটি একটি User মডেল এবং আপনি কিছু পরিবর্তনের সাথে এটির একটি নতুন copy তৈরি করতে চান। আপনি নিচের মত করে replicate method ব্যবহার করতে পারেন:
use App\Models\User; Route::get('/replicate', function () { $user = User::find(2); $newUser = $user->replicate(); $newUser->email = 'abc@gmail.com'; $newUser->save(); });
এই উদাহরণে, আমরা প্রথমে find method ব্যবহার করে 2 এর আইডি সহ বিদ্যমান User model instance retrieve করি। তারপর, আমরা মডেল ইনস্ট্যান্সের একটি নতুন কপি তৈরি করতে replicate method ব্যবহার করি, যা আমরা $newUser ভেরিয়েবলে সংরক্ষণ করি।
তারপরে আমরা new user এর যে কোনও properties পরিবর্তন করতে পারি যা আমরা পরিবর্তন করতে চাই। এই উদাহরণে, আমরা email address পরিবর্তন করছি। অবশেষে, আমরা save method ব্যবহার করে new user কে ডাটাবেসে সংরক্ষণ করি।
মনে রাখবেন যে replicate method টি original model instance এর primary key value টি copy করে না, তাই ডাটাবেসে সংরক্ষিত হলে নতুন instance টিকে একটি নতুন primary key value বরাদ্দ করা হবে।
তবে উপরের কাজটি আপনি চাইলে fill() Method ব্যবহার করেও করতে পারেন:
use App\Models\User; Route::get('/replicate', function () { $user = User::find(3); $newUser = $user->replicate()->fill([ 'email' => 'pqr@gmail.com' ]); $newUser->save(); });
তবে আপনি যদি কোনো কলাম কে কপি না করতে চান, সেক্ষেত্রে আপনাকে নিচের মতো করে excluded কলাম গুলো বলে দিতে হবে।
use App\Models\User; Route::get('/replicate', function () { $user = User::find(3); $newUser = $user->replicate(['phone','address'])->fill([ 'email' => 'abd@gmail.com' ]); $newUser->save(); });
Query Scopes
লারাভেলে, Query Scopes গুলি আপনাকে পুনরায় ব্যবহারযোগ্য constraints সেটগুলি সংজ্ঞায়িত করতে দেয় যা ডাটাবেস কোয়েরিতে প্রয়োগ করা যেতে পারে। এগুলি মূলত আপনার ইলোকুয়েন্ট মডেলের methods যা এক্সিকিউট করার আগে ক্যোয়ারী গুলিকে সংশোধন করতে পারে।
Query Scopes গুলি আপনাকে আপনার কোড organize করতে এবং কোড duplication এড়াতে আপনাকে common constraints গুলি একবার define করতে এবং একাধিক ক্যোয়ারী জুড়ে পুনরায় ব্যবহার করতে সাহায্য করতে পারে৷ উদাহরণস্বরূপ, আপনি আপনার অ্যাপ্লিকেশনে শুধুমাত্র active ইউজারদের retrieve করার সুযোগ নির্ধারণ করতে পারেন।
Laravel Framework এ Query Scope দুই ধরণের:
১. Local Scope
2. Global Scope
Local Scope
লারাভেলে, Global Scope গুলির বিপরীতে local scopes গুলি reusable query constraints গুলিকে ডিফাইন করার একটি উপায় যা একটি Eloquent model এ প্রয়োগ করা যেতে পারে। , যা একটি মডেলের জন্য সমস্ত queries এর ক্ষেত্রে প্রযোজ্য, local scopes গুলি শুধুমাত্র সেই queries এর ক্ষেত্রেই প্রয়োগ করা হয় যা তাদের স্পষ্টভাবে কল করে।
local scopes গুলিকে ইলোকুয়েন্ট মডেলের methods হিসাবে সংজ্ঞায়িত করা হয়েছে। এই methods গুলি সাধারণত অতিরিক্ত constraints প্রয়োগের সাথে একটি query builder instance প্রদান করে। এখানে একটি local scopes এর একটি উদাহরণ যা সমস্ত একটিভ ইউজারদের retrieve করে:
class User extends Model { public function scopeActive($query) { return $query->where('active', true); } }
তারপর আপনি এই মেথডটিকে একটি query scope প্রয়োগ করতে পারেন:
use App\Models\User; Route::get('/get', function () { $activeUsers = User::active()->get(); dd($adminUsers); });
এখানে active() Method টি হল একটি query scope, এবং এটি একটি modified ক্যোয়ারী প্রদান করে যা শুধুমাত্র active user দের retrieve করার constraint অন্তর্ভুক্ত করে।
complex queries তৈরি করতে আপনি একাধিক স্কোপ একসাথে চেইন করতে পারেন। উদাহরণ স্বরূপ:
class User extends Model { public function scopeActive($query) { return $query->where('active', true); } public function scopeAdmin($query) { return $query->where('role', 'admin'); } }
use App\Models\User; Route::get('/get', function () { //$activeUsers = User::active()->get(); $adminUsers = User::active()->admin()->get(); dd($adminUsers); });
এটি শুধুমাত্র সে সব active ইউজারদের retrieve করে যারা administrator.
Query scopes গুলি বিভিন্ন parameters ও গ্রহণ করতে পারে, যা তাদের আরও flexible করার জন্য কার্যকর হতে পারে। উদাহরণস্বরূপ:
class User extends Model { public function scopeActive($query) { return $query->where('active', true); } public function scopeAdmin($query) { return $query->where('role', 'admin'); } public function scopeRole($query, $role) { return $query->where('role', $role); } }
use App\Models\User; Route::get('/get', function () { //$activeUsers = User::active()->get(); //$adminUsers = User::active()->admin()->get(); $adminUsers = User::active()->role('admin')->get(); dd($adminUsers); });
এই উদাহরণে, role() scope একটি প্যারামিটার গ্রহণ করে যা retrieve role নির্দিষ্ট করে।
Global Scopes
লারাভেলে, Global Scopes হচ্ছে একটি প্রদত্ত Eloquent model এর জন্য সমস্ত database queries এর স্বয়ংক্রিয়ভাবে constraints প্রয়োগ করার একটি উপায়। এর মাধ্যমে আপনি common constraints গুলি ডিফাইন করার সুযোগ দেয় যা একটি নির্দিষ্ট মডেলের জন্য সমস্ত queries এর ক্ষেত্রে প্রয়োগ করা যায়, এবং এর জন্য আপনাকে প্রতিটি ক্যোয়ারীতে ম্যানুয়ালি উক্ত constraints অন্তর্ভুক্ত করার প্রয়োজন হয়না।
Global Scopes কে addGlobalScope() method ব্যবহার করে Eloquent model এ ডিফাইন করা হয়েছে। এইmethod টি দুটি আর্গুমেন্ট গ্রহণ করে: scope name , এবং একটি closure যা constraints গুলিকে ডিফাইন করে।
একটি নতুন global scope class তৈরি করতে, আপনি make:scope Artisan কমান্ডটি ব্যবহার করতে পারেন:
php artisan make:scope ActiveScope
এখন Models/Scopes/ActiveScope.php ফাইলে নিচের মতো করে আপডেট করে নিন:
<?php namespace App\Models\Scopes; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Scope; class ActiveScope implements Scope { /** * Apply the scope to a given Eloquent query builder. */ public function apply(Builder $builder, Model $model): void { $builder->where('active', true); } }
এখানে আমরা ActiveScope নামক একটি global scope define করি যা শুধুমাত্র active ইউজারদের retrieve করার জন্য একটি constraint যোগ করে।
এখন আমরা User model এর boot() Method এ addGlobalScope() কল করে এই global scope রেজিস্টার করব:
<?php namespace App\Models; // use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; use App\Models\Scopes\ActiveScope; class User extends Authenticatable { protected static function boot() { parent::boot(); static::addGlobalScope(new ActiveScope); } }
এখন যদি আপনি আপনার route এ নিচের মতো করে User Model এর জন্য নিচের মতো করে query করেন, তাহলে কোনো ধরণের ম্যানুয়ালি কোনো constraints প্রয়োগ না করেই একটিভ ইউজার লিস্ট পেয়ে যাবেন :
use App\Models\User; Route::get('/get', function () { $activeUsers=User::all(); dd($activeUsers); });
আর আপনি যদি একটি নির্দিষ্ট query এর জন্য global scope remove করতে চান, আপনি withoutGlobalScope() method ব্যবহার করতে পারেন:
$users = User::withoutGlobalScope(ActiveScope::class)->get(); // retrieves all users
global scope using a closure
আপনি একটি dedicated class তৈরি করার পরিবর্তে একটি closure ব্যবহার করে একটি global scope ডিফাইন করতে পারেন। এখানে একটি closure ব্যবহার করে একটি global scope কীভাবে ডিফাইন করা যায় তার একটি উদাহরণ দেওয়া হলো:
<?php namespace App\Models; // use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { protected static function boot() { parent::boot(); static::addGlobalScope('active', function (Builder $builder) { $builder->where('active', true); }); } }
এই উদাহরণে, আমরা active নামক একটি global scope ডিফাইন করি যা শুধুমাত্র active ইউজারদের retrieve করার জন্য একটি constraint যোগ করে। আমরা User মডেলের boot() method এ addGlobalScope() কল করে এবং এটিকে একটি closure এ পাস করে যা constraint কে ডিফাইন করে এই global scope কে রেজিস্ট্রি করি।
এখন যদি আপনি আপনার route এ নিচের মতো করে User Model এর জন্য নিচের মতো করে query করেন, তাহলে কোনো ধরণের ম্যানুয়ালি কোনো constraints প্রয়োগ না করেই একটিভ ইউজার লিস্ট পেয়ে যাবেন :
$users = User::all(); // only retrieves active users
আপনি যদি একটি নির্দিষ্ট query এর জন্য closure দিয়ে তৈরি global scope রিমুভ করতে চান, আপনি withoutGlobalScope() method ব্যবহার করতে পারেন:
$users = User::withoutGlobalScope('active')->get(); // retrieves all users
Comparing Models
লারাভেলে, একাধিক মডেল ডাটাবেসের একই রেকর্ড রিপ্রেজেন্ট করে কিনা তা চেক করতে আপনি দুটি মডেলের তুলনা করতে পারেন। এটি করার জন্য, আপনি Eloquent model এ is() method টি ব্যবহার করতে পারেন।
দুটি User models কে তাদের primary key-এর উপর ভিত্তি করে কীভাবে তুলনা করা যায় তার একটি উদাহরণ এখানে দেওয়া হল:
use App\Models\User; $user1 = User::find(1); $user2 = User::find(2); if ($user1->is($user2)) { echo "User 1 and User 2 are the same"; } else { echo "User 1 and User 2 are different"; }
এই উদাহরণে, আমরা ডাটাবেস থেকে দুটি user models retrieve করি এবং is() method ব্যবহার করে তাদের তুলনা করি। যদি তারা ডাটাবেসে একই রেকর্ড রিপ্রেজেন্ট করে (অর্থাৎ, তাদের একই primary key থাকে), তবে is() method টি সত্য হবে।
তবে আপনি primary key ছাড়াও অন্যান্য attributes এর উপর ভিত্তি করে দুটি মডেল তুলনা করতে পারেন। উদাহরণস্বরূপ, ধরুন আমাদের কাছে একটি email attribute সহ একটি User মডেল রয়েছে:
use App\Models\User; $user1 = User::where('email', 'user1@example.com')->first(); $user2 = User::where('email', 'user2@example.com')->first(); if ($user1->is($user2)) { echo "User 1 and User 2 are the same"; } else { echo "User 1 and User 2 are different"; }
এই উদাহরণে, আমরা তাদের email address এর উপর ভিত্তি করে দুটি user models retrieve করি এবং is() method ব্যবহার করে তাদের তুলনা করি। যদি তাদের একই email address থাকে তবে is() method টি true হবে।
এছাড়াও লারাভেলে, isNot() method ব্যবহার করে একটি Eloquent model এ is() model এর বিপরীত বা একই নয় তা চেক করতে পারেন । এটি আপনাকে একটি মডেল অন্য মডেলের তুলনায় ডাটাবেসে একটি ভিন্ন রেকর্ড রিপ্রেজেন্ট করে কিনা তা পরীক্ষা করার অনুমতি দেয়।
কিভাবে isNot() Method ব্যবহার করতে হয় তার একটি উদাহরণ এখানে দেওয়া হল:
use App\Models\User; $user1 = User::find(1); $user2 = User::find(2); if ($user1->isNot($user2)) { echo "User 1 and User 2 are different"; } else { echo "User 1 and User 2 are the same"; }
Events
লারাভেলে, Eloquent models গুলি built-in ইভেন্টগুলির একটি সেট সরবরাহ করে যা আপনাকে মডেলের লাইফ সাইকেলের বিভিন্ন পয়েন্টে আবদ্ধ করতে দেয়। যখনই একটি মডেলের created, updated, deleted, বা saved করা হয় তখন এই ইভেন্টগুলি আপনাকে attributes গুলি মোডিফাই করা, notifications পাঠানো বা অন্যান্য কাজ সম্পাদন করার মতো কাজগুলি করতে দেয়৷
এখানে বিদ্যমান Eloquent model events গুলির একটি তালিকা রয়েছে:
- retrieved: ডাটাবেস থেকে একটি মডেল retrieved করাকালীন এটি কার্যকর হয়।
- creating: একটি নতুন মডেল instance তৈরি করাকালীন এটি কার্যকর হয়।
- created: যখন একটি নতুন মডেলের instance ডাটাবেসে সংরক্ষণ করার পরপরই এটি কার্যকর হয়।
- updating: একটি বিদ্যমান মডেলের instance আপডেট করাকালীন এটি কার্যকর হয়।
- updated: একটি বিদ্যমান মডেলের instance ডাটাবেসে আপডেট হওয়ার পরপরই এটি কার্যকর হয়।
- saving: যখন একটি মডেল সংরক্ষিত হতে চলেছে (তৈরি বা আপডেট) তখন এটি কার্যকর হয়।
- saved: একটি মডেল সংরক্ষণ করা হলে তখন এটি কার্যকর হয় (হয় তৈরি বা আপডেট)।
- deleting: একটি বিদ্যমান মডেল instance মুছে ফেলা কালীন এটি কার্যকর হয়।
- deleted: ডাটাবেস থেকে একটি বিদ্যমান মডেল উদাহরণ মুছে ফেলা হলে এটি কার্যকর হয়।
- restoring: যখন একটি soft-deleted model instance restored করা হচ্ছে তখন এটি কার্যকর হয়।
- restored: যখন একটি soft-deleted model instance restore করা হয় তখন এটি কার্যকর হয়।
ধরুন আপনার অ্যাপ্লিকেশনে একটি Post model রয়েছে এবং আপনি প্রতিটি পোস্টের জন্য একটি unique slug তৈরি করতে চান যখনই Post টি তৈরি হয়৷ আপনি পোস্ট মডেলের creating event ব্যবহার করে এটি সম্ভব করতে পারেন।
প্রথমে, আপনাকে আপনার পোস্ট টেবিলে একটি slug column যোগ করতে হবে। তারপর, আপনি আপনার পোস্ট মডেলে একটি creating method নির্ধারণ করতে পারেন যা পোস্টের টাইটেলের উপর ভিত্তি করে একটি unique slug তৈরি করে:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Str; class Post extends Model { use HasFactory; protected static function boot() { parent::boot(); static::creating(function ($post) { $slug = Str::slug($post->title); $count = Post::whereRaw("slug RLIKE '^{$slug}(-[0-9]+)?$'")->count(); $post->slug = $count ? "{$slug}-{$count}" : $slug; }); } }
এই উদাহরণে, আমরা static::creating method ব্যবহার করে পোস্ট মডেলে একটি creating method ডিফাইন করি। তারপর closure function এর মধ্যে , আমরা Str::slug method টি ব্যবহার করে পোস্টের টাইটেলের উপর ভিত্তি করে একটি slug তৈরি করি, এবং তারপর চেক করি যে একই slug অন্য কোন পোস্ট ইতিমধ্যেই ডাটাবেসে বিদ্যমান আছে কিনা। যদি তাই হয়, আমরা স্লাগের শেষে একটি সংখ্যা যুক্ত করি যাতে এটি unique হয়।
এখন, যখনই একটি নতুন Post instance তৈরি করা হয়, creating method টি স্বয়ংক্রিয়ভাবে কল করা হবে। আর এই method টি পোস্টের জন্য একটি unique slug তৈরি করবে এবং পোস্টটি ডাটাবেসে সংরক্ষিত হওয়ার আগে slug attribute সেট করবে।
এখন আপনি নিম্নোক্ত route লিখে ব্যাপারটা পরীক্ষা করে দেখতে পারেন:
use App\Models\Post; Route::get('/create', function () { $post = new Post; $post->title = "This is a sample Title"; $post->content = "This is a sample Content"; $post->save(); });
Using Closures
উপরের উদাহরণে আপনি Post Model এর boot ফাঙ্কশনের মাধ্যমে করেছেন। তবে আপনি চাইলে আপনার route বা Controller এ সাধারণ একটা closure লেখার মাধ্যমে কাজটি করতে পারেন ।এক্ষেত্রে আপনাকে Post Model এর boot() মেথডে আলাদা করে কিছু লেখার দরকার নাই :
use App\Models\Post; use Illuminate\Support\Str; Route::get('/create', function () { Post::creating(function ($post) { $slug = Str::slug($post->title); $count = Post::whereRaw("slug RLIKE '^{$slug}(-[0-9]+)?$'")->count(); $post->slug = $count ? "{$slug}-{$count}" : $slug; }); $post = new Post; $post->title = "This is a sample Title"; $post->content = "This is a sample Content"; $post->save(); });
Observers
লারাভেলে, observer হলো এমন একটি class যা একটি মডেলের নির্দিষ্ট ইভেন্টগুলির জন্য পর্যবেক্ষণ করে, যেমন creating, updating, or deleting, এবং সেই ইভেন্টগুলির response হিসাবে প্রয়োজনীয় কাজ গুলো সম্পাদন করে। observer গুলো আমাদের কোড গুলোকে Model থেকে আলাদা করার একটি সুবিধাজনক উপায় প্রদান করে, এবং অনেক গুলো মডুলার এবং রক্ষণাবেক্ষণযোগ্য কোড করার সুযোগ দেয়।
লারাভেলে observers ব্যবহার করতে হলে, আপনাকে একটি observer class ডিফাইন করতে হবে এবং আপনি যে মডেলটি observe করতে চান তার সাথে এটি রেজিস্ট্রি করতে হবে।
নিম্নোক্ত artisan কমান্ডের মাধ্যমে আপনি একটি Observer Class তৈরি করতে পারেন:
php artisan make:observer PostObserver --model=Post
এবার আপনার সদ্য তৈরি হওয়া Models/Observers/PostObserver.php ফাইলে নিচের মতো করে creating() Method টি লিখুন:
<?php namespace App\Observers; use App\Models\Post; use Illuminate\Support\Str; class PostObserver { /** * Handle the Post "created" event. */ public function created(Post $post): void { // } /** * Handle the Post "creating" event. */ public function creating(Post $post) { $slug = Str::slug($post->title); $count = Post::whereRaw("slug RLIKE '^{$slug}(-[0-9]+)?$'")->count(); $post->slug = $count ? "{$slug}-{$count}" : $slug; } /** * Handle the Post "updated" event. */ public function updated(Post $post): void { // } /** * Handle the Post "deleted" event. */ public function deleted(Post $post): void { // } /** * Handle the Post "restored" event. */ public function restored(Post $post): void { // } /** * Handle the Post "force deleted" event. */ public function forceDeleted(Post $post): void { // } }
এই উদাহরণে, আমরা PostObserver নামক একটি observer class কে ডিফাইন করেছি যা একটি পোস্ট মডেলে ইভেন্ট তৈরি করা কালীন কাজ করবে। এটি একটি নতুন Post instance তৈরি করাকালীন observer এর creating method টি কল হবে, এবং $post প্যারামিটারে নতুন পোস্টের instance থাকবে।
creating method এর অভ্যন্তরে, আমরা Str::slug method ব্যবহার করে পোস্টের টাইটেলের উপর ভিত্তি করে একটি unique slug তৈরি করছি, ঠিক যেমন আমরা পূর্ববর্তী closure উদাহরণ দিয়ে করেছি। তারপরে আমরা $post->slug attribute টি জেনারেট করা slug value এর সাথে সেট করছি, যা পোস্টটি সংরক্ষিত হলে ডাটাবেসে সংরক্ষিত হবে।
Post model এর সাথে observer নিবন্ধন করার জন্য, আমাদের AppServiceProvider এর boot method এ কোডের নিম্নলিখিত লাইন যোগ করতে হবে:
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use App\Models\Post; use App\Observers\PostObserver; class AppServiceProvider extends ServiceProvider { /** * Register any application services. */ public function register(): void { // } /** * Bootstrap any application services. */ public function boot(): void { Post::observe(PostObserver::class); } }
এখন আপনি নিম্নোক্ত route লিখে ব্যাপারটা পরীক্ষা করে দেখতে পারেন:
use App\Models\Post; Route::get('/create', function () { $post = new Post; $post->title = "This is a sample Title"; $post->content = "This is a sample Content"; $post->save(); });