- 
                Notifications
    You must be signed in to change notification settings 
- Fork 11.6k
Feature/composite primary keys #57492
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/composite primary keys #57492
Conversation
This commit adds the ConfirmableTrait to config:cache, route:cache, view:cache, and event:cache commands to prevent accidental cache regeneration in production environments. Changes: - Added ConfirmableTrait to all cache commands - Added --force option to bypass confirmation prompts - Converted $name property to $signature to support options - Added confirmToProceed() check before cache operations The confirmation prompt only appears when running in production environment, following the same pattern used in other destructive commands like key:generate. Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
This commit adds missing @throws annotations to methods that throw exceptions but were not properly documented. This improves: - IDE autocomplete and warnings - PHPStan/Psalm static analysis - Developer experience Changes: - Config/Repository: Added @throws InvalidArgumentException to string(), integer(), float(), boolean(), and array() methods - Redis/Connections/PacksPhpRedisValues: Added @throws RuntimeException and UnexpectedValueException to pack() method - Session/SymfonySessionDecorator: Added @throws BadMethodCallException to registerBag(), getBag(), and getMetadataBag() methods 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
This commit implements comprehensive support for composite (multi-column)
primary keys in Laravel's Eloquent ORM, enabling developers to work with
tables that use multiple columns as their primary key.
## Core Changes
### Model Class (src/Illuminate/Database/Eloquent/Model.php)
- Updated `$primaryKey` property to accept `string|array<int, string>`
- Updated `$keyType` property to accept `string|array<int, string>`
- Added `hasCompositeKey()` method to detect composite keys
- Modified `getKey()` to return array of key values for composite keys
- Modified `getKeyName()` to return array of key names for composite keys
- Modified `getQualifiedKeyName()` to return array of qualified key names
- Modified `setKeysForSaveQuery()` to handle multiple WHERE clauses
- Modified `getKeyForSaveQuery()` to accept optional key parameter
- Added validation in `performInsert()` to prevent auto-incrementing composite keys
### Builder Class (src/Illuminate/Database/Eloquent/Builder.php)
- Modified `whereKey()` to detect and handle composite keys
- Added `whereCompositeKey()` method for composite key WHERE clauses
- Added `isAssociativeArray()` helper method
- Added `InvalidArgumentException` import for validation errors
## Features
✅ **Create**: Insert records with composite primary keys
✅ **Read**: Find records using composite key arrays
✅ **Update**: Update records identified by composite keys
✅ **Delete**: Delete records using composite keys
✅ **Query Builder**: whereKey() and whereKeyNot() support
✅ **Validation**: Prevents auto-incrementing composite keys
✅ **Backward Compatible**: Zero impact on existing single-key models
## Usage Example
```php
class OrderItem extends Model {
    protected $primaryKey = ['order_id', 'product_id'];
    public $incrementing = false;
}
// Find by composite key
$item = OrderItem::find(['order_id' => 1, 'product_id' => 5]);
// Update
$item->quantity = 10;
$item->save();
// Query builder
OrderItem::whereKey(['order_id' => 1, 'product_id' => 5])->first();
```
## Tests
Added comprehensive integration test suite (18 test cases):
- Basic CRUD operations
- findOrFail() with composite keys
- whereKey() and whereKeyNot() queries
- Validation for auto-incrementing restriction
- fresh() and refresh() methods
- Multiple record queries
## Breaking Changes
None - fully backward compatible with existing single-key models.
Related to enterprise requirements for normalized database schemas
commonly using composite primary keys.
    There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| $keyNames = $this->model->getKeyName(); | ||
|  | ||
| // Single composite key: ['order_id' => 1, 'product_id' => 5] | ||
| if ($this->isAssociativeArray($keyValues)) { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could just be:
| if ($this->isAssociativeArray($keyValues)) { | |
| if (! array_is_list($keyValues))) { | 
| { | ||
| if ($this->hasCompositeKey()) { | ||
| return array_map( | ||
| fn ($key) => $this->qualifyColumn($key), | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want, you can use first-class callable syntax here to improve static analysis among other things
| fn ($key) => $this->qualifyColumn($key), | |
| $this->qualifyColumn(...), | 
Native Composite Primary Key Support for Eloquent ORM
Summary
This PR implements comprehensive support for composite (multi-column) primary keys in Laravel's Eloquent ORM, enabling developers to work seamlessly with tables that use multiple columns as their primary key.
Motivation
Many enterprise and normalized database schemas use composite primary keys for:
order_itemswithorder_id+product_id)tenant_id+id)document_id+version)Currently, developers must use workarounds or third-party packages like
awobaz/compoships, which only support relationships but not full CRUD operations.Changes
Core Model Changes (
Illuminate\Database\Eloquent\Model)1. Enhanced Property Types