PHP code example of andanteproject / nullable-embeddable-bundle

1. Go to this page and download the library: Download andanteproject/nullable-embeddable-bundle library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

andanteproject / nullable-embeddable-bundle example snippets



// ... use statements
use Andante\NullableEmbeddableBundle\Attribute\NullableEmbeddable;
use Andante\NullableEmbeddableBundle\PropertyAccess\PropertyAccessor;
use Andante\NullableEmbeddableBundle\Result;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Embeddable]
#[NullableEmbeddable(processor: static function (PropertyAccessor $propertyAccessor, object $embeddableObject): Result {
    // We check if the 'code' property is uninitialized.
    if ($propertyAccessor->isUninitialized($embeddableObject, 'code')) {
        return Result::SHOULD_BE_NULL;
    }
    return Result::KEEP_INITIALIZED;
})]
class Country
{
    public function __construct(
        #[ORM\Column(type: Types::STRING, length: 2, nullable: true)]
        private string $code,
    ) {
    }
    // ... getters and setters
}

return [
    // ...
    Andante\NullableEmbeddableBundle\AndanteNullableEmbeddableBundle::class => ['all' => true],
    // ...
];



declare(strict_types=1);

namespace Andante\NullableEmbeddableBundle;

use Andante\NullableEmbeddableBundle\Exception\UnexpectedEmbeddableClassException;
use Andante\NullableEmbeddableBundle\PropertyAccess\PropertyAccessor;
use Symfony\Component\PropertyAccess\PropertyPathInterface;

interface ProcessorInterface
{
    /**
     * @throws UnexpectedEmbeddableClassException
     */
    public function analyze(PropertyAccessor $propertyAccessor, object $embeddableObject, PropertyPathInterface $propertyPath, object $rootEntity, mixed $embeddedConfig): Result;
}



declare(strict_types=1);

namespace App\Entity;

use Andante\NullableEmbeddableBundle\Attribute\NullableEmbeddable;
use Doctrine\ORM\Mapping as ORM;
use App\Processor\AddressEmbeddableProcessor; // Your custom processor

#[ORM\Embeddable]
#[NullableEmbeddable(processor: AddressEmbeddableProcessor::class)]
class Address
{
    // ... properties, getters, setters
}



declare(strict_types=1);

namespace App\Processor;

use Andante\NullableEmbeddableBundle\ProcessorInterface;
use Andante\NullableEmbeddableBundle\PropertyAccess\PropertyAccessor;
use Andante\NullableEmbeddableBundle\Result;
use Andante\NullableEmbeddableBundle\Exception\UnexpectedEmbeddableClassException;
use App\Entity\Address;
use Symfony\Component\PropertyAccess\PropertyPathInterface;

class AddressEmbeddableProcessor implements ProcessorInterface
{
    public function analyze(PropertyAccessor $propertyAccessor, object $embeddableObject, PropertyPathInterface $propertyPath, object $rootEntity, mixed $embeddedConfig): Result
    {
        if (!$embeddableObject instanceof Address) {
            throw UnexpectedEmbeddableClassException::create(Address::class, $embeddableObject);
        }

        if (
            null === $propertyAccessor->getValue($embeddableObject, 'street')
            && null === $propertyAccessor->getValue($embeddableObject, 'city')
            && null === $propertyAccessor->getValue($embeddableObject, 'country')
        ) {
            return Result::SHOULD_BE_NULL;
        }

        return Result::KEEP_INITIALIZED;
    }
}



declare(strict_types=1);

namespace App\Entity;

use Andante\NullableEmbeddableBundle\Attribute\NullableEmbeddable;
use Andante\NullableEmbeddableBundle\Exception\UnexpectedEmbeddableClassException;
use Andante\NullableEmbeddableBundle\PropertyAccess\PropertyAccessor;
use Andante\NullableEmbeddableBundle\Result;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Embeddable]
#[NullableEmbeddable(processor: static function (PropertyAccessor $propertyAccessor, object $embeddableObject): Result {
    if (!$embeddableObject instanceof Address) {
        throw UnexpectedEmbeddableClassException::create(Address::class, $embeddableObject);
    }
    if (
        null === $propertyAccessor->getValue($embeddableObject, 'street')
        && null === $propertyAccessor->getValue($embeddableObject, 'city')
        && null === $propertyAccessor->getValue($embeddableObject, 'country')
    ) {
        return Result::SHOULD_BE_NULL;
    }

    return Result::KEEP_INITIALIZED;
})]
class Address
{
    #[ORM\Column(type: Types::STRING, nullable: true)]
    private ?string $street = null;

    #[ORM\Column(type: Types::STRING, nullable: true)]
    private ?string $city = null;

    #[ORM\Embedded(class: Country::class, columnPrefix: 'country_')]
    private ?Country $country = null;

    // ... getters and setters
}



declare(strict_types=1);

namespace App\Entity;

use Andante\NullableEmbeddableBundle\Attribute\NullableEmbeddable;
use Andante\NullableEmbeddableBundle\Exception\UnexpectedEmbeddableClassException;
use Andante\NullableEmbeddableBundle\PropertyAccess\PropertyAccessor;
use Andante\NullableEmbeddableBundle\Result;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Embeddable]
#[NullableEmbeddable(processor: static function (PropertyAccessor $propertyAccessor, object $embeddableObject): Result {
    if (!$embeddableObject instanceof Country) {
        throw UnexpectedEmbeddableClassException::create(Country::class, $embeddableObject);
    }

    if ($propertyAccessor->isUninitialized($embeddableObject, 'code')) {
        return Result::SHOULD_BE_NULL;
    }

    return Result::KEEP_INITIALIZED;
})]
class Country
{
    #[ORM\Column(type: Types::STRING, length: 255, nullable: true)]
    private ?string $name = null;

    public function __construct(
        #[ORM\Column(type: Types::STRING, length: 2, nullable: true)]
        private string $code,
    ) {
    }

    // ... getters and setters
}

   // BAD - Don't do this:
   private bool $enabled = true;  // Doctrine gets NULL from DB but property shows true

   // GOOD - Do this instead:
   public function __construct(
       private bool $enabled = true,
   ) {}
   

#[ORM\Embeddable]
#[NullableEmbeddable(processor: /* ... */)]
class Address
{
    // ERROR: Property has non-null default outside constructor
    // private bool $isPrimary = false;

    // ERROR: Column is not nullable (neither PHP type nor explicit attribute)
    // #[ORM\Column(type: Types::STRING)]
    // private string $street;

    // CORRECT: Column is nullable via PHP type (Doctrine infers nullable: true)
    #[ORM\Column(type: Types::STRING)]
    private ?string $street = null;

    // ALSO CORRECT: Column explicitly nullable (even with non-nullable PHP type)
    #[ORM\Column(type: Types::STRING, nullable: true)]
    private string $city;

    // CORRECT: Uninitialized embedded property (will remain uninitialized when the parent is null)
    #[ORM\Embedded(class: Country::class)]
    private Country $country;

    // ALSO CORRECT: Embedded property initialized to null
    #[ORM\Embedded(class: Region::class)]
    private ?Region $region = null;

    // CORRECT: Default value in constructor
    public function __construct(
        #[ORM\Column(type: Types::BOOLEAN, nullable: true)]
        private bool $isPrimary = false,
    ) {}
}


use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    if ('prod' === $containerConfigurator->env()) {
        $containerConfigurator->extension('andante_nullable_embeddable', [
            'metadata_cache_warmer_enabled' => true,
        ]);
    }
};