Skip to content

Commit

Permalink
Add classes to cast ObjectId and UUID instances (mongodb#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
alcaeus committed Jun 29, 2023
1 parent ea2da7a commit b6e8a44
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 0 deletions.
63 changes: 63 additions & 0 deletions src/Eloquent/Casts/BinaryUuid.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace Jenssegers\Mongodb\Eloquent\Casts;

use function bin2hex;
use function hex2bin;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Jenssegers\Mongodb\Eloquent\Model;
use MongoDB\BSON\Binary;
use function str_replace;
use function substr;

class BinaryUuid implements CastsAttributes
{
/**
* Cast the given value.
*
* @param Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return mixed
*/
public function get($model, string $key, $value, array $attributes)
{
if (! $value instanceof Binary || $value->getType() !== Binary::TYPE_UUID) {
return $value;
}

$base16Uuid = bin2hex($value->getData());

return sprintf(
'%s-%s-%s-%s-%s',
substr($base16Uuid, 0, 8),
substr($base16Uuid, 8, 4),
substr($base16Uuid, 12, 4),
substr($base16Uuid, 16, 4),
substr($base16Uuid, 20, 12),
);
}

/**
* Prepare the given value for storage.
*
* @param Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return mixed
*/
public function set($model, string $key, $value, array $attributes)
{
if ($value instanceof Binary) {
return $value;
}

if (is_string($value) && strlen($value) === 16) {
return new Binary($value, Binary::TYPE_UUID);
}

return new Binary(hex2bin(str_replace('-', '', $value)), Binary::TYPE_UUID);
}
}
46 changes: 46 additions & 0 deletions src/Eloquent/Casts/ObjectId.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace Jenssegers\Mongodb\Eloquent\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Jenssegers\Mongodb\Eloquent\Model;
use MongoDB\BSON\ObjectId as BSONObjectId;

class ObjectId implements CastsAttributes
{
/**
* Cast the given value.
*
* @param Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return mixed
*/
public function get($model, string $key, $value, array $attributes)
{
if (! $value instanceof BSONObjectId) {
return $value;
}

return (string) $value;
}

/**
* Prepare the given value for storage.
*
* @param Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return mixed
*/
public function set($model, string $key, $value, array $attributes)
{
if ($value instanceof BSONObjectId) {
return $value;
}

return new BSONObjectId($value);
}
}
48 changes: 48 additions & 0 deletions tests/Casts/BinaryUuidTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace Jenssegers\Mongodb\Tests\Casts;

use Generator;
use function hex2bin;
use Jenssegers\Mongodb\Tests\Models\CastBinaryUuid;
use Jenssegers\Mongodb\Tests\TestCase;
use MongoDB\BSON\Binary;

class BinaryUuidTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();

CastBinaryUuid::truncate();
}

/** @dataProvider provideBinaryUuidCast */
public function testBinaryUuidCastModel(string $expectedUuid, string|Binary $saveUuid, Binary $queryUuid): void
{
CastBinaryUuid::create(['uuid' => $saveUuid]);

$model = CastBinaryUuid::firstWhere('uuid', $queryUuid);
$this->assertNotNull($model);
$this->assertSame($expectedUuid, $model->uuid);
}

public static function provideBinaryUuidCast(): Generator
{
$uuid = '0c103357-3806-48c9-a84b-867dcb625cfb';
$binaryUuid = new Binary(hex2bin('0c103357380648c9a84b867dcb625cfb'), Binary::TYPE_UUID);

yield 'Save Binary, Query Binary' => [$uuid, $binaryUuid, $binaryUuid];
yield 'Save string, Query Binary' => [$uuid, $uuid, $binaryUuid];
}

public function testQueryByStringDoesNotCast(): void
{
$uuid = '0c103357-3806-48c9-a84b-867dcb625cfb';

CastBinaryUuid::create(['uuid' => $uuid]);

$model = CastBinaryUuid::firstWhere('uuid', $uuid);
$this->assertNull($model);
}
}
50 changes: 50 additions & 0 deletions tests/Casts/ObjectIdTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace Jenssegers\Mongodb\Tests\Casts;

use Generator;
use Jenssegers\Mongodb\Tests\Models\CastObjectId;
use Jenssegers\Mongodb\Tests\TestCase;
use MongoDB\BSON\ObjectId;

class ObjectIdTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();

CastObjectId::truncate();
}

/** @dataProvider provideObjectIdCast */
public function testStoreObjectId(string|ObjectId $saveObjectId, ObjectId $queryObjectId): void
{
$stringObjectId = (string) $saveObjectId;

CastObjectId::create(['oid' => $saveObjectId]);

$model = CastObjectId::firstWhere('oid', $queryObjectId);
$this->assertNotNull($model);
$this->assertSame($stringObjectId, $model->oid);
}

public static function provideObjectIdCast(): Generator
{
$objectId = new ObjectId();
$stringObjectId = (string) $objectId;

yield 'Save ObjectId, Query ObjectId' => [$objectId, $objectId];
yield 'Save string, Query ObjectId' => [$stringObjectId, $objectId];
}

public function testQueryByStringDoesNotCast(): void
{
$objectId = new ObjectId();
$stringObjectId = (string) $objectId;

CastObjectId::create(['oid' => $objectId]);

$model = CastObjectId::firstWhere('oid', $stringObjectId);
$this->assertNull($model);
}
}
17 changes: 17 additions & 0 deletions tests/Models/CastBinaryUuid.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Jenssegers\Mongodb\Tests\Models;

use Jenssegers\Mongodb\Eloquent\Casts\BinaryUuid;
use Jenssegers\Mongodb\Eloquent\Model as Eloquent;

class CastBinaryUuid extends Eloquent
{
protected $connection = 'mongodb';
protected static $unguarded = true;
protected $casts = [
'uuid' => BinaryUuid::class,
];
}
17 changes: 17 additions & 0 deletions tests/Models/CastObjectId.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Jenssegers\Mongodb\Tests\Models;

use Jenssegers\Mongodb\Eloquent\Casts\ObjectId;
use Jenssegers\Mongodb\Eloquent\Model as Eloquent;

class CastObjectId extends Eloquent
{
protected $connection = 'mongodb';
protected static $unguarded = true;
protected $casts = [
'oid' => ObjectId::class,
];
}

0 comments on commit b6e8a44

Please sign in to comment.