Skip to content

অধ্যায় ২: আইসোলেটেড এনভায়রনমেন্ট সেটআপ

এই অধ্যায়ের উদ্দেশ্য হলো প্যাকেজকে এমনভাবে সাজানো যাতে এটি হোস্ট অ্যাপের Kernel ব্যবহার করতে পারে, কিন্তু নিজের কোড, টেস্ট, অ্যাসেট সম্পূর্ণ আলাদা ও আইসোলেটেড থাকে। আমরা ফোল্ডার স্ট্রাকচার, composer.json মেটাডাটা, এবং Composer Path Repository (সিমলিংক) কিভাবে ইন্টারনালি কাজ করে তা ডিপ ডাইভ করব।

২.১ packages ডিরেক্টরি এবং ফোল্ডার স্ট্রাকচার ডিজাইন

হোস্ট অ্যাপের রুটে একটি packages/ ডিরেক্টরি তৈরি করে তার নিচে ভেন্ডর/প্যাকেজ নাম অনুযায়ী নেমস্পেসড ফোল্ডার রাখুন।

laravel-app/
├─ app/
├─ bootstrap/
├─ packages/
│  └─ DevMaster/
│     └─ InvoiceLite/
│        ├─ src/                // মূল কোড (ServiceProvider, Facade, Jobs, etc.)
│        ├─ config/             // প্যাকেজ কনফিগ (প্রিফিক্সড)
│        ├─ routes/             // web.php/api.php (কনফিগ-ড্রিভেন প্রিফিক্স)
│        ├─ database/
│        │  └─ migrations/      // stub বা publish করার ফাইল
│        ├─ resources/
│        │  ├─ views/           // loadViewsFrom + publish
│        │  └─ assets/          // JS/CSS/Images (publish->public/vendor)
│        ├─ tests/              // Orchestra Testbench
│        ├─ composer.json
│        └─ README.md
└─ composer.json
  • প্রত্যেক ফোল্ডারের ওপর .gitkeep রাখতে পারেন যাতে গিট খালি ফোল্ডার ট্র্যাক করে।
  • App Kernel (App\Http\Kernel) কখনোই এই স্ট্রাকচারে থাকে না; প্যাকেজ কেবল সার্ভিস প্রোভাইডারের মাধ্যমে Kernel-এর রাউটিং/মিডলওয়্যার পাইপলাইন ব্যবহার করবে।
  • একাধিক প্যাকেজ থাকলে packages/DevMaster/{PackageName} প্যাটার্ন অনুসরণ করলে PSR-4 অটোলোড ম্যাপ সহজ হয়।

২.২ composer.json কনফিগারেশন এবং মেটাডাটা স্ট্যান্ডার্ড

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

json
{
  "name": "devmaster/invoicelite",
  "description": "InvoiceLite - Laravel invoice package (multi-tenant ready).",
  "type": "library",
  "license": "MIT",
  "authors": [
    { "name": "Your Name", "email": "you@example.com" }
  ],
  "require": {
    "php": "^8.2",
    "illuminate/support": "^11.0"
  },
  "require-dev": {
    "orchestra/testbench": "^9.0"
  },
  "autoload": {
    "psr-4": {
      "DevMaster\\InvoiceLite\\": "src/"
    },
    "files": [
      "src/helpers.php"
    ]
  },
  "autoload-dev": {
    "psr-4": {
      "DevMaster\\InvoiceLite\\Tests\\": "tests/"
    }
  },
  "scripts": {
    "test": "vendor/bin/phpunit"
  },
  "minimum-stability": "dev",
  "prefer-stable": true,
  "extra": {
    "laravel": {
      "providers": [
        "DevMaster\\InvoiceLite\\InvoiceLiteServiceProvider"
      ],
      "aliases": {
        "InvoiceLite": "DevMaster\\InvoiceLite\\Facades\\InvoiceLite"
      }
    }
  }
}

ডিজাইন নোটস:

  • "type": "library" রাখুন; "project" দিলে কম্পোজার এটিকে অ্যাপ ধরে নেবে (ভুল)।
  • minimum-stability: dev + prefer-stable: true দিলে আপনি Path Repository থেকে @dev ভার্সন নিতে পারবেন, কিন্তু স্টেবল থাকলে সেটাই আগে নেবে।
  • scripts.test রাখলে হোস্ট অ্যাপেও composer test -d packages/DevMaster/InvoiceLite চালিয়ে প্যাকেজ টেস্ট করা সহজ হয়।
  • autoload.files এ হেল্পার দিলে তা ইগার-লোড হবে; তবে এগুলো নামস্পেসড স্ট্যাটিক ক্লাস হলে আরও সেফ।

