# Bagisto Package Development Guide
> Bagisto 2.x | Laravel 12 | PHP 8.3+

---

## 1. Package Directory Structure

```
packages/Lauren/{PackageName}/
├── composer.json
├── src/
│   ├── Providers/
│   │   └── {PackageName}ServiceProvider.php
│   ├── Config/
│   │   ├── menu.php          # Admin sidebar menu items
│   │   ├── acl.php           # Permissions (ACL)
│   │   └── system.php        # System configuration (Admin > Configure)
│   ├── Database/
│   │   └── Migrations/
│   ├── Http/
│   │   ├── Controllers/
│   │   │   ├── Admin/
│   │   │   └── Shop/
│   │   ├── Middleware/
│   │   └── Requests/         # Form Request validation
│   ├── Models/
│   ├── Repositories/         # (optional) Repository pattern
│   ├── DataGrids/            # Admin data tables
│   ├── Listeners/            # Event listeners
│   ├── Observers/            # Model observers
│   ├── Services/             # Business logic
│   ├── Mail/                 # Mailable classes
│   ├── Jobs/                 # Queued jobs
│   ├── Console/
│   │   └── Commands/         # Artisan commands
│   ├── Resources/
│   │   ├── lang/
│   │   │   ├── ar/
│   │   │   │   └── app.php
│   │   │   └── en/
│   │   │       └── app.php
│   │   └── views/
│   │       ├── admin/
│   │       ├── admin-override/   # Override existing admin views
│   │       └── shop/
│   └── Routes/
│       ├── admin.php
│       └── shop.php
```

---

## 2. composer.json (Package Level)

```json
{
    "name": "lauren/{package-name}",
    "description": "Package description",
    "version": "1.0.0",
    "type": "bagisto-package",
    "license": "MIT",
    "authors": [
        {
            "name": "Lauren Bakery",
            "email": "dev@laurenbakery.sa"
        }
    ],
    "require": {
        "php": "^8.3"
    },
    "autoload": {
        "psr-4": {
            "Lauren\\{PackageName}\\": "src/"
        }
    },
    "extra": {
        "laravel": {
            "providers": [
                "Lauren\\{PackageName}\\Providers\\{PackageName}ServiceProvider"
            ]
        }
    }
}
```

---

## 3. Registration Steps (Root composer.json)

Add PSR-4 mapping in root `composer.json`:

```json
"autoload": {
    "psr-4": {
        "Lauren\\{PackageName}\\": "packages/Lauren/{PackageName}/src"
    }
}
```

Then run:
```bash
composer dump-autoload
php artisan package:discover
php artisan migrate
php artisan optimize:clear
```

---

## 4. Service Provider (The Heart of the Package)

```php
<?php

namespace Lauren\{PackageName}\Providers;

use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;

class {PackageName}ServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Merge config into the core config arrays
        $this->mergeConfigFrom(__DIR__ . '/../Config/menu.php', 'menu.admin');
        $this->mergeConfigFrom(__DIR__ . '/../Config/acl.php', 'acl');
        $this->mergeConfigFrom(__DIR__ . '/../Config/system.php', 'core');

        // Register singletons
        $this->app->singleton(\Lauren\{PackageName}\Services\MyService::class);

        // Override existing bindings (e.g., replace a DataGrid)
        // $this->app->bind(
        //     \Webkul\Admin\DataGrids\Sales\OrderDataGrid::class,
        //     \Lauren\{PackageName}\DataGrids\OrderDataGrid::class
        // );
    }

    public function boot(): void
    {
        // Migrations
        $this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');

        // Translations (namespace: '{package-name}')
        $this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', '{package-name}');

        // Views (namespace: '{package-name}')
        $this->loadViewsFrom(__DIR__ . '/../Resources/views', '{package-name}');

        // Override existing views (prepend to existing namespace)
        // $this->app['view']->prependNamespace('admin', __DIR__ . '/../Resources/views/admin-override');
        // $this->app['view']->prependNamespace('shop', __DIR__ . '/../Resources/views/shop');

        // Admin routes
        Route::middleware(['web', 'admin'])
            ->prefix(config('app.admin_url'))
            ->group(__DIR__ . '/../Routes/admin.php');

        // Shop routes
        Route::middleware(['web', 'shop'])
            ->group(__DIR__ . '/../Routes/shop.php');

        // Event listeners
        $this->registerEvents();
    }

    protected function registerEvents(): void
    {
        // Hook into checkout
        Event::listen('checkout.order.save.after', function ($order) {
            // Custom logic after order creation
        });

        // Inject UI into admin order view
        Event::listen('bagisto.admin.sales.order.view.after', function ($order) {
            // echo view('{package-name}::admin.custom-section', compact('order'))->render();
        });

        // Inject UI into shop checkout
        Event::listen('bagisto.shop.checkout.onepage.address.before', function () {
            // echo view('{package-name}::shop.custom-component')->render();
        });
    }
}
```

