Skip to content

অধ্যায় ৬: ডাটাবেস - ইনভয়েস সংরক্ষণ করা

আমাদের প্যাকেজ এখন বিজনেস লজিক হ্যান্ডেল করতে পারে, কিন্তু এই ডেটাগুলো শুধুমাত্র একটি রিকোয়েস্টের জন্য স্থায়ী। ব্রাউজার রিফ্রেশ করলেই সব হারিয়ে যায়। একটি বাস্তব-বিশ্বের অ্যাপ্লিকেশনের জন্য, আমাদের ডেটা স্থায়ীভাবে সংরক্ষণ করতে হবে। এই কাজটি করা হয় ডাটাবেসের মাধ্যমে। এই অধ্যায়ে আমরা ডাটাবেস মাইগ্রেশন এবং Eloquent মডেল তৈরি করব।

৬.১ লক্ষ্য: ইনভয়েসের তথ্য ডাটাবেসে সংরক্ষণ করা

আমাদের লক্ষ্য হলো:

  1. ইনভয়েস সংরক্ষণের জন্য একটি ডাটাবেস টেবিলের স্কিমা (schema) বা ব্লুপ্রিন্ট তৈরি করা, যা মাইগ্রেশন নামে পরিচিত।
  2. ব্যবহারকারীকে সেই মাইগ্রেশন পাবলিশ করার সুযোগ দেওয়া।
  3. একটি Eloquent মডেল তৈরি করা, যা ওই টেবিলের সাথে সহজে ডেটা আদান-প্রদান করতে সাহায্য করবে।
  4. কন্ট্রোলার থেকে মডেল ব্যবহার করে ডাটাবেসে ডেটা রিড,রাইট করা।

৬.২ Step 1: মাইগ্রেশন তৈরি

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

প্যাকেজের জন্য মাইগ্রেশন স্ট্র্যাটেজি: প্যাকেজে মাইগ্রেশন যুক্ত করার দুটি উপায় আছে:

  1. loadMigrationsFrom(): লারাভেল সরাসরি প্যাকেজের ফোল্ডার থেকে মাইগ্রেশন রান করে। এটি সহজ, কিন্তু ব্যবহারকারী টেবিল স্কিমা পরিবর্তন করতে পারে না।
  2. Publishing (পাবলিশ করা): আমরা মাইগ্রেশন ফাইলটি ব্যবহারকারীর database/migrations ফোল্ডারে কপি করে দিই। এটি ব্যবহারকারীকে চূড়ান্ত নিয়ন্ত্রণ দেয় এবং এটিই বেস্ট প্র্যাকটিস। আমরা এই পদ্ধতিটিই ব্যবহার করব।

১. মাইগ্রেশন ফাইল তৈরি করুন: প্রথমে, প্যাকেজের ভেতরে database/migrations ডিরেক্টরিটি তৈরি করুন:

bash
# packages/DevMaster/InvoiceBuilder ফোল্ডারের ভেতর থেকে
mkdir -p database/migrations

এবার, database/migrations/create_invoices_table.php.stub নামে একটি ফাইল তৈরি করুন। .stub এক্সটেনশন দিয়ে আমরা বোঝাচ্ছি যে এটি একটি টেমপ্লেট ফাইল।

php
<?php
// create_invoices_table.php.stub

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        // কনফিগ থেকে টেবিলের নাম নেওয়া হচ্ছে
        $tableName = config('invoice-builder.database.tables.invoices', 'invoices');

        Schema::create($tableName, function (Blueprint $table) {
            $table->id();
            $table->string('number')->unique();
            $table->decimal('total', 15, 2);
            $table->string('status')->default('draft');
            $table->timestamps();
        });
    }

    public function down(): void
    {
        $tableName = config('invoice-builder.database.tables.invoices', 'invoices');
        Schema::dropIfExists($tableName);
    }
};

গুরুত্বপূর্ণ বিষয়: আমরা টেবিলের নাম হার্ডকোড না করে config() হেল্পার দিয়ে নিয়েছি। এটি ব্যবহারকারীকে টেবিলের নাম পরিবর্তন করার সুযোগ দেবে।

