> ## Documentation Index
> Fetch the complete documentation index at: https://docs.laravelshopper.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Customers

> Complete reference for the User model, addresses, and customer management in Shopper.

Customers are users who have the customer role and can place orders in your store. Shopper extends your Laravel User model with e-commerce specific fields and relationships.

## User Model Integration

Shopper doesn't create a separate Customer model. Instead, it extends your existing `App\Models\User` model with the `InteractsWithShopper` trait and `ShopperUser` contract.

```php theme={null}
namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Shopper\Models\Contracts\ShopperUser;
use Shopper\Traits\InteractsWithShopper;

class User extends Authenticatable implements ShopperUser
{
    use InteractsWithShopper;
}
```

The `InteractsWithShopper` trait provides:

* `HasRoles` (Spatie Permission) for role-based access control
* `HasDiscounts` for customer-specific discount eligibility
* `HasProfilePhoto` for avatar management

## Database Schema

### User Table Modifications

Shopper modifies the standard Laravel `users` table:

| Column              | Type      | Nullable | Default    | Description                         |
| ------------------- | --------- | -------- | ---------- | ----------------------------------- |
| `id`                | bigint    | no       | auto       | Primary key                         |
| `first_name`        | string    | yes      | null       | First name                          |
| `last_name`         | string    | no       | -          | Last name                           |
| `email`             | string    | no       | -          | Email address (unique)              |
| `password`          | string    | yes      | null       | Password (nullable for social auth) |
| `email_verified_at` | timestamp | yes      | null       | Email verification date             |
| `gender`            | string    | yes      | null       | Gender enum value                   |
| `phone_number`      | string    | yes      | null       | Phone number                        |
| `birth_date`        | date      | yes      | null       | Birth date                          |
| `avatar_type`       | string    | no       | avatar\_ui | Avatar source type                  |
| `avatar_location`   | string    | yes      | null       | Avatar file path                    |
| `timezone`          | string    | yes      | null       | User timezone                       |
| `opt_in`            | boolean   | no       | false      | Marketing email consent             |
| `last_login_at`     | timestamp | yes      | null       | Last login timestamp                |
| `last_login_ip`     | string    | yes      | null       | Last login IP address               |

### Address Table

| Column                | Type    | Nullable | Default | Description              |
| --------------------- | ------- | -------- | ------- | ------------------------ |
| `id`                  | bigint  | no       | auto    | Primary key              |
| `first_name`          | string  | no       | -       | First name               |
| `last_name`           | string  | no       | -       | Last name                |
| `company_name`        | string  | yes      | null    | Company name             |
| `street_address`      | string  | no       | -       | Street address           |
| `street_address_plus` | string  | yes      | null    | Additional address line  |
| `postal_code`         | string  | no       | -       | Postal/ZIP code          |
| `city`                | string  | no       | -       | City                     |
| `state`               | string  | yes      | null    | State/province/region    |
| `phone_number`        | string  | yes      | null    | Phone number             |
| `shipping_default`    | boolean | no       | false   | Default shipping address |
| `billing_default`     | boolean | no       | false   | Default billing address  |
| `type`                | string  | yes      | null    | Address type enum        |
| `country_id`          | bigint  | yes      | null    | FK to countries          |
| `user_id`             | bigint  | no       | -       | FK to users              |

## Gender Type

The `GenderType` enum defines available gender options:

```php theme={null}
use Shopper\Core\Enum\GenderType;

GenderType::Male    // male
GenderType::Female  // female
```

## Address Type

The `AddressType` enum defines address categories:

```php theme={null}
use Shopper\Core\Enum\AddressType;

AddressType::Billing   // billing
AddressType::Shipping  // shipping
```

## Relationships

### Orders

```php theme={null}
// Get all customer orders
$customer->orders; // Collection of Order models

// Get recent orders
$customer->orders()
    ->latest()
    ->take(5)
    ->get();
```

### Addresses

```php theme={null}
// Get all customer addresses
$customer->addresses; // Collection of Address models

// Get default shipping address
$customer->addresses()
    ->where('shipping_default', true)
    ->first();

// Get default billing address
$customer->addresses()
    ->where('billing_default', true)
    ->first();
```

