PHPackages                             awobaz/compoships - 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. [Database &amp; ORM](/categories/database)
4. /
5. awobaz/compoships

ActiveLibrary[Database &amp; ORM](/categories/database)

awobaz/compoships
=================

Laravel relationships with support for composite/multiple keys

3.0.2(1mo ago)1.2k11.7M↓35.8%142[34 issues](https://github.com/topclaudy/compoships/issues)[9 PRs](https://github.com/topclaudy/compoships/pulls)20MITPHPPHP ^8.2CI passing

Since Apr 4Pushed 1mo ago12 watchersCompare

[ Source](https://github.com/topclaudy/compoships)[ Packagist](https://packagist.org/packages/awobaz/compoships)[ Fund](https://paypal.me/awobaz)[ RSS](/packages/awobaz-compoships/feed)WikiDiscussions master Synced 3d ago

READMEChangelog (9)Dependencies (13)Versions (57)Used By (20)

Compoships
==========

[](#compoships)

**Compoships** offers the ability to specify relationships based on two (or more) columns in Laravel's Eloquent ORM. The need to match multiple columns in the definition of an Eloquent relationship often arises when working with third party or pre existing schema/database.

The problem
-----------

[](#the-problem)

Eloquent doesn't support composite keys. As a consequence, there is no way to define a relationship from one model to another by matching more than one column. Trying to use `where clauses` (like in the example below) won't work when eager loading the relationship because at the time the relationship is processed **$this-&gt;team\_id** is null.

```
namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    public function tasks()
    {
        //WON'T WORK WITH EAGER LOADING!!!
        return $this->hasMany(Task::class)->where('team_id', $this->team_id);
    }
}
```

#### Related discussions:

[](#related-discussions)

- [Relationship on multiple keys](https://laracasts.com/discuss/channels/eloquent/relationship-on-multiple-keys)
- [Querying relations with extra conditions not working as expected](https://github.com/laravel/framework/issues/1272)
- [Querying relations with extra conditions in Eager Loading not working](https://github.com/laravel/framework/issues/19488)
- [BelongsTo relationship with 2 foreign keys](https://laravel.io/forum/08-02-2014-belongsto-relationship-with-2-foreign-keys)
- [Laravel Eloquent: multiple foreign keys for relationship](https://stackoverflow.com/questions/48077890/laravel-eloquent-multiple-foreign-keys-for-relationship/49834070#49834070)
- [Laravel hasMany association with multiple columns](https://stackoverflow.com/questions/32471084/laravel-hasmany-association-with-multiple-columns)

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

[](#installation)

The recommended way to install **Compoships** is through [Composer](http://getcomposer.org/)

```
$ composer require awobaz/compoships
```

Usage
-----

[](#usage)

### Using the `Awobaz\Compoships\Database\Eloquent\Model` class

[](#using-the-awobazcomposhipsdatabaseeloquentmodel-class)

Simply make your model class derive from the `Awobaz\Compoships\Database\Eloquent\Model` base class. The `Awobaz\Compoships\Database\Eloquent\Model` extends the `Eloquent` base class without changing its core functionality.

### Using the `Awobaz\Compoships\Compoships` trait

[](#using-the-awobazcomposhipscompoships-trait)

If for some reason you can't derive your models from `Awobaz\Compoships\Database\Eloquent\Model`, you may take advantage of the `Awobaz\Compoships\Compoships` trait. Simply use the trait in your models.

**Note:** To define a multi-columns relationship from a model *A* to another model *B*, **both models must either extend `Awobaz\Compoships\Database\Eloquent\Model` or use the `Awobaz\Compoships\Compoships` trait**

### Syntax

[](#syntax)

... and now we can define a relationship from a model *A* to another model *B* by matching two or more columns (by passing an array of columns instead of a string).

```
namespace App;

use Illuminate\Database\Eloquent\Model;

class A extends Model
{
    use \Awobaz\Compoships\Compoships;

    public function b()
    {
        return $this->hasMany('B', ['foreignKey1', 'foreignKey2'], ['localKey1', 'localKey2']);
    }
}
```

We can use the same syntax to define the inverse of the relationship:

```
namespace App;

use Illuminate\Database\Eloquent\Model;

class B extends Model
{
    use \Awobaz\Compoships\Compoships;

    public function a()
    {
        return $this->belongsTo('A', ['foreignKey1', 'foreignKey2'], ['ownerKey1', 'ownerKey2']);
    }
}
```

We can also define many-to-many relationships with composite keys through a pivot table:

```
namespace App;

use Illuminate\Database\Eloquent\Model;

class A extends Model
{
    use \Awobaz\Compoships\Compoships;

    public function b()
    {
        return $this->belongsToMany(
            B::class,
            'a_b',                                  // pivot table
            ['a_foreignKey1', 'a_foreignKey2'],     // foreign pivot keys for A
            ['b_foreignKey1', 'b_foreignKey2'],     // foreign pivot keys for B
            ['localKey1', 'localKey2'],             // local keys on A
            ['localKey1', 'localKey2']              // local keys on B
        );
    }
}
```

All standard `belongsToMany` operations work with composite keys: `attach()`, `detach()`, `sync()`, `toggle()`, `withPivot()`, `withTimestamps()`, eager loading, and existence queries (`has()`, `whereHas()`).

#### Composite-key input shapes for `attach()` and `sync()`

[](#composite-key-input-shapes-for-attach-and-sync)

Two input shapes are supported for `attach()`, `sync()`, `syncWithoutDetaching()`, and `toggle()` on composite-key relations.

A list of composite tuples (each tuple is an array aligned with the related-pivot-key columns):

```
$team->projects()->attach([
    ['EU', 2],
    ['US', 1],
]);
```

A map of `json_encode($tuple) => $perRowAttributes`, equivalent to Laravel's single-key `[id => attributes]` shape. The key must be the JSON encoding of the composite tuple, produced via `json_encode([...])`. Per-row attributes override any shared bulk attributes on key conflict, and any per-row attribute keys colliding with the foreign-pivot-key columns are silently dropped to prevent overriding the parent linkage.

```
$team->projects()->attach([
    json_encode(['EU', 2]) => ['role' => 'reviewer'],
    json_encode(['US', 1]) => ['role' => 'lead'],
], ['note' => 'bulk applied to all']);
```

Passing an associative array key that is not a JSON-encoded tuple of the correct arity throws `Awobaz\Compoships\Exceptions\InvalidUsageException`.

### Factories

[](#factories)

Chances are that you may need factories for your Compoships models. If so, you will probably need to use Factory methods to create relationship models. For example, by using the -&gt;has() method. Just use the `Awobaz\Compoships\Database\Eloquent\Factories\ComposhipsFactory` trait in your factory classes to be able to use relationships correctly.

### Example

[](#example)

As an example, let's pretend we have a task list with categories, managed by several teams of users where:

- a task belongs to a category
- a task is assigned to a team
- a team has many users
- a user belongs to one team
- a user is responsible for one category of tasks

The user responsible for a particular task is the user *currently* in charge for the category inside the team.

```
namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    use \Awobaz\Compoships\Compoships;

    public function tasks()
    {
        return $this->hasMany(Task::class, ['team_id', 'category_id'], ['team_id', 'category_id']);
    }
}
```

Again, same syntax to define the inverse of the relationship:

```
namespace App;

use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
    use \Awobaz\Compoships\Compoships;

    public function user()
    {
        return $this->belongsTo(User::class, ['team_id', 'category_id'], ['team_id', 'category_id']);
    }
}
```

For a many-to-many scenario, imagine users can be assigned to projects, where both the user and the project are identified by a composite key (`team_id` and `department_id`):

```
namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    use \Awobaz\Compoships\Compoships;

    public function projects()
    {
        return $this->belongsToMany(
            Project::class,
            'project_user',
            ['user_team_id', 'user_department_id'],
            ['project_team_id', 'project_department_id'],
            ['team_id', 'department_id'],
            ['team_id', 'department_id']
        );
    }
}
```

Supported relationships
-----------------------

[](#supported-relationships)

**Compoships** supports the following Laravel Eloquent relationships:

- hasOne
- hasMany
- belongsTo
- belongsToMany

Also please note that while **nullable columns are supported by Compoships**, relationships with only null values are not currently possible.

**Note on `belongsToMany`:** Custom pivot models (via `using()`) with composite keys are supported. Your custom pivot class should extend `Awobaz\Compoships\Database\Eloquent\Relations\Pivot` instead of Laravel's base `Pivot` class to ensure correct behavior for save, delete, and queue operations.

Composite primary keys
----------------------

[](#composite-primary-keys)

By default, Eloquent builds the WHERE clause for `UPDATE`, `DELETE`, and `refresh()` / `fresh()` using only the scalar `$primaryKey`. On tables whose primary key spans multiple columns (such as `(id, tenant_id)` in multi-tenant or partitioned schemas), `$model->save()` on a hydrated row emits a query like `UPDATE table SET ... WHERE id = ?`, missing the discriminator. The same scalar id can exist under another discriminator value, so the operation silently targets the wrong row.

**Compoships** lets you opt into composite primary key handling on the write path. Declare a `$compositeKey` property on the model that enumerates every column in the primary key (including the scalar slot named by `$primaryKey`). Keep `$primaryKey` as the scalar column name so `getKey()`, `Model::find($id)`, route model binding, and queue serialization continue to work unchanged.

```
namespace App;

use Awobaz\Compoships\Compoships;
use Illuminate\Database\Eloquent\Model;

class TenantUser extends Model
{
    use Compoships;

    protected $primaryKey = 'id';

    public $incrementing = false;

    protected $keyType = 'string';

    protected $compositeKey = ['id', 'tenant_id'];

    protected $guarded = [];
}
```

With this declaration, the following operations scope their WHERE clause by every column in `$compositeKey`:

- `save()` and `update()` on a hydrated model.
- `delete()` (including soft deletes via `SoftDeletes::runSoftDelete()`).
- `refresh()` and `fresh()`.

For example:

```
$user = TenantUser::where('id', 'u1')->where('tenant_id', 't1')->first();
$user->name = 'Alice';
$user->save();
// UPDATE tenant_users SET name = ? WHERE id = ? AND tenant_id = ?

```

The following operations are **not** affected. They remain identical to stock Eloquent.

- `Model::find($id)` looks up by the scalar primary key only.
- `firstOrCreate`, `updateOrCreate`, and similar helpers build their own WHERE clauses from user input.
- Route model binding by single id continues to use the scalar key.

Queue serialization (via `Illuminate\Queue\SerializesModels`, used by queueable jobs, events, and notifications) participates in composite handling for **single-model** properties on the job: `getQueueableId()` returns a JSON-encoded array of the composite key columns, and `newQueryForRestoration()` decodes it back into a query that scopes by every key column on the worker side. Round-tripping a single composite-keyed model through the queue reloads the exact composite row that was queued. Old queued payloads predating this feature (with a scalar id) continue to restore via the parent path, so no queue drain is required on upgrade.

**Collection round-trip requires the `QueueableCompositeCollection` wrapper.** A raw `Illuminate\Database\Eloquent\Collection` of composite-keyed models on a job property will restore as an empty collection. The cause is in Laravel's `restoreCollection`: it re-keys loaded models by scalar `getKey()` and looks up by the original queued ids (our JSON-encoded composite strings), so the lookup keys never match. The package ships a wrapper class that sidesteps the issue by capturing composite-key tuples at queue time and rebuilding the collection via composite-aware query at restore time:

```
use Awobaz\Compoships\Queue\QueueableCompositeCollection;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Queue\SerializesModels;

class ProcessUsers
{
    use SerializesModels;

    public QueueableCompositeCollection $users;

    public function __construct(Collection $users)
    {
        $this->users = QueueableCompositeCollection::for($users);
    }

    public function handle(): void
    {
        $users = $this->users->restore();
        // ...
    }
}
```

The wrapper preserves the original collection order, eager-loaded relations, and the model's connection. It rejects mixed-class collections (throws `LogicException`) and misconfigured `$compositeKey` declarations (throws `InvalidUsageException`) at wrap time. The wrapper is opaque to `SerializesModels`, so PHP's standard serialization captures its state directly. Each call to `restore()` issues one database query.

If you declare `$compositeKey` on a model whose array does not contain the value of `$primaryKey`, the trait throws `Awobaz\Compoships\Exceptions\InvalidUsageException` on the first save, delete, or refresh. The array must enumerate the whole primary key.

If you mutate a discriminator column in memory before calling `save()` (for example, `$user->tenant_id = $newTenant`), the UPDATE still targets the row as it exists in storage. The WHERE clause uses the original raw value from `$model->original`, then the SET clause writes the new value.

Nullable composite-key columns are supported. When the original raw value of a column is `null`, the trait emits `WHERE column IS NULL` rather than binding `null` into a `=` predicate (which SQL evaluates as never-true). This makes `$compositeKey` safe to use as a composite scoping key for tables that use a `UNIQUE(...)` index with a nullable discriminator rather than a strict composite primary key.

#### Note for consumers with their own override

[](#note-for-consumers-with-their-own-override)

If your model already overrides `setKeysForSaveQuery()` or `setKeysForSelectQuery()`, call `parent::setKeysForSaveQuery($query)` (and the select equivalent) first to inherit the composite key handling. Without `parent::`, the override loses the composite WHERE silently.

Support for nullable columns in 2.x
-----------------------------------

[](#support-for-nullable-columns-in-2x)

Version 2.x brings support for nullable columns. The results may now be different than on version 1.x when a column is null on a relationship, so we bumped the version to 2.x, as this might be a breaking change.

Scope
-----

[](#scope)

**Compoships** targets two specific gaps in Eloquent:

1. Defining `hasOne`, `hasMany`, `belongsTo`, and `belongsToMany` relationships across multiple columns.
2. Scoping the write path (`save`, `update`, `delete`, `refresh`, `fresh`) by every column of a composite primary key on models that opt in via `$compositeKey`.

The package does not re-implement Laravel's primary-key handling end-to-end. The following continue to use the scalar `$primaryKey`:

- `Model::find($id)` lookups.
- Route model binding.

Queue serialization (`Illuminate\Queue\SerializesModels`) is supported for composite-keyed models. See the "Composite primary keys" section for the round-trip contract.

Builder-level bulk operations (`Model::query()->...->update(...)`) continue to use whatever WHERE clauses you build. For composite-key bulk patterns, the custom Query Builder's tuple `whereIn` works directly:

```
Model::whereIn(['id', 'tenant_id'], [['u1', 't1'], ['u2', 't2']])->update(['name' => 'X']);
```

Most Laravel applications work best with a single scalar primary key. Compoships exists for the cases where the schema is not under your control (third-party databases, legacy systems, partitioned or multi-tenant tables) or where matching multiple columns in a relationship definition is unavoidable.

Contributing
------------

[](#contributing)

Please read [CONTRIBUTING.md](https://github.com/topclaudy/compoships/blob/master/CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests.

[![](https://camo.githubusercontent.com/3170920f099804f92e9cdc9fe54f73add9e529e334ab959f8c1b99020392996e/68747470733a2f2f736f757263657265722e696f2f66616d652f746f70636c617564792f746f70636c617564792f636f6d706f73686970732f696d616765732f30)](https://sourcerer.io/fame/topclaudy/topclaudy/compoships/links/0)[![](https://camo.githubusercontent.com/1ddc7163b26f6c714e4395ea35ff3ecc34bd2077cb23bedf2999efe31ad9be35/68747470733a2f2f736f757263657265722e696f2f66616d652f746f70636c617564792f746f70636c617564792f636f6d706f73686970732f696d616765732f31)](https://sourcerer.io/fame/topclaudy/topclaudy/compoships/links/1)[![](https://camo.githubusercontent.com/b5e66a4b32c7b4e53706897ac21635902eb9ddd2de0fddd64608080f1c15d578/68747470733a2f2f736f757263657265722e696f2f66616d652f746f70636c617564792f746f70636c617564792f636f6d706f73686970732f696d616765732f32)](https://sourcerer.io/fame/topclaudy/topclaudy/compoships/links/2)[![](https://camo.githubusercontent.com/fccbbcccdfc98e00baa58eaf0b1178ed25a9471576de48d25f6b926bfc9bc02c/68747470733a2f2f736f757263657265722e696f2f66616d652f746f70636c617564792f746f70636c617564792f636f6d706f73686970732f696d616765732f33)](https://sourcerer.io/fame/topclaudy/topclaudy/compoships/links/3)[![](https://camo.githubusercontent.com/e0e7d39c8486044702bb78ad3b9f443cfda7c1d0417e59ab69d4aad37bcc7ac6/68747470733a2f2f736f757263657265722e696f2f66616d652f746f70636c617564792f746f70636c617564792f636f6d706f73686970732f696d616765732f34)](https://sourcerer.io/fame/topclaudy/topclaudy/compoships/links/4)[![](https://camo.githubusercontent.com/e5eb643768cdf5532c81a834b77e6b96241c6dedc5c4449adac01e4d9a089c65/68747470733a2f2f736f757263657265722e696f2f66616d652f746f70636c617564792f746f70636c617564792f636f6d706f73686970732f696d616765732f35)](https://sourcerer.io/fame/topclaudy/topclaudy/compoships/links/5)[![](https://camo.githubusercontent.com/c6d275bb8cf53c5aef6bc6dbaab4d12be3f8ded8d1c5f1c589b43460e1797218/68747470733a2f2f736f757263657265722e696f2f66616d652f746f70636c617564792f746f70636c617564792f636f6d706f73686970732f696d616765732f36)](https://sourcerer.io/fame/topclaudy/topclaudy/compoships/links/6)

Versioning
----------

[](#versioning)

We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/topclaudy/compoships/tags).

Unit Tests
----------

[](#unit-tests)

To run unit tests you have to use PHPUnit

Install compoships repository

```
git clone https://github.com/topclaudy/compoships.git
cd compoships
composer install
```

Run PHPUnit

```
./vendor/bin/phpunit
```

### Running the full CI matrix locally

[](#running-the-full-ci-matrix-locally)

The package is tested against multiple Laravel and PHP versions in CI. To reproduce that matrix on your machine without setting up each PHP version manually, use the bundled Docker runner:

```
./run-matrix-tests.sh
```

The script mirrors `.github/workflows/run-tests.yml` exactly. It iterates over every Laravel and PHP combination, installs the requested Laravel version with Composer inside an ephemeral Docker container, runs PHPUnit, and prints a pass/fail summary at the end. Docker is the only prerequisite.

You can narrow the run to a subset by passing a filter argument that matches against the matrix label (`L PHP`):

```
./run-matrix-tests.sh "12.*"     # only Laravel 12 combinations
./run-matrix-tests.sh "PHP8.4"   # only PHP 8.4 combinations
```

Authors
-------

[](#authors)

- [Claudin J. Daniel](https://github.com/topclaudy) - *Initial work*

Support This Project
--------------------

[](#support-this-project)

[![Buy Me a Coffee via Paypal](https://camo.githubusercontent.com/7f803b4c3a634f4d451c4310d00c2520aae1f15ef910420a0f27890e3c4787dd/68747470733a2f2f617a3734333730322e766f2e6d7365636e642e6e65742f63646e2f6b6f6669332e706e673f763d30)](https://paypal.me/awobaz)

License
-------

[](#license)

**Compoships** is licensed under the [MIT License](http://opensource.org/licenses/MIT).

###  Health Score

78

—

ExcellentBetter than 100% of packages

Maintenance93

Actively maintained with recent releases

Popularity71

Solid adoption and visibility

Community45

Growing community involvement

Maturity89

Battle-tested with a long release history

 Bus Factor2

2 contributors hold 50%+ of commits

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.

###  Release Activity

Cadence

Every ~54 days

Recently: every ~40 days

Total

56

Last Release

31d ago

Major Versions

1.1.18 → 2.0.02020-05-14

2.5.5 → 3.0.02026-04-22

PHP version history (3 changes)1.0.0PHP &gt;=7

1.0.5PHP &gt;=5.6.4

3.0.0PHP ^8.2

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1437237?v=4)[Claudin J. Daniel](/maintainers/topclaudy)[@topclaudy](https://github.com/topclaudy)

---

Top Contributors

[![topclaudy](https://avatars.githubusercontent.com/u/1437237?v=4)](https://github.com/topclaudy "topclaudy (140 commits)")[![akalongman](https://avatars.githubusercontent.com/u/423050?v=4)](https://github.com/akalongman "akalongman (33 commits)")[![erikn69](https://avatars.githubusercontent.com/u/4933954?v=4)](https://github.com/erikn69 "erikn69 (21 commits)")[![parkourben99](https://avatars.githubusercontent.com/u/7295774?v=4)](https://github.com/parkourben99 "parkourben99 (16 commits)")[![yurii-github](https://avatars.githubusercontent.com/u/1162656?v=4)](https://github.com/yurii-github "yurii-github (13 commits)")[![mpyw](https://avatars.githubusercontent.com/u/1351893?v=4)](https://github.com/mpyw "mpyw (12 commits)")[![isclopezm](https://avatars.githubusercontent.com/u/14881514?v=4)](https://github.com/isclopezm "isclopezm (8 commits)")[![ChristopheB](https://avatars.githubusercontent.com/u/5377792?v=4)](https://github.com/ChristopheB "ChristopheB (7 commits)")[![SlyDave](https://avatars.githubusercontent.com/u/1842611?v=4)](https://github.com/SlyDave "SlyDave (5 commits)")[![jayjfletcher](https://avatars.githubusercontent.com/u/5743488?v=4)](https://github.com/jayjfletcher "jayjfletcher (5 commits)")[![SavKS](https://avatars.githubusercontent.com/u/1731510?v=4)](https://github.com/SavKS "SavKS (4 commits)")[![didac-adria](https://avatars.githubusercontent.com/u/63950600?v=4)](https://github.com/didac-adria "didac-adria (4 commits)")[![chrispappas](https://avatars.githubusercontent.com/u/4694803?v=4)](https://github.com/chrispappas "chrispappas (4 commits)")[![hackel](https://avatars.githubusercontent.com/u/583677?v=4)](https://github.com/hackel "hackel (3 commits)")[![fmangelsdorf](https://avatars.githubusercontent.com/u/24457087?v=4)](https://github.com/fmangelsdorf "fmangelsdorf (3 commits)")[![KentarouTakeda](https://avatars.githubusercontent.com/u/4785040?v=4)](https://github.com/KentarouTakeda "KentarouTakeda (2 commits)")[![pedrofmj](https://avatars.githubusercontent.com/u/935566?v=4)](https://github.com/pedrofmj "pedrofmj (2 commits)")[![wojo1206](https://avatars.githubusercontent.com/u/8170483?v=4)](https://github.com/wojo1206 "wojo1206 (2 commits)")[![leo108](https://avatars.githubusercontent.com/u/1551716?v=4)](https://github.com/leo108 "leo108 (2 commits)")[![axelitus](https://avatars.githubusercontent.com/u/732441?v=4)](https://github.com/axelitus "axelitus (1 commits)")

---

Tags

composite-keyseloquentlaravelmulti-columnsmulti-keysrelationshipslaravellaravel composite keyslaravel relationships

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/awobaz-compoships/health.svg)

```
[![Health](https://phpackages.com/badges/awobaz-compoships/health.svg)](https://phpackages.com/packages/awobaz-compoships)
```

###  Alternatives

[spatie/laravel-medialibrary

Associate files with Eloquent models

6.1k43.2M632](/packages/spatie-laravel-medialibrary)[mongodb/laravel-mongodb

A MongoDB based Eloquent model and Query builder for Laravel

7.1k8.4M96](/packages/mongodb-laravel-mongodb)[kirschbaum-development/eloquent-power-joins

The Laravel magic applied to joins.

1.6k32.6M46](/packages/kirschbaum-development-eloquent-power-joins)[yajra/laravel-oci8

Oracle DB driver for Laravel via OCI8

8793.2M25](/packages/yajra-laravel-oci8)[bavix/laravel-wallet

It's easy to work with a virtual wallet.

1.3k1.3M19](/packages/bavix-laravel-wallet)[glushkovds/phpclickhouse-laravel

Adapter of the most popular library https://github.com/smi2/phpClickHouse to Laravel

2051.5M2](/packages/glushkovds-phpclickhouse-laravel)

PHPackages © 2026

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