২. কনফিগারেশন আপডেট করুন:config/invoice-builder.php ফাইলে ডাটাবেস কনফিগারেশন যোগ করুন:

php
<?php
// config/invoice-builder.php
return [
    // ... আগের কনফিগ
    'database' => [
        'tables' => [
            'invoices' => 'invoices',
        ],
    ],
];

৩. মাইগ্রেশন পাবলিশ করার ব্যবস্থা করুন:InvoiceBuilderServiceProvider-এর boot() মেথডে মাইগ্রেশন পাবলিশ করার কোড যোগ করুন:

php
public function boot(): void
{
    // ... loadRoutesFrom, loadViewsFrom

    if ($this->app->runningInConsole()) {
        // ... config and view publishing

        // Migrations publishing
        $this->publishes([
            __DIR__.'/../database/migrations/create_invoices_table.php.stub' => database_path('migrations/'.date('Y_m_d_His', time()).'_create_invoices_table.php'),
        ], 'invoice-builder-migrations');
    }
}

নোট: আমরা ফাইলের নামের সাথে date() ফাংশন ব্যবহার করে বর্তমান টাইমস্ট্যাম্প যোগ করেছি। লারাভেল মাইগ্রেশনগুলোকে টাইমস্ট্যাম্প অনুযায়ী রান করে, তাই এটি জরুরি।

৬.৩ Step 2: Eloquent মডেল তৈরি

Eloquent ORM যেহেতু Active Record প্যাটার্নে চলে তাই মডেল হলো একটি পিএইচপি ক্লাস যা সরাসরি ডাটাবেসের একটি নির্দিষ্ট টেবিলকে রিপ্রেজেন্ট করে। এটি আমাদের কোড থেকে টেবিলের সাথে কানেক্টিভিটি সহজ করে দেয়।

১. মডেল ফাইল তৈরি করুন:src/Models ডিরেক্টরি এবং তার ভেতরে Invoice.php ফাইল তৈরি করুন।

bash
mkdir -p src/Models
touch src/Models/Invoice.php

src/Models/Invoice.php ফাইলে নিচের কোডটি লিখুন:

php
<?php

namespace DevMaster\InvoiceBuilder\Models;

use Illuminate\Database\Eloquent\Model;

class Invoice extends Model
{
    protected $guarded = [];

    public function getTable()
    {
        return config('invoice-builder.database.tables.invoices', parent::getTable());
    }
}

কোড বিশ্লেষণ:

  • $guarded = []: এটি একটি মাস-অ্যাসাইনমেন্ট সুরক্ষা। খালি অ্যারে দিয়ে আমরা বলছি যে, এই মডেলের যেকোনো কলামে ডেটা ইনসার্ট করা যাবে। প্যাকেজের জন্য এটি সুবিধাজনক, কারণ ব্যবহারকারী মাইগ্রেশনে নতুন কলাম যোগ করতে পারে। তবে ক্ষেত্রবিশেষ এটা ঝুকিপুর্ন ও হতে পারে, তাই একটু বুঝে শুনে ব্যাবহার করতে হবে।
  • getTable(): এটি প্যাকেজ ডেভেলপমেন্টের জন্য অত্যন্ত গুরুত্বপূর্ণ। Eloquent সাধারণত ক্লাসের নাম থেকে টেবিলের নাম অনুমান করে (যেমন Invoice -> invoices)। আমরা এই মেথডটি ওভাররাইড করে Eloquent-কে বলছি যেন সে আমাদের কনফিগ ফাইল থেকে টেবিলের নামটি নেয়। এর ফলে, ব্যবহারকারী টেবিলের নাম পরিবর্তন করলেও আমাদের প্যাকেজ সঠিকভাবে কাজ করবে।

৬.৪ Step 3: কন্ট্রোলারে মডেল ব্যবহার করা

এখন আমরা আমাদের InvoiceController-এ ডাটাবেস থেকে ইনভয়েস তৈরি এবং প্রদর্শন করার জন্য Invoice মডেলটি ব্যবহার করব।

src/Http/Controllers/InvoiceController.php ফাইলটি আপডেট করুন:

