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.lock এ version ফিল্ড 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/invoicelite → composer 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