Skip to content

[Bug]: Incorrect validation/Rule::exists behavior for UUID model keys #1045

@romalytvynenko

Description

@romalytvynenko

What happened?

Rule::exists validation with UUID-based models doesn't work as expected. For example:

- ['required', Rule::exists('circles')] // ❌ circle_id: integer (incorrectly maps to column circle_id)
- ['required', Rule::exists('circles', 'id')] // ❌ circle_id: integer
- ['required', 'exists:circles'] // ❌ circle_id: integer
- ['required', 'exists:circles,id'] // ❌ circle_id: integer

How to reproduce the bug

  1. Use a Laravel model with HasUuids trait for its primary key (see code below).
  2. Circle migration example:
Schema::create('circles', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->foreignUuid('creator_id')
        ->constrained('users')
        ->restrictOnDelete();
    $table->string('name');
    $table->text('description')->nullable();
    $table->string('emoji')->nullable();
    $table->timestamps();
});
  1. Sample model:
<?php

declare(strict_types=1);

namespace App\Models;

use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;

/**
 * Circle aggregates members, media, and invitations.
 */
final class Circle extends Model implements HasMedia
{
    use HasFactory;
    use HasUuids;
    use InteractsWithMedia;

    public const MEDIA_COLLECTION_AVATAR = 'avatar';

    protected function casts(): array
    {
        return [
            'created_at' => 'immutable_datetime',
            'updated_at' => 'immutable_datetime',
        ];
    }

    public function creator(): BelongsTo
    {
        return $this->belongsTo(User::class, 'creator_id');
    }

    public function memberships(): HasMany
    {
        return $this->hasMany(CircleMembership::class);
    }

    public function members(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'circle_user')
            ->using(CircleMembership::class)
            ->withPivot('id', 'role', 'created_at', 'updated_at')
            ->withTimestamps();
    }

    public function invitations(): HasMany
    {
        return $this->hasMany(CircleInvitation::class);
    }

    public function registerMediaCollections(): void
    {
        $this->addMediaCollection(self::MEDIA_COLLECTION_AVATAR)
            ->singleFile();
    }
}
  1. Attempt to validate 'circle_id' as follows:
['required', Rule::exists('circles')]
['required', Rule::exists('circles', 'id')]
['required', 'exists:circles']
['required', 'exists:circles,id']

Observe that only the first works for string, others fail with integer expectation or incorrect column mapping.

Package Version

latest

PHP Version

any

Laravel Version

12.x

Which operating systems does with happen with?

No response

Notes

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions