Skip to main content

Overview

Policies are Laravel classes that define authorization logic for models. Unleash Commerce Core includes built-in policies for models like Order, Customer, and more. You can replace these policies with custom implementations to control who can perform actions on your models.

Extending a Core Policy

If you only need to tweak behavior, extend the default core policy and override the specific methods you want to change:
namespace App\Policies;

use App\Models\User;
use Esign\UnleashCommerce\Core\Models\Order;
use Esign\UnleashCommerce\Core\Policies\OrderPolicy as CoreOrderPolicy;

class OrderPolicy extends CoreOrderPolicy
{
    public function update(User $user, Order $order): bool|null
    {
        // Block updates for completed orders, then fall back to core logic
        if ($order->status === 'completed') {
            return false;
        }

        return parent::update($user, $order);
    }
}

Policy Return Values

Policies should return one of three values:
  • true - The action is authorized
  • false - The action is explicitly denied (even for super admins)
  • null - Let the default authorization gate handle the decision

Replacing a Core Policy

Register your custom policy in a service provider:
<?php

namespace App\Providers;

use App\Policies\OrderPolicy;
use Esign\UnleashCommerce\Core\Contracts\Models\Order as OrderContract;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Support\ServiceProvider;

class UnleashCommerceServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->registerPolicies();
    }

    protected function registerPolicies(): void
    {
        $gate = $this->app->make(GateContract::class);

        $gate->policy(
            OrderContract::class,
            OrderPolicy::class
        );
    }
}

Using Policies in Your Code

Check authorization in controllers:
namespace App\Http\Controllers;

use Esign\UnleashCommerce\Core\Models\Order;
use Illuminate\Http\JsonResponse;

class OrderController extends Controller
{
    public function show(Order $order): JsonResponse
    {
        $this->authorize('view', $order);
        
        return response()->json($order);
    }

    public function update(Order $order): JsonResponse
    {
        $this->authorize('update', $order);
        
        // Update logic
        $order->update($this->validated());
        
        return response()->json($order);
    }
}
Check authorization in views:
@can('update', $order)
    <a href="{{ route('orders.edit', $order) }}">Edit Order</a>
@endcan

@cannot('delete', $order)
    <span class="text-gray-500">Cannot delete</span>
@endcannot

Testing Policies

Test your custom policies to ensure authorization works correctly:
<?php

namespace Tests\Feature\Policies;

use App\Models\User;
use Esign\UnleashCommerce\Core\Models\Order;
use Tests\TestCase;

class OrderPolicyTest extends TestCase
{
    public function test_user_can_view_their_own_orders()
    {
        // Arrange
        $user = User::factory()->create();
        $order = Order::factory()->for($user->customer)->create();

        // Act
        $can = $user->can('view', $order);

        // Assert
        $this->assertTrue($can);
    }

    public function test_user_cannot_view_other_customers_orders()
    {
        // Arrange
        $userA = User::factory()->create();
        $userB = User::factory()->create();
        $orderByUserB = Order::factory()->for($userB->customer)->create();

        // Act
        $can = $userA->can('view', $orderByUserB);

        // Assert
        $this->assertFalse($can);
    }

    public function test_user_can_only_update_draft_orders()
    {
        // Arrange
        $user = User::factory()->create();
        $draftOrder = Order::factory()
            ->for($user->customer)
            ->state(['status' => 'draft'])
            ->create();
        $completedOrder = Order::factory()
            ->for($user->customer)
            ->state(['status' => 'completed'])
            ->create();

        // Act
        $canUpdateDraft = $user->can('update', $draftOrder);
        $canUpdateCompleted = $user->can('update', $completedOrder);

        // Assert
        $this->assertTrue($canUpdateDraft);
        $this->assertFalse($canUpdateCompleted);
    }
}

Available Core Policies

Refer to the Policies Reference for a complete list of available policies you can replace.