---

## 5. Routes

### Admin Routes (`src/Routes/admin.php`)

```php
<?php

use Illuminate\Support\Facades\Route;
use Lauren\{PackageName}\Http\Controllers\Admin\MyController;

Route::group(['prefix' => '{package-prefix}', 'middleware' => ['admin']], function () {
    Route::controller(MyController::class)->group(function () {
        Route::get('/',       'index')->name('admin.{package}.index');
        Route::get('create',  'create')->name('admin.{package}.create');
        Route::post('store',  'store')->name('admin.{package}.store');
        Route::get('{id}',    'edit')->name('admin.{package}.edit');
        Route::put('{id}',    'update')->name('admin.{package}.update');
        Route::delete('{id}', 'destroy')->name('admin.{package}.destroy');
    });
});
```

### Shop Routes (`src/Routes/shop.php`)

```php
<?php

use Illuminate\Support\Facades\Route;
use Lauren\{PackageName}\Http\Controllers\Shop\FrontController;

Route::group(['prefix' => '{package-prefix}'], function () {
    Route::controller(FrontController::class)->group(function () {
        Route::get('/', 'index')->name('shop.{package}.index');
        Route::post('/process', 'process')->name('shop.{package}.process');
    });
});
```

---

## 6. Admin Menu (`src/Config/menu.php`)

```php
<?php

return [
    // Top-level menu item (shows in admin sidebar)
    [
        'key'   => '{package}',
        'name'  => '{package-name}::app.admin.title',
        'route' => 'admin.{package}.index',
        'sort'  => 8,
        'icon'  => 'icon-settings',   // Use Bagisto icon classes
    ],
    // Sub-menu item
    [
        'key'   => '{package}.items',
        'name'  => '{package-name}::app.admin.items.title',
        'route' => 'admin.{package}.items.index',
        'sort'  => 1,
        'icon'  => '',
    ],
];
```

**Available Icons:**
`icon-dashboard`, `icon-sales`, `icon-store`, `icon-catalog`, `icon-customers`,
`icon-marketing`, `icon-cms`, `icon-settings`, `icon-configuration`, `icon-ship`,
`icon-cancel`, `icon-export`, `icon-view`, `icon-edit`, `icon-delete`

---

## 7. ACL / Permissions (`src/Config/acl.php`)

```php
<?php

return [
    [
        'key'   => '{package}',
        'name'  => '{package-name}::app.admin.acl.title',
        'route' => 'admin.{package}.index',
        'sort'  => 8,
    ],
    [
        'key'   => '{package}.items',
        'name'  => '{package-name}::app.admin.acl.items',
        'route' => 'admin.{package}.items.index',
        'sort'  => 1,
    ],
    [
        'key'   => '{package}.items.create',
        'name'  => '{package-name}::app.admin.acl.create',
        'route' => 'admin.{package}.items.create',
        'sort'  => 1,
    ],
    [
        'key'   => '{package}.items.edit',
        'name'  => '{package-name}::app.admin.acl.edit',
        'route' => 'admin.{package}.items.edit',
        'sort'  => 2,
    ],
    [
        'key'   => '{package}.items.delete',
        'name'  => '{package-name}::app.admin.acl.delete',
        'route' => 'admin.{package}.items.delete',
        'sort'  => 3,
    ],
];
```

**Usage in code:**
```php
if (bouncer()->hasPermission('{package}.items.create')) {
    // authorized
}
```

---

## 8. System Configuration (`src/Config/system.php`)

Settings that appear in **Admin > Configure**.

```php
<?php

return [
    // Root section (tab in sidebar)
    [
        'key'  => '{package}_settings',
        'name' => '{package-name}::app.admin.config.title',
        'info' => '{package-name}::app.admin.config.info',
        'sort' => 9,
    ],

    // Sub-tab
    [
        'key'  => '{package}_settings.general',
        'name' => '{package-name}::app.admin.config.general.title',
        'info' => '{package-name}::app.admin.config.general.info',
        'icon' => 'settings/store.svg',
        'sort' => 1,
    ],

    // Field group
    [
        'key'    => '{package}_settings.general.options',
        'name'   => '{package-name}::app.admin.config.general.options.title',
        'info'   => '{package-name}::app.admin.config.general.options.info',
        'sort'   => 1,
        'fields' => [
            [
                'name'    => 'enabled',
                'title'   => '{package-name}::app.admin.config.general.options.enabled',
                'type'    => 'boolean',
                'default' => 1,
            ],
            [
                'name'    => 'api_key',
                'title'   => '{package-name}::app.admin.config.general.options.api-key',
                'type'    => 'text',
            ],
            [
                'name'    => 'mode',
                'title'   => '{package-name}::app.admin.config.general.options.mode',
                'type'    => 'select',
                'default' => 'sandbox',
                'options' => [
                    ['title' => 'Sandbox', 'value' => 'sandbox'],
                    ['title' => 'Live',    'value' => 'live'],
                ],
            ],
        ],
    ],
];
```