## Query Scopes

```php theme={null}
use App\Models\User;

// Get only customers (users with 'user' role)
User::query()->customers()->get();

// Get administrators (admin or manager roles)
User::query()->administrators()->get();
```

## Role Checking

```php theme={null}
// Check if user is an admin
$user->isAdmin();

// Check if user is a manager
$user->isManager();

// Check if email is verified
$user->isVerified();
```

## Computed Attributes

```php theme={null}
// Get full name
$customer->full_name; // "John Doe"

// Get formatted birth date
$customer->birth_date_formatted; // "15, January 1990"

// Get roles as comma-separated string
$customer->roles_label; // "User, VIP"

// Get profile picture URL
$customer->picture; // Avatar URL (ui-avatars or uploaded)
```

## Address Model

The model used is `Shopper\Core\Models\Address`. It implements `Shopper\Core\Models\Contracts\Address` and is configurable via `config/shopper/models.php`.

### Extending the Address Model

```php theme={null}
namespace App\Models;

use Shopper\Core\Models\Address as BaseAddress;

class Address extends BaseAddress
{
    // Add your customizations here
}
```

Update `config/shopper/models.php`:

```php theme={null}
return [
    'address' => \App\Models\Address::class,
];
```

### Address Relationships

Each address belongs to a user and optionally to a country:

```php theme={null}
$address->user;    // The customer who owns this address
$address->country; // The Country model (nullable)
```

### Address Methods

The Address model provides helpers for checking default status:

```php theme={null}
$address->isShippingDefault(); // true if this is the default shipping address
$address->isBillingDefault();  // true if this is the default billing address
$address->full_name;           // "John Doe" (computed from first_name + last_name)
```

## Creating Customers

### Basic Customer

```php theme={null}
use App\Models\User;
use Shopper\Core\Enum\GenderType;
use Illuminate\Support\Facades\Hash;

$customer = User::query()->create([
    'first_name' => 'John',
    'last_name' => 'Doe',
    'email' => 'john@example.com',
    'password' => Hash::make('password'),
    'gender' => GenderType::Male,
    'email_verified_at' => now(),
]);

// Assign customer role
$customer->assignRole(config('shopper.admin.roles.user'));
```

### Customer with Address

```php theme={null}
use Shopper\Core\Models\Address;
use Shopper\Core\Enum\AddressType;

$customer = User::query()->create([
    'first_name' => 'Jane',
    'last_name' => 'Doe',
    'email' => 'jane@example.com',
    'gender' => GenderType::Female,
    'phone_number' => '+1234567890',
    'opt_in' => true,
]);

$customer->assignRole(config('shopper.admin.roles.user'));

// Add shipping address
$customer->addresses()->create([
    'first_name' => 'Jane',
    'last_name' => 'Doe',
    'street_address' => '123 Main Street',
    'city' => 'New York',
    'postal_code' => '10001',
    'country_id' => $countryId,
    'type' => AddressType::Shipping,
    'shipping_default' => true,
]);
```

## Retrieving Customers

```php theme={null}
use App\Models\User;

// Get all customers
$customers = User::query()
    ->customers()
    ->latest()
    ->get();

// Search customers
$customers = User::query()
    ->customers()
    ->where(function ($query) use ($search) {
        $query->where('first_name', 'like', "%{$search}%")
            ->orWhere('last_name', 'like', "%{$search}%")
            ->orWhere('email', 'like', "%{$search}%");
    })
    ->get();

// Get customer with addresses and orders
$customer = User::query()
    ->with(['addresses', 'orders'])
    ->findOrFail($id);

// Get customers who opted in for marketing
$marketingList = User::query()
    ->customers()
    ->where('opt_in', true)
    ->get();

// Get verified customers only
$verified = User::query()
    ->customers()
    ->whereNotNull('email_verified_at')
    ->get();
```

## Managing Addresses

