PHPackages                             martingold/autotype - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. martingold/autotype

ActiveLibrary[Database &amp; ORM](/categories/database)

martingold/autotype
===================

Automatically register value objects (DTOs, crates) as Doctrine types

0.2.1(1y ago)04.1kMITPHPPHP ^8.3

Since Nov 10Pushed 1y ago1 watchersCompare

[ Source](https://github.com/martingold/autotype)[ Packagist](https://packagist.org/packages/martingold/autotype)[ RSS](/packages/martingold-autotype/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (11)Versions (6)Used By (0)

AutoType
========

[](#autotype)

Library for auto-registering value objects (crates, DTOs) as Doctrine types.

Wouldn't it be nice if you could use your Value Objects as entity type directly? With AutoType you do not have to create [doctrine type](https://www.doctrine-project.org/projects/doctrine-orm/en/3.3/cookbook/custom-mapping-types.html)for every single value object type.

Usage
-----

[](#usage)

```
composer require martingold/autotype
```

You have few options:

1. Add `#[ValueGetter]` and `#[Constructor]` (not required) attributes to your ValueObject, DTO, Crate, ...
2. Make your objects implement ValueObject interface
3. Create your own driver (implement your own `TypeDefinitionDriver`). See below 👇

Example using attributes:

```
final readonly class Url
{
    private function __construct(
        private string $value,
    ) {
    }

    // Regular constructor is used when attribute not found
    #[Constructor]
    public static function create(string $url): self
    {
        if (filter_var($value, FILTER_VALIDATE_URL) === false) {
            throw MalformedUrl::fromString($value);
        }

        return new self($url)
    }

    #[ValueGetter]
    public function getValue(): string
    {
        return $this->value;
    }

    public function isSecure(): bool
    {
        return str_starts_with('https://', $this->value);
    }
}
```

Register types at the entry point of your application (kernel boot when using symfony):

```
// Get a PSR-6 cache instance
$cache = $this->container->get(CacheItemPoolInterface::class);

// Alternatively, use Doctrine's PSR-6 metadata cache
$entityManager = $this->container->get(EntityManagerInterface::class);
$cache = $entityManager->getConfiguration()->getMetadataCache();

// Create a type provider
$cachedTypeFinder = new CachedTypeDefinitionProvider(
    new DefaultTypeDefinitionProvider(__DIR__ . '/../ValueObject'),
    $cache
);

// Register dynamic types
(new DynamicTypeRegistry($cachedTypeFinder))->register();
```

Use the value object directly in your entities.

```
#[Entity]
class Company
{
    #[Column]
    private string $name;

    #[Column(type: Url::class)]
    private Url $url;
}
```

### Drivers

[](#drivers)

See `tests/Entity` for the example usage of the drivers. The library comes with two default drivers:

#### AttributeTypeDefinitionDriver

[](#attributetypedefinitiondriver)

This driver registers all classes with a `#[ValueGetter]` method as Doctrine types. If a static factory method is needed, add the `#[Constructor]` to the method which should be used for constructing the object back from database value.

#### InterfaceTypeDefinitionDriver

[](#interfacetypedefinitiondriver)

This driver registers all classes implementing `ValueObject` interface.

#### Custom driver

[](#custom-driver)

If you have existing value objects based on your project's conventions and do not want to add additional interfaces or custom attributes, you can implement your own driver and use it during type registration:

```
$typeDefinitionProvider = new CachedTypeDefinitionProvider(
    new ScanTypeDefinitionProvider($sourceFolder, [
        new CustomTypeDefinitionDriver(),
    ]),
    $cache,
);

(new DynamicTypeRegistry($typeDefinitionProvider))->register();
```

The possibilities are endless. You can even specify your own custom dynamic types in case you have special requirements like column length or database-specific optimizations. See `AttributeTypeDefinitionDriver` and `InterfaceTypeDefinitionDriver` for more examples.

```
class CustomTypeDefinitionDriver implements TypeDefinitionDriver
{
    /**
     * Should be the class treated as doctrine type?
     * @param ReflectionClass $class
     */
    public function supports(ReflectionClass $class): bool
    {
        return str_ends_with($class->getShortName(), 'Crate');
    }

    /**
     * Get dynamic type class. Whether it is value a string or int.
     * @param ReflectionClass $class
     *
     * @return class-string
     */
    public function getDynamicTypeClass(ReflectionClass $class): string
    {
        return match ($this->getValueMethodReturnType($class)) {
            'string' => StringDynamicType::class,
            'int' => IntegerDynamicType::class,
            default => throw new UnsupportedType('Only string|int type is supported.'),
        };
    }

    /**
     * Name of the method which should be used when persisting object to database.
     * @param ReflectionClass $class
     */
    public function getValueMethodName(ReflectionClass $class): string
    {
        return 'getValue';
    }

    /**
     * Method to use when creating the object from database value. Must have single argument.
     * When null returned, the regular constructor is used.
     */
    public function getConstructorMethodName(ReflectionClass $class): string|null
    {
        return $class->hasMethod('of') ? 'of' : null;
    }

    /**
     * Get value getter method return type to determine if database value should be string or int
     * @param ReflectionClass $class
     *
     * @throws UnsupportedType
     */
    private function getValueMethodReturnType(ReflectionClass $class): string
    {
        $returnType = $class->getMethod('getValue')->getReturnType();

        if (!$returnType instanceof ReflectionNamedType) {
            throw new UnsupportedType("Intersection or union return type not supported in method {$class->getName()}::getValue()");
        }

        if (!$returnType->isBuiltin()) {
            throw new UnsupportedType("Only scalar return types are supported in {$class->getName()}::getValue()");
        }

        return $returnType->getName();
    }
}
```

###  Health Score

31

—

LowBetter than 68% of packages

Maintenance42

Moderate activity, may be stable

Popularity19

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~18 days

Total

5

Last Release

481d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/03a0120fc880b0d44d457896546142bd1212d15e92d9e67d9d0272e5cf272705?d=identicon)[martingold](/maintainers/martingold)

---

Top Contributors

[![martingold](https://avatars.githubusercontent.com/u/15158789?v=4)](https://github.com/martingold "martingold (18 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/martingold-autotype/health.svg)

```
[![Health](https://phpackages.com/badges/martingold-autotype/health.svg)](https://phpackages.com/packages/martingold-autotype)
```

###  Alternatives

[doctrine/dbal

Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.

9.7k578.4M5.6k](/packages/doctrine-dbal)[martin-georgiev/postgresql-for-doctrine

Extends Doctrine with native PostgreSQL support for arrays, JSONB, ranges, PostGIS geometries, text search, ltree, uuid, and 100+ PostgreSQL-specific functions.

4485.3M4](/packages/martin-georgiev-postgresql-for-doctrine)[damienharper/auditor-bundle

Integrate auditor library in your Symfony projects.

4542.8M](/packages/damienharper-auditor-bundle)[sonata-project/entity-audit-bundle

Audit for Doctrine Entities

644989.8k1](/packages/sonata-project-entity-audit-bundle)[overtrue/laravel-versionable

Make Laravel model versionable.

585308.0k5](/packages/overtrue-laravel-versionable)[worksome/foggy

Foggy is a tool for making database dumps with some data removed/changed.

26571.7k1](/packages/worksome-foggy)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