**Field types:** `text`, `password`, `number`, `boolean`, `select`, `multiselect`, `textarea`, `image`, `file`

**Accessing settings:**
```php
$value = core()->getConfigData('{package}_settings.general.options.enabled');
```

---

## 9. DataGrid (Admin Data Table)

```php
<?php

namespace Lauren\{PackageName}\DataGrids;

use Illuminate\Support\Facades\DB;
use Webkul\DataGrid\DataGrid;

class ItemDataGrid extends DataGrid
{
    protected $primaryColumn = 'id';

    public function prepareQueryBuilder()
    {
        $queryBuilder = DB::table('my_items')
            ->select('id', 'name', 'status', 'created_at');

        $this->addFilter('id', 'my_items.id');

        return $queryBuilder;
    }

    public function prepareColumns()
    {
        $this->addColumn([
            'index'      => 'id',
            'label'      => trans('{package-name}::app.admin.datagrid.id'),
            'type'       => 'integer',
            'searchable' => false,
            'filterable' => true,
            'sortable'   => true,
        ]);

        $this->addColumn([
            'index'      => 'name',
            'label'      => trans('{package-name}::app.admin.datagrid.name'),
            'type'       => 'string',
            'searchable' => true,
            'filterable' => true,
            'sortable'   => true,
        ]);

        $this->addColumn([
            'index'           => 'status',
            'label'           => trans('{package-name}::app.admin.datagrid.status'),
            'type'            => 'string',
            'filterable'      => true,
            'filterable_type' => 'dropdown',
            'filterable_options' => [
                ['label' => 'Active',   'value' => 'active'],
                ['label' => 'Inactive', 'value' => 'inactive'],
            ],
            'sortable'  => true,
            'closure'   => function ($row) {
                $class = $row->status === 'active' ? 'label-active' : 'label-canceled';
                return '<p class="' . $class . '">' . ucfirst($row->status) . '</p>';
            },
        ]);

        $this->addColumn([
            'index'           => 'created_at',
            'label'           => trans('{package-name}::app.admin.datagrid.date'),
            'type'            => 'date',
            'filterable'      => true,
            'filterable_type' => 'date_range',
            'sortable'        => true,
        ]);
    }

    public function prepareActions()
    {
        if (bouncer()->hasPermission('{package}.items.edit')) {
            $this->addAction([
                'icon'   => 'icon-edit',
                'title'  => 'Edit',
                'method' => 'GET',
                'url'    => fn ($row) => route('admin.{package}.items.edit', $row->id),
            ]);
        }

        if (bouncer()->hasPermission('{package}.items.delete')) {
            $this->addAction([
                'icon'   => 'icon-delete',
                'title'  => 'Delete',
                'method' => 'DELETE',
                'url'    => fn ($row) => route('admin.{package}.items.destroy', $row->id),
            ]);
        }
    }

    public function prepareMassActions()
    {
        $this->addMassAction([
            'title'  => 'Delete',
            'method' => 'POST',
            'url'    => route('admin.{package}.items.mass-delete'),
        ]);
    }
}
```

---

## 10. Models

```php
<?php

namespace Lauren\{PackageName}\Models;

use Illuminate\Database\Eloquent\Model;

class Item extends Model
{
    protected $table = '{package}_items';

    protected $fillable = [
        'name',
        'description',
        'status',
        'options',
        'price',
        'is_active',
    ];

    protected $casts = [
        'options'   => 'array',
        'price'     => 'decimal:4',
        'is_active' => 'boolean',
    ];

    // Scopes
    public function scopeActive($query)
    {
        return $query->where('is_active', true);
    }
}
```

---

## 11. Migrations

```php
<?php

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

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('{package}_items', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->text('description')->nullable();
            $table->string('status')->default('active');
            $table->json('options')->nullable();
            $table->decimal('price', 12, 4)->default(0);
            $table->boolean('is_active')->default(true);
            $table->timestamps();

            $table->index(['status', 'is_active']);
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('{package}_items');
    }
};
```