php
<?php

namespace DevMaster\InvoiceBuilder\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use DevMaster\InvoiceBuilder\Models\Invoice; // মডেল ইম্পোর্ট করুন

class InvoiceController extends Controller
{
    public function index()
    {
        $invoices = Invoice::latest()->get();

        return view('invoice-builder::index', [
            'invoices' => $invoices,
        ]);
    }

    public function store(Request $request)
    {
        Invoice::create([
            'number' => 'INV-'.uniqid(),
            'total' => rand(100, 1000),
            'status' => 'draft',
        ]);

        return redirect()->back();
    }
}
  • index() মেথডে আমরা ডাটাবেসের সব ইনভয়েস নিয়ে index ভিউতে পাস করছি।
  • store() মেথডে আমরা একটি নতুন ইনভয়েস তৈরি করছি। আপাতত আমরা ডেমো ডেটা ব্যবহার করছি।

রাউট আপডেট: আমাদের store মেথডের জন্য একটি POST রাউট প্রয়োজন। routes/web.php ফাইলটি আপডেট করুন:

php
// routes/web.php
Route::group(config('invoice-builder.routes', []), function () {
    Route::get('/', [InvoiceController::class, 'index'])->name('invoices.index');
    Route::post('/', [InvoiceController::class, 'store'])->name('invoices.store');
});

ভিউ আপডেট:resources/views/index.blade.php ফাইলটি আপডেট করে ইনভয়েসের তালিকা এবং একটি "Create" বাটন যোগ করুন:

blade
<div class="container">
    <h1>Invoices</h1>
    <p>A list of invoices from the database.</p>

    <form action="{{ route('invoices.store') }}" method="POST" style="margin-bottom: 1rem;">
        @csrf
        <button type="submit">Create Dummy Invoice</button>
    </form>

    <table border="1" cellpadding="5" cellspacing="0" width="100%">
        <thead>
            <tr>
                <th>Invoice Number</th>
                <th>Total</th>
                <th>Status</th>
                <th>Created At</th>
            </tr>
        </thead>
        <tbody>
            @forelse ($invoices as $invoice)
                <tr>
                    <td>{{ $invoice->number }}</td>
                    <td>${{ $invoice->total }}</td>
                    <td>{{ $invoice->status }}</td>
                    <td>{{ $invoice->created_at->format('Y-m-d') }}</td>
                </tr>
            @empty
                <tr>
                    <td colspan="4">No invoices found.</td>
                </tr>
            @endforelse
        </tbody>
    </table>
</div>

৬.৫ যাচাইকরণ

১. মাইগ্রেশন পাবলিশ এবং রান করুন:

bash
# প্রথমে মাইগ্রেশন ফাইলটি পাবলিশ করুন
php artisan vendor:publish --tag=invoice-builder-migrations

# এবার ডাটাবেসে টেবিল তৈরি করুন
php artisan migrate

আপনার ডাটাবেসে এখন একটি invoices টেবিল তৈরি হয়ে যাওয়ার কথা।

২. অ্যাপ্লিকেশন পরীক্ষা করুন:

  • ব্রাউজারে /invoices URL-এ যান। টেবিলটি খালি দেখানোর কথা।
  • "Create Dummy Invoice" বাটনে ক্লিক করুন। পেজটি রিফ্রেশ হবে এবং আপনি টেবিলে একটি নতুন ইনভয়েস দেখতে পাবেন।
  • বাটনে কয়েকবার ক্লিক করে দেখুন একাধিক ইনভয়েস তৈরি হচ্ছে।

আপনি সফলভাবে আপনার প্যাকেজে ডাটাবেস ইন্টিগ্রেট করেছেন! আপনার প্যাকেজ এখন ডেটা স্থায়ীভাবে সংরক্ষণ করতে পারে।

পরবর্তী অধ্যায়ে আমরা কাস্টম artisan কমান্ড তৈরি করা শিখব, যা দিয়ে আমরা প্যাকেজ ইনস্টলেশনকে স্বয়ংক্রিয় করব এবং অন্যান্য রুটিন কাজ সহজে করতে পারব।