প্যাকেজকে হোস্ট অ্যাপে ইনস্টল করা মানে vendor/-এ কপি করা নয়; আমরা Composer Path Repository ব্যবহার করে সিমলিংক তৈরি করব। এতে একই ফাইল সিস্টেমে এক কপি থাকবে, উভয় জায়গায় দেখা যাবে।

২.৩.১ হোস্ট composer.json এ Path Repository

json
{
  "repositories": {
    "invoicelite": {
      "type": "path",
      "url": "packages/DevMaster/InvoiceLite",
      "options": {
        "symlink": true
      }
    }
  }
}
  • symlink: true হলে vendor/devmaster/invoicelite আসলে packages/DevMaster/InvoiceLite-এর সিমলিংক হয়। ফাইল সেভ করলেই হোস্ট অ্যাপে প্রভাব পড়ে।
  • যদি symlink বাদ দেন বা ফাইল সিস্টেম সাপোর্ট না করে, Composer প্যাকেজ ফোল্ডার কপি করবে (ডুপ্লিকেট); ডেভেলপমেন্টে এটি এড়িয়ে চলুন।

২.৩.২ প্যাকেজ ইনস্টল ও ভার্সন রেজল্যুশন

bash
composer require devmaster/invoicelite:@dev
  • Path রিপোজিটরিতে ট্যাগ না থাকলে ভার্সন স্বয়ংক্রিয়ভাবে dev-main (অথবা বর্তমান ব্রাঞ্চ) হিসেবে রেজল্ভ হয়।
  • composer.lockversion ফিল্ড dev-main এবং install_path সিমলিংক পাথ হিসেবে সেভ হয়:
php
// vendor/composer/installed.php
[
  'name' => 'devmaster/invoicelite',
  'version' => 'dev-main',
  'install_path' => __DIR__ . '/../devmaster/invoicelite',
  'type' => 'library',
  'dev' => true,
],

২.৩.৩ সিমলিংক যাচাই ও রিফ্রেশ

bash
ls -l vendor/devmaster/invoicelite
# ... invoicelite -> ../../packages/DevMaster/InvoiceLite
  • সিমলিংক না হয়ে কপি হয়ে গেলে:
    composer remove devmaster/invoicelitecomposer clear-cache (প্রয়োজনে) → Path repo কনফিগ ঠিক আছে কিনা চেক → আবার composer require ....
  • composer dump-autoload বা composer install সিমলিংক ভাঙে না; এটি শুধু অটোলোড ম্যাপ রিফ্রেশ করে।

২.৩.৪ Kernel অ্যাক্সেসের জন্য গুরুত্বপূর্ণ চেক

  • সিমলিংক পাথ যদি ভুল হয়, InvoiceLiteServiceProvider লোড হবে না, ফলে হোস্ট Kernel রাউট/মিডলওয়্যার আপনার প্যাকেজকে দেখবে না।
  • হোস্ট অ্যাপ config/app.php ম্যানুয়ালি স্পর্শ করার দরকার নেই (Auto-Discovery যথেষ্ট)। তবে অটো-ডিসকভারি বন্ধ করতে চাইলে হোস্ট composer.json"extra": {"laravel": {"dont-discover": ["devmaster/invoicelite"]}} সেট করতে পারে—ডকুমেন্টে উল্লেখ রাখুন।

🔨 ২.৪ Complete Step-by-Step Implementation: InvoiceBuilder Package তৈরি

এখন আমরা সম্পূর্ণ হাতে-কলমে একটি InvoiceBuilder প্যাকেজ তৈরি করব। প্রতিটি ধাপ copy-paste করে অনুসরণ করুন।

২.৪.১ Project Initialization

Step 1: Laravel প্রজেক্ট তৈরি

bash
# নতুন Laravel অ্যাপ তৈরি
laravel new invoice-demo
cd invoice-demo

# Package ডিরেক্টরি structure তৈরি
mkdir -p packages/DevMaster/InvoiceBuilder/{src,config,database/migrations,routes,resources/views,tests}

Step 2: .gitkeep ফাইল যোগ (খালি ডিরেক্টরি track করতে)