```php theme={null}
use Shopper\Core\Models\Address;

// Create address for customer
$address = Address::query()->create([
    'user_id' => $customer->id,
    'first_name' => 'John',
    'last_name' => 'Doe',
    'street_address' => '456 Oak Avenue',
    'city' => 'Los Angeles',
    'postal_code' => '90001',
    'country_id' => $countryId,
    'type' => AddressType::Billing,
    'billing_default' => true,
]);

// Update default address
Address::query()
    ->where('user_id', $customer->id)
    ->where('type', AddressType::Shipping)
    ->update(['shipping_default' => false]);

$newDefault = Address::query()->find($addressId);
$newDefault->update(['shipping_default' => true]);
```

## Observer Behavior

When a user is deleted, the `InteractsWithShopper` trait automatically:

* Detaches all roles
* Deletes all addresses

```php theme={null}
// When deleting a user:
$customer->delete();
// Roles are detached and addresses are deleted automatically
```

## Role Configuration

Roles are defined in `config/shopper/admin.php`:

```php theme={null}
return [
    'roles' => [
        'admin' => 'administrator',
        'manager' => 'manager',
        'user' => 'user',
    ],
];
```

## Permissions

The admin panel generates five permissions for customer management:

| Permission         | Description                    |
| ------------------ | ------------------------------ |
| `browse_customers` | View the customers list        |
| `read_customers`   | View a single customer profile |
| `add_customers`    | Create new customers           |
| `edit_customers`   | Edit customer profiles         |
| `delete_customers` | Delete or anonymize customers  |

## Components

To customize the admin UI for customer management:

```bash theme={null}
php artisan shopper:component:publish customer
```

Creates `config/shopper/components/customer.php`:

```php theme={null}
use Shopper\Livewire;
use Shopper\Livewire\Components;

return [
    'pages' => [
        'customer-index' => Livewire\Pages\Customers\Index::class,
        'customer-create' => Livewire\Pages\Customers\Create::class,
        'customer-show' => Livewire\Pages\Customers\Show::class,
    ],
    'components' => [
        'customers.addresses' => Components\Customers\Addresses::class,
        'customers.orders' => Components\Customers\Orders::class,
        'customers.profile' => Components\Customers\Profile::class,
    ],
];
```

## Storefront Example

```php theme={null}
namespace App\Http\Controllers;

use App\Models\User;
use Shopper\Core\Models\Address;
use Shopper\Core\Enum\GenderType;
use Shopper\Core\Enum\AddressType;
use Illuminate\Support\Facades\Hash;

class AccountController extends Controller
{
    public function show()
    {
        $customer = auth()->user();

        return view('account.show', [
            'customer' => $customer,
            'orders' => $customer->orders()->latest()->paginate(10),
            'addresses' => $customer->addresses,
        ]);
    }

    public function updateProfile(Request $request)
    {
        $validated = $request->validate([
            'first_name' => 'required|string|max:255',
            'last_name' => 'required|string|max:255',
            'phone_number' => 'nullable|string|max:20',
            'birth_date' => 'nullable|date',
            'opt_in' => 'boolean',
        ]);

        auth()->user()->update($validated);

        return back()->with('success', 'Profile updated successfully');
    }

    public function storeAddress(Request $request)
    {
        $validated = $request->validate([
            'first_name' => 'required|string',
            'last_name' => 'required|string',
            'street_address' => 'required|string',
            'city' => 'required|string',
            'postal_code' => 'required|string',
            'country_id' => 'required|exists:countries,id',
            'type' => 'required|in:billing,shipping',
        ]);

        auth()->user()->addresses()->create([
            ...$validated,
            'type' => AddressType::from($validated['type']),
        ]);

        return back()->with('success', 'Address added successfully');
    }
}
```

## Use Cases

| Scope              | Description                          |
| ------------------ | ------------------------------------ |
| `customers()`      | Users with 'user' role               |
| `administrators()` | Users with 'admin' or 'manager' role |

| Method         | Description                    |
| -------------- | ------------------------------ |
| `isAdmin()`    | Check if user has admin role   |
| `isManager()`  | Check if user has manager role |
| `isVerified()` | Check if email is verified     |
