Installation:
composer require digital-craftsman/ids
Ensure PHP 8.4+ is used (or 8.5+ for latest features).
First Use Case: Define an ID value object for your entity:
use DigitalCraftsman\Ids\Id;
class UserId extends Id {}
Now use it in your entity:
class User
{
private UserId $id;
public function __construct(UserId $id)
{
$this->id = $id;
}
}
Key Classes to Explore:
Id (base class for all IDs)IdList (for collections of IDs)Doctrine\DBAL\Types\IdType (for database storage)Doctrine\DBAL\Types\IdListType (for database storage of lists)Entity Design:
Replace primitive string/int IDs with typed value objects:
class Order
{
private OrderId $id;
private OrderId $parentOrderId;
private OrderId[] $relatedOrders;
public function __construct(OrderId $id)
{
$this->id = $id;
}
}
Database Integration: Use Doctrine DBAL types for seamless storage:
# config/packages/doctrine.yaml
doctrine:
dbal:
types:
id: DigitalCraftsman\Ids\Doctrine\DBAL\Types\IdType
id_list: DigitalCraftsman\Ids\Doctrine\DBAL\Types\IdListType
Then annotate entity properties:
use Doctrine\DBAL\Types\Types;
class Order
{
#[ORM\Column(type: Types::ID)]
private OrderId $id;
}
API/Serialization: Leverages Symfony’s normalizers for automatic conversion:
// Request/Response
{
"id": "550e8400-e29b-41d4-a716-446655440000"
}
No manual casting needed—works with:
UUID Generation:
Prefer the PHP uuid extension for performance:
$id = new UserId(); // Auto-generates UUID
Fallback to symfony/polyfill-uuid if extension is unavailable.
Custom ID Formats:
Extend Id for non-UUID formats (e.g., ULIDs, integers):
class CustomId extends Id
{
protected static function generate(): string
{
return (string) random_int(1, PHP_INT_MAX);
}
}
Validation: Use built-in validation (e.g., UUIDv4 format):
use Symfony\Component\Validator\Constraints as Assert;
class UserId extends Id
{
#[Assert\Type('DigitalCraftsman\Ids\Id')]
public function __toString(): string
{
return $this->value;
}
}
Query Building:
Use IdList for IN clauses:
$ids = new IdList([new UserId('1'), new UserId('2')]);
$qb->andWhere('u.id IN (:ids)')
->setParameter('ids', $ids->toArray());
Testing: Mock IDs easily:
$mockId = new UserId('fixed-uuid-for-testing');
$user = new User($mockId);
Doctrine ORM Caching:
php bin/console doctrine:cache:clear-metadata
php bin/console doctrine:cache:clear-query
php bin/console doctrine:cache:clear-result
UUID Extension Conflict:
ramsey/uuid, ensure no version conflicts (both packages may use uuid namespace).digital-craftsman/ids’s UUID logic over ramsey/uuid to avoid duplication.Serialization Edge Cases:
#[Groups] in API Platform).Database Portability:
BINARY(16) for optimal indexing.IdType’s uuidType in Doctrine:
doctrine:
dbal:
types:
id:
uuid_type: 'binary(16)'
Validation Errors:
new UserId('invalid') throws InvalidArgumentException).Id::isValid() for manual checks:
if (!UserId::isValid('not-a-uuid')) {
throw new \InvalidArgumentException('Invalid ID format');
}
Doctrine Mappings:
Types::ID is correctly mapped in entity metadata:
$metadata->mapField([
'fieldName' => 'id',
'type' => 'id',
]);
Performance:
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
$id = new UserId();
}
echo microtime(true) - $start; // Compare with/without extension
Custom Normalizers: Override Symfony’s normalizers for custom formats:
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
class CustomIdDenormalizer implements DenormalizerInterface
{
public function denormalize($data, string $type, string $format, array $context)
{
return new $type($data); // Custom logic here
}
// ...
}
Register via Symfony’s serializer.normalizers service tag.
Event Listeners: Add logic on ID generation/validation:
use DigitalCraftsman\Ids\Event\IdGeneratedEvent;
$dispatcher->addListener(IdGeneratedEvent::class, function (IdGeneratedEvent $event) {
// Log or audit ID creation
});
Doctrine Lifecycle Callbacks:
Use prePersist/preUpdate to validate IDs:
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Order
{
#[ORM\PrePersist]
public function validateId(): void
{
if (!$this->id->isValid()) {
throw new \RuntimeException('Invalid ID');
}
}
}
API Platform: Customize ID output in collections:
# config/api_platform/resources.yaml
resources:
App\Entity\User:
collectionOperations:
get:
normalization_context:
groups: ['user:read']
denormalization_context:
groups: ['user:write']
How can I help you explore Laravel packages today?