bash
touch packages/DevMaster/InvoiceBuilder/src/.gitkeep
touch packages/DevMaster/InvoiceBuilder/tests/.gitkeep
touch packages/DevMaster/InvoiceBuilder/resources/views/.gitkeep

২.৪.২ Package composer.json Setup

Complete composer.json তৈরি:

bash
cat > packages/DevMaster/InvoiceBuilder/composer.json << 'EOF'
{
    "name": "devmaster/invoice-builder",
    "description": "Laravel Invoice Builder - Generate professional invoices with PDF support",
    "type": "library",
    "license": "MIT",
    "keywords": ["laravel", "invoice", "pdf", "mpdf", "billing"],
    "authors": [
        {
            "name": "DevMaster",
            "email": "dev@example.com"
        }
    ],
    "require": {
        "php": "^8.1",
        "illuminate/support": "^10.0|^11.0",
        "mpdf/mpdf": "^8.2"
    },
    "require-dev": {
        "orchestra/testbench": "^8.0|^9.0",
        "phpunit/phpunit": "^10.0"
    },
    "autoload": {
        "psr-4": {
            "DevMaster\\InvoiceBuilder\\": "src/"
        },
        "files": [
            "src/helpers.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "DevMaster\\InvoiceBuilder\\Tests\\": "tests/"
        }
    },
    "scripts": {
        "test": "vendor/bin/phpunit",
        "test-coverage": "vendor/bin/phpunit --coverage-html coverage"
    },
    "minimum-stability": "dev",
    "prefer-stable": true,
    "extra": {
        "laravel": {
            "providers": [
                "DevMaster\\InvoiceBuilder\\InvoiceBuilderServiceProvider"
            ],
            "aliases": {
                "InvoiceBuilder": "DevMaster\\InvoiceBuilder\\Facades\\InvoiceBuilder"
            }
        }
    }
}
EOF

২.৪.৩ Basic Service Provider Implementation

Minimal Service Provider তৈরি (Chapter 2 scope):

php
// packages/DevMaster/InvoiceBuilder/src/InvoiceBuilderServiceProvider.php
<?php

namespace DevMaster\InvoiceBuilder;

use Illuminate\Support\ServiceProvider;

class InvoiceBuilderServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register()
    {
        // Basic registration - will expand in Chapter 3
    }

    /**
     * Bootstrap any application services.
     */
    public function boot()
    {
        // Basic boot - will expand in Chapter 3
    }
}

২.৪.৪ Package Configuration

Configuration ফাইল তৈরি:

php
// packages/DevMaster/InvoiceBuilder/config/invoice-builder.php
<?php

