PHPackages                             whilesmart/eloquent-owner-access - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. whilesmart/eloquent-owner-access

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

whilesmart/eloquent-owner-access
================================

Pluggable polymorphic owner authorization for Laravel packages with owner\_type/owner\_id columns. Lets host apps decide who can see and modify owner-scoped records without coupling the underlying packages to a specific tenancy model.

044PHP

Since Apr 25Pushed 1mo agoCompare

[ Source](https://github.com/whilesmartphp/eloquent-owner-access)[ Packagist](https://packagist.org/packages/whilesmart/eloquent-owner-access)[ RSS](/packages/whilesmart-eloquent-owner-access/feed)WikiDiscussions dev Synced 1w ago

READMEChangelogDependenciesVersions (1)Used By (0)

eloquent-owner-access
=====================

[](#eloquent-owner-access)

Pluggable polymorphic owner authorization for Laravel packages with `owner_type` / `owner_id` columns.

Domain packages (accounts, expenses, payments, invoices, products, customers, ...) expose CRUD endpoints scoped by a polymorphic owner. They should not have to know how the host app decides who owns what. This package gives them a single contract to consult and a default that preserves existing "trust the client" behavior, so adoption is incremental and safe.

Concept
-------

[](#concept)

One contract:

```
interface OwnerAuthorizer
{
    public function authorize(?Authenticatable $user, string $ownerType, mixed $ownerId): bool;

    public function scope(
        Builder $query,
        ?Authenticatable $user,
        string $ownerTypeColumn = 'owner_type',
        string $ownerIdColumn = 'owner_id',
    ): Builder;
}
```

`authorize()` is per-record (called from `FormRequest::authorize()` for store/update and from controllers for show/destroy). `scope()` constrains index queries.

The default binding is `AllowAllAuthorizer` (everything goes through). Hosts that want strict tenancy bind their own implementation.

Installation
------------

[](#installation)

```
composer require whilesmart/eloquent-owner-access
```

The service provider auto-registers via Laravel package discovery and `bindIf`s the default authorizer.

Usage in a package
------------------

[](#usage-in-a-package)

In a `StoreXRequest`:

```
use Whilesmart\OwnerAccess\Concerns\AuthorizesOwnerRequest;

class StoreAccountRequest extends FormRequest
{
    use AuthorizesOwnerRequest;

    public function authorize(): bool
    {
        return $this->authorizeOwnerInRequest();
    }

    public function rules(): array { /* ... */ }
}
```

In an `UpdateXRequest`:

```
public function authorize(): bool
{
    return $this->authorizeOwnerOfBoundModel('account');
}
```

In the controller:

```
use Whilesmart\OwnerAccess\Concerns\AuthorizesOwnerController;

class AccountController extends Controller
{
    use AuthorizesOwnerController;

    public function index(Request $request)
    {
        $query = Account::query();
        $query = $this->scopeAccessibleOwners($query, $request->user());
        // ...
    }

    public function show(Account $account, Request $request)
    {
        $this->authorizeAccessTo($account, $request->user());
        // ...
    }

    public function destroy(Account $account, Request $request)
    {
        $this->authorizeAccessTo($account, $request->user());
        $account->delete();
        // ...
    }
}
```

Update is already covered by `UpdateXRequest::authorize()`, so no controller change is needed there.

Usage in a host app
-------------------

[](#usage-in-a-host-app)

By default everything is allowed. To enforce tenancy, write an authorizer that consults your tenancy model and bind it:

```
namespace App\Authorization;

use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Builder;
use Whilesmart\OwnerAccess\Contracts\OwnerAuthorizer;

class WorkspaceMemberAuthorizer implements OwnerAuthorizer
{
    public function authorize(?Authenticatable $user, string $ownerType, mixed $ownerId): bool
    {
        if ($user === null || $ownerType !== \App\Models\Workspace::class) {
            return false;
        }

        return $user->workspaces()->whereKey($ownerId)->exists();
    }

    public function scope(Builder $query, ?Authenticatable $user, string $ownerTypeColumn = 'owner_type', string $ownerIdColumn = 'owner_id'): Builder
    {
        if ($user === null) {
            return $query->whereRaw('0 = 1');
        }

        return $query->where($ownerTypeColumn, \App\Models\Workspace::class)
            ->whereIn($ownerIdColumn, $user->workspaces()->pluck('workspaces.id'));
    }
}
```

Then in `AppServiceProvider::register()`:

```
$this->app->bind(
    \Whilesmart\OwnerAccess\Contracts\OwnerAuthorizer::class,
    \App\Authorization\WorkspaceMemberAuthorizer::class,
);
```

That single binding switches every consuming package over to strict mode at once.

Backwards compatibility
-----------------------

[](#backwards-compatibility)

The provider uses `bindIf`, so any explicit binding the host registers (before or after) wins. Without an explicit binding, the default `AllowAllAuthorizer` keeps current behavior.

Testing
-------

[](#testing)

```
composer test
```

License
-------

[](#license)

MIT.

###  Health Score

22

—

LowBetter than 22% of packages

Maintenance60

Regular maintenance activity

Popularity10

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/a1ca6f6e01ecbfe6640ff410c4f0321054fb48d05a5f843c2b89887b90369bcd?d=identicon)[whilesmart](/maintainers/whilesmart)

---

Top Contributors

[![nfebe](https://avatars.githubusercontent.com/u/14317775?v=4)](https://github.com/nfebe "nfebe (2 commits)")

### Embed Badge

![Health badge](/badges/whilesmart-eloquent-owner-access/health.svg)

```
[![Health](https://phpackages.com/badges/whilesmart-eloquent-owner-access/health.svg)](https://phpackages.com/packages/whilesmart-eloquent-owner-access)
```

###  Alternatives

[kartik-v/yii2-password

Useful password strength validation utilities for Yii Framework 2.0

761.2M17](/packages/kartik-v-yii2-password)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
