<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/compoships-eloquent-relationships-on-multiple-columns-sslnzi5iu" -->

---
title: Compoships: Eloquent Relationships on Multiple Columns
description: Compoships is a package that extends Laravel Eloquent to support relationships and composite primary keys spanning multiple columns, useful for schemas with...
canonical: https://daily.dev/posts/compoships-eloquent-relationships-on-multiple-columns-sslnzi5iu
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Compoships: Eloquent Relationships on Multiple Columns | daily.dev
og:description: Compoships is a package that extends Laravel Eloquent to support relationships and composite primary keys spanning multiple columns, useful for schemas with...
og:url: https://daily.dev/posts/compoships-eloquent-relationships-on-multiple-columns-sslnzi5iu
og:image: https://api.daily.dev/og/posts/ssLNzI5IU.png
og:image:alt: Compoships: Eloquent Relationships on Multiple Columns
og:image:width: 1200
og:image:height: 630
og:locale: en
---

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

# Compoships: Eloquent Relationships on Multiple Columns

**[Laravel News](https://daily.dev/sources/ln)** · 5 min read · 3 upvotes · 0 comments

## Summary

Compoships is a package that extends Laravel Eloquent to support relationships and composite primary keys spanning multiple columns, useful for schemas with multi-column foreign keys such as imported accounting or legacy databases. It covers defining hasOne, hasMany, belongsTo, and belongsToMany relationships on column arrays, handling many-to-many pivot tables with tuple-based attach/detach/sync, scoping save/update/delete/refresh operations by composite keys, and correctly restoring composite-keyed models and collections from queued jobs. It requires PHP 8.2 and Laravel 12 or 13, installed via Composer.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://laravel-news.com/compoships>

## Questions this post answers

### How do I define an Eloquent hasMany relationship that matches on two columns instead of one?

Use the Compoships package's trait and pass arrays of column names instead of strings to hasMany, hasOne, belongsTo, or belongsToMany. Both models need the Awobaz\Compoships\Compoships trait, and the relationship method takes the foreign and local key columns as arrays, e.g. hasMany(OrderLine::class, ['company_code', 'order_no'], ['company_code', 'order_no']).

_Developers wiring up multi-column relationships can find implementation patterns like this one on daily.dev._

### Why does chaining where() onto a Laravel hasMany relationship break under eager loading?

Eloquent builds an eager-loaded relationship from a new empty model instance, so any parent attribute referenced inside a where() clause is null at that point, causing the query to return the wrong rows. This is why a manual multi-column workaround fails silently instead of throwing an error, and why a dedicated package like Compoships is needed for true multi-column matching.

_Anyone debugging eager-loading edge cases in Laravel can track fixes like this on daily.dev._

### How do you scope Eloquent save and delete operations by a composite primary key instead of a single column?

Declare a protected $compositeKey array property listing all key columns while keeping $primaryKey as a scalar column name, using the Compoships trait. This makes save(), update(), delete() (including soft deletes), refresh(), and fresh() build their WHERE clause from every column in $compositeKey, preventing an UPDATE from matching the wrong row when the scalar key alone is not unique.

_Teams handling composite-keyed tables can keep tabs on Eloquent tooling like this via daily.dev._

## Similar posts on daily.dev

- [Many-to-Many Polymorphic Relationships](https://daily.dev/posts/many-to-many-polymorphic-relationships-po58cqxvb) · Larablog · 0 upvotes · 0 comments
- [Many-to-Many Relationships](https://daily.dev/posts/many-to-many-relationships-ehzqud6br) · Larablog · 0 upvotes · 0 comments
- [Polymorphic Relationships](https://daily.dev/posts/polymorphic-relationships-dozujvkuq) · Larablog · 0 upvotes · 0 comments
- [One-to-Many Relationships](https://daily.dev/posts/one-to-many-relationships-swnrc9u0p) · Larablog · 1 upvotes · 0 comments

---

Tags: [#database](https://daily.dev/tags/database), [#php](https://daily.dev/tags/php), [#laravel](https://daily.dev/tags/laravel)

[View this post on daily.dev](https://daily.dev/posts/compoships-eloquent-relationships-on-multiple-columns-sslnzi5iu)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Compoships: Eloquent Relationships on Multiple Columns","url":"https://daily.dev/posts/compoships-eloquent-relationships-on-multiple-columns-sslnzi5iu","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/compoships-eloquent-relationships-on-multiple-columns-sslnzi5iu"},"datePublished":"2026-09-01T13:04:44.420Z","dateModified":"2026-09-01T13:05:10.610Z","description":"Compoships is a package that extends Laravel Eloquent to support relationships and composite primary keys spanning multiple columns, useful for schemas with...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/eac259c835eea8ce20f1408f7822a65f?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/eac259c835eea8ce20f1408f7822a65f?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Laravel News","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Laravel News","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/ln","url":"https://daily.dev/sources/ln"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/compoships-eloquent-relationships-on-multiple-columns-sslnzi5iu","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":3},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"database,php,laravel","timeRequired":"PT5M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Laravel News","item":"https://daily.dev/sources/ln"},{"@type":"ListItem","position":3,"name":"Compoships: Eloquent Relationships on Multiple Columns"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/compoships-eloquent-relationships-on-multiple-columns-sslnzi5iu#faq","mainEntity":[{"@type":"Question","name":"How do I define an Eloquent hasMany relationship that matches on two columns instead of one?","acceptedAnswer":{"@type":"Answer","text":"Use the Compoships package's trait and pass arrays of column names instead of strings to hasMany, hasOne, belongsTo, or belongsToMany. Both models need the Awobaz\\Compoships\\Compoships trait, and the relationship method takes the foreign and local key columns as arrays, e.g. hasMany(OrderLine::class, ['company_code', 'order_no'], ['company_code', 'order_no']). Developers wiring up multi-column relationships can find implementation patterns like this one on daily.dev."}},{"@type":"Question","name":"Why does chaining where() onto a Laravel hasMany relationship break under eager loading?","acceptedAnswer":{"@type":"Answer","text":"Eloquent builds an eager-loaded relationship from a new empty model instance, so any parent attribute referenced inside a where() clause is null at that point, causing the query to return the wrong rows. This is why a manual multi-column workaround fails silently instead of throwing an error, and why a dedicated package like Compoships is needed for true multi-column matching. Anyone debugging eager-loading edge cases in Laravel can track fixes like this on daily.dev."}},{"@type":"Question","name":"How do you scope Eloquent save and delete operations by a composite primary key instead of a single column?","acceptedAnswer":{"@type":"Answer","text":"Declare a protected $compositeKey array property listing all key columns while keeping $primaryKey as a scalar column name, using the Compoships trait. This makes save(), update(), delete() (including soft deletes), refresh(), and fresh() build their WHERE clause from every column in $compositeKey, preventing an UPDATE from matching the wrong row when the scalar key alone is not unique. Teams handling composite-keyed tables can keep tabs on Eloquent tooling like this via daily.dev."}}]}
```