return [
    /*
    |--------------------------------------------------------------------------
    | Default Invoice Settings
    |--------------------------------------------------------------------------
    | These are the default settings for invoice generation
    */
    'currency' => env('INVOICE_CURRENCY', 'USD'),
    'currency_symbol' => env('INVOICE_CURRENCY_SYMBOL', '$'),
    'locale' => env('INVOICE_LOCALE', 'en'),

    /*
    |--------------------------------------------------------------------------
    | Company Information (Default)
    |--------------------------------------------------------------------------
    | Default company information for invoices
    */
    'company' => [
        'name' => env('INVOICE_COMPANY_NAME', 'Your Company'),
        'address' => env('INVOICE_COMPANY_ADDRESS', ''),
        'email' => env('INVOICE_COMPANY_EMAIL', ''),
        'phone' => env('INVOICE_COMPANY_PHONE', ''),
        'website' => env('INVOICE_COMPANY_WEBSITE', ''),
    ],

    /*
    |--------------------------------------------------------------------------
    | PDF Generation Settings
    |--------------------------------------------------------------------------
    | Settings for PDF generation using mPDF
    */
    'pdf' => [
        'format' => 'A4',
        'orientation' => 'P', // P for Portrait, L for Landscape
        'margin' => [
            'top' => 15,
            'bottom' => 15,
            'left' => 15,
            'right' => 15,
        ],
        'font' => [
            'default' => 'dejavu-sans', // mPDF compatible font
            'size' => 10,
        ],
        'watermark' => [
            'enabled' => false,
            'text' => 'DRAFT',
            'opacity' => 0.1,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Database Settings
    |--------------------------------------------------------------------------
    | Database table configurations
    */
    'database' => [
        'table_prefix' => env('INVOICE_TABLE_PREFIX', 'invoice_'),
        'connection' => env('INVOICE_DB_CONNECTION', null), // null = default connection
    ],

    /*
    |--------------------------------------------------------------------------
    | Invoice Number Settings
    |--------------------------------------------------------------------------
    | Settings for automatic invoice number generation
    */
    'invoice_number' => [
        'prefix' => env('INVOICE_NUMBER_PREFIX', 'INV-'),
        'length' => 6, // Padding length for numbers (e.g., 000001)
        'start_from' => 1,
    ],

    /*
    |--------------------------------------------------------------------------
    | Tax Settings
    |--------------------------------------------------------------------------
    | Default tax configurations
    */
    'tax' => [
        'default_rate' => 0.0, // 0% default
        'label' => 'Tax',
        'included' => false, // Tax is added on top
    ],
];

২.৪.৫ Placeholder Files (Chapter 2 scope)

Create placeholder files for future chapters:

bash
# Create empty files that we'll build in later chapters
touch packages/DevMaster/InvoiceBuilder/src/Services/.gitkeep
touch packages/DevMaster/InvoiceBuilder/src/Facades/.gitkeep
touch packages/DevMaster/InvoiceBuilder/src/helpers.php
touch packages/DevMaster/InvoiceBuilder/routes/web.php

Basic Helper (empty for now):

php
// packages/DevMaster/InvoiceBuilder/src/helpers.php
<?php

// Helper functions will be added in later chapters

Basic Routes (placeholder):

php
// packages/DevMaster/InvoiceBuilder/routes/web.php
<?php

// Routes will be added in Chapter 6

২.৪.৯ Local Repository Setup

Main app এর composer.json modify করুন:

json
{
    "repositories": [
        {
            "type": "path",
            "url": "./packages/DevMaster/InvoiceBuilder",
            "options": {
                "symlink": true
            }
        }
    ]
}

২.৪.১০ Package Installation & Testing

Install করুন:

bash
# Main Laravel app directory তে
composer require devmaster/invoice-builder:@dev

# Auto-discovery run করুন
php artisan package:discover

# Configuration publish করুন
php artisan vendor:publish --tag=invoice-builder-config

Basic Testing (Chapter 2 scope):

bash
# Check if package is discovered
php artisan package:discover | grep InvoiceBuilder

# Check config is published
php artisan vendor:publish --tag=invoice-builder-config
ls config/invoice-builder.php

# Basic tinker test
php artisan tinker
>>> config('invoice-builder.currency')
>>> class_exists('DevMaster\InvoiceBuilder\InvoiceBuilderServiceProvider')

২.৫ অধ্যায় ২: এক নজরে কী শিখলেন

এই অধ্যায় শেষে আপনি যা হাতে-কলমে আয়ত্ত করেছেন:

  • প্যাকেজ ফোল্ডার স্থাপত্য - packages/DevMaster/InvoiceBuilder স্ট্রাকচার কেন ও কীভাবে সাজাতে হয়।
  • composer.json সেটআপ - PSR-4 অটোলোড, মেটাডাটা, এবং Laravel auto-discovery কনফিগারেশন।
  • লোকাল প্যাকেজ লিংকিং - Path repository + symlink ব্যবহার করে একই কোডে দ্রুত ডেভেলপমেন্ট।
  • মিনিমাল সার্ভিস প্রোভাইডার - register()boot() এর বেস স্ট্রাকচার তৈরি (পরের অধ্যায়ে বিস্তৃত হবে)।
  • পাব্লিশেবল কনফিগ ফাইল - ইউজারের অ্যাপে publish করা যায় এমন প্যাকেজ কনফিগ প্রস্তুত করা।
  • ভবিষ্যৎ অধ্যায়ের ভিত্তি - Facade, Service, Routes, Views ইত্যাদির জন্য placeholder ফাইল/স্ট্রাকচার।

দ্রুত যাচাই কমান্ড (Quick Check):

bash
# প্যাকেজ স্ট্রাকচার ঠিক আছে কি না
ls -la packages/DevMaster/InvoiceBuilder/

# প্যাকেজ auto-discovery কাজ করছে কি না
php artisan package:discover | grep InvoiceBuilder

# কনফিগ publish হয়েছে কি না
php artisan vendor:publish --tag=invoice-builder-config
cat config/invoice-builder.php

# vendor path symlink হয়েছে কি না
ls -l vendor/devmaster/invoice-builder

সৎ ক্রেডিট: লেখক AI, সম্পাদক আবুল হাসান