---

## 12. Translations (`src/Resources/lang/ar/app.php`)

```php
<?php

return [
    'admin' => [
        'title' => 'اسم الحزمة',
        'items' => [
            'title'  => 'العناصر',
            'create' => 'إنشاء عنصر',
            'edit'   => 'تعديل عنصر',
        ],
        'datagrid' => [
            'id'     => 'المعرف',
            'name'   => 'الاسم',
            'status' => 'الحالة',
            'date'   => 'التاريخ',
        ],
        'acl' => [
            'title'  => 'اسم الحزمة',
            'items'  => 'العناصر',
            'create' => 'إنشاء',
            'edit'   => 'تعديل',
            'delete' => 'حذف',
        ],
        'config' => [
            'title' => 'إعدادات الحزمة',
            'info'  => 'إدارة إعدادات الحزمة',
        ],
    ],
    'shop' => [
        // Frontend translations
    ],
];
```

**Usage:**
```php
trans('{package-name}::app.admin.title')
```

---

## 13. Key Integration Points (Events)

### Checkout Events
| Event | Fires When |
|-------|-----------|
| `checkout.order.save.before` | Before order is saved |
| `checkout.order.save.after` | After order is saved |
| `checkout.order.orderitem.save.before` | Before each order item |
| `checkout.order.orderitem.save.after` | After each order item |

### Order Status Events
| Event | Fires When |
|-------|-----------|
| `sales.order.cancel.after` | Order canceled |
| `sales.order.comment.create.after` | Comment added |
| `sales.invoice.save.after` | Invoice created |
| `sales.shipment.save.after` | Shipment created |
| `sales.refund.save.after` | Refund created |

### Customer Events
| Event | Fires When |
|-------|-----------|
| `customer.registration.after` | Customer registered |
| `customer.password.update.after` | Password changed |
| `customer.subscription.after` | Newsletter subscribed |

### UI Injection Events (View Composers)
| Event | Where It Renders |
|-------|-----------------|
| `bagisto.admin.sales.order.view.after` | Admin order detail page |
| `bagisto.admin.sales.order.page_action.after` | Admin order action buttons |
| `bagisto.shop.checkout.onepage.address.before` | Shop checkout before address |
| `bagisto.shop.layout.body.after` | Shop body end |
| `bagisto.admin.layout.head.after` | Admin head section |

---

## 14. Overriding Existing Classes (Binding)

```php
// In register() method of ServiceProvider

// Override a DataGrid
$this->app->bind(
    \Webkul\Admin\DataGrids\Sales\OrderDataGrid::class,
    \Lauren\{PackageName}\DataGrids\OrderDataGrid::class
);

// Override a Repository
$this->app->bind(
    \Webkul\Sales\Repositories\OrderRepository::class,
    \Lauren\{PackageName}\Repositories\CustomOrderRepository::class
);
```

---

## 15. Overriding Views

```php
// In boot() method - prepend views to existing namespace
// Files in admin-override/ with same path will take priority

$this->app['view']->prependNamespace('admin', __DIR__ . '/../Resources/views/admin-override');
$this->app['view']->prependNamespace('shop', __DIR__ . '/../Resources/views/shop-override');
```

**Example:** To override `admin::sales.orders.index`, create:
```
src/Resources/views/admin-override/sales/orders/index.blade.php
```

---

## 16. Quick Reference: New Package Checklist

1. Create directory structure under `packages/Lauren/{PackageName}/`
2. Create `composer.json` with PSR-4 autoload
3. Create `ServiceProvider` in `src/Providers/`
4. Add PSR-4 mapping to root `composer.json`
5. Create `src/Config/menu.php` for admin menu
6. Create `src/Config/acl.php` for permissions
7. Create `src/Config/system.php` for settings (if needed)
8. Create migrations in `src/Database/Migrations/`
9. Create models in `src/Models/`
10. Create routes in `src/Routes/`
11. Create controllers in `src/Http/Controllers/`
12. Create views in `src/Resources/views/`
13. Create translations in `src/Resources/lang/`
14. Create DataGrids in `src/DataGrids/` (if needed)
15. Run: `composer dump-autoload && php artisan package:discover && php artisan migrate && php artisan optimize:clear`

---

## 17. Existing Custom Packages (Lauren)

| Package | Purpose | Namespace |
|---------|---------|-----------|
| DeliveryScheduler | Delivery/pickup scheduling with zones, time slots, off days | `Lauren\DeliveryScheduler` |
| Paymob | Paymob payment gateway integration | `Lauren\Paymob` |
| MsegatOtp | Msegat SMS OTP verification | `Lauren\MsegatOtp` |
