Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions src/BinaryUuidBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php

declare(strict_types=1);

namespace Dyrynda\Database\Support;

use Illuminate\Database\Eloquent\Builder;
use Ramsey\Uuid\Uuid;

/**
* Custom Eloquent Builder for models with binary UUID columns.
*
* This builder automatically converts UUID strings to binary format
* when querying columns that use the EfficientUuid cast, ensuring
* Eloquent relationships work correctly with binary UUID storage.
*
* @template TModel of \Illuminate\Database\Eloquent\Model
*
* @extends Builder<TModel>
*/
class BinaryUuidBuilder extends Builder
{
/**
* Add a basic where clause to the query.
*
* Automatically converts UUID strings to binary when querying
* columns that use the EfficientUuid cast.
*
* @param \Closure|string|array<mixed>|\Illuminate\Contracts\Database\Query\Expression $column
* @param mixed $operator
* @param mixed $value
* @param string $boolean
* @return $this
*/
public function where($column, $operator = null, $value = null, $boolean = 'and')
{
// Check if this is a simple where clause on a UUID column
if (is_string($column) && $this->shouldConvertToUuidBinary($column, $value ?? $operator)) {
$actualValue = func_num_args() === 2 ? $operator : $value;

if (is_string($actualValue) && Uuid::isValid($actualValue)) {
// Convert UUID string to binary
$binaryValue = Uuid::fromString($actualValue)->getBytes();

if (func_num_args() === 2) {
return parent::where($column, '=', $binaryValue, $boolean);
}

return parent::where($column, $operator, $binaryValue, $boolean);
}
}

return parent::where($column, $operator, $value, $boolean);
}

/**
* Add a "where in" clause to the query.
*
* Automatically converts arrays of UUID strings to binary format.
* This is crucial for eager loading (with()) and other bulk queries.
*
* @param \Illuminate\Contracts\Database\Query\Expression|string $column
* @param mixed $values
* @param string $boolean
* @param bool $not
* @return $this
*/
public function whereIn($column, $values, $boolean = 'and', $not = false)
{
if (is_string($column) && $this->isUuidColumn($column)) {
// Convert array of UUID strings to binary
$values = collect($values)->map(function ($value) {
if (is_string($value) && Uuid::isValid($value)) {
return Uuid::fromString($value)->getBytes();
}

return $value;
})->all();
}

return parent::whereIn($column, $values, $boolean, $not);
}

/**
* Add a "where not in" clause to the query.
*
* Automatically converts arrays of UUID strings to binary format.
*
* @param \Illuminate\Contracts\Database\Query\Expression|string $column
* @param mixed $values
* @param string $boolean
* @return $this
*/
public function whereNotIn($column, $values, $boolean = 'and')
{
return $this->whereIn($column, $values, $boolean, true);
}

/**
* Check if column should convert UUID string to binary.
*/
protected function shouldConvertToUuidBinary(string $column, mixed $value): bool
{
return $this->isUuidColumn($column) && is_string($value) && Uuid::isValid($value);
}

/**
* Check if a column uses UUID binary cast (EfficientUuid).
*/
protected function isUuidColumn(string $column): bool
{
$model = $this->getModel();

// Remove table prefix if present (e.g., 'users.id' -> 'id')
$columnName = str_contains($column, '.')
? substr($column, strrpos($column, '.') + 1)
: $column;

// Get casts from the model
$casts = $model->getCasts();

if (! isset($casts[$columnName])) {
return false;
}

$castType = $casts[$columnName];

// Handle string class names
if (is_string($castType)) {
return $castType === Casts\EfficientUuid::class
|| $castType === 'Dyrynda\Database\Support\Casts\EfficientUuid';
}

// Handle object instances (Laravel 12+)
return $castType instanceof Casts\EfficientUuid;
}
}
58 changes: 58 additions & 0 deletions src/UsesBinaryUuidBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace Dyrynda\Database\Support;

/**
* Enables binary UUID relationship support.
*
* Use this trait on models that store UUIDs as BINARY(16) using
* the EfficientUuid cast to enable automatic conversion in
* Eloquent relationships.
*
* When to use this trait:
* - Your model uses the EfficientUuid cast on UUID columns
* - You need Eloquent relationships to work with binary UUID storage
* - You're experiencing issues with belongsTo, hasMany, or other relationships
*
* Example usage:
* ```php
* use Dyrynda\Database\Support\{GeneratesUuid, UsesBinaryUuidBuilder};
* use Dyrynda\Database\Support\Casts\EfficientUuid;
*
* class User extends Model
* {
* use GeneratesUuid, UsesBinaryUuidBuilder;
*
* public $incrementing = false;
* protected $keyType = 'string';
*
* protected function casts(): array
* {
* return ['id' => EfficientUuid::class];
* }
*
* public function posts()
* {
* return $this->hasMany(Post::class);
* }
* }
* ```
*/
trait UsesBinaryUuidBuilder
{
/**
* Create a new Eloquent query builder for the model.
*
* This method is called automatically by Eloquent when building queries.
* It returns our custom BinaryUuidBuilder to handle UUID binary conversions.
*
* @param \Illuminate\Database\Query\Builder $query
* @return BinaryUuidBuilder<static>
*/
public function newEloquentBuilder($query): BinaryUuidBuilder
{
return new BinaryUuidBuilder($query);
}
}
Loading