Edit

Embeddables

Embeddables are not entities themselves, but rather wrap few properties and are embedded in entities. Using embaddables helps to separate concerns, reuse code, and design more secure architecture since embeddables are immutable by design.

Embeddable wraps properties to own data class. The embeddable's nullability and its properties' nullability is derived from PHPDoc types. Embeddable has to inherit from Nextras\Orm\Entity\Embeddable\Embeddable abstract class. Properties of embeddable are defined the same way as for entity.

Initializing an embeddable object uses constructor which accepts an array with keys as property names and appropriate values. However, you can override constructor or add other methods to provide more convenient way of creating an embeddable object.

In the following example we join multiple address fields to one unified (and reusable) address data class. If all address properties are null, Orm does not instantiate embeddable, but put null into $address.

/**
 * @property Address|null $address {embeddable}
 */
class User extends Nextras\Orm\Entity\Entity
{
}

/**
 * @property string $street
 * @property string $city
 * @property string|null $state
 * @property string $country
 * @property string $zipCode
 */
class Address extends Nextras\Orm\Entity\Embeddable
{
    public function __construct(string $street, string $city, ?string $state, string $country, string $zipCode)
    {
        parent::__construct([
            'street' => $street,
            'city' => $city,
            'state' => $state,
            'country' => $country,
            'zipCode' => $zipCode,
        ]);
    }
}

The example by default store data in address_street column, etc. You may also filter by the nested structure. This works for both array and dbal collection.

$users = $orm->users->findBy(['address->city' => 'Prague']);

Setting value require creating new embeddable instance by yourself. If you want change its property, create a new one and set it again.

$user = new User();
$user->address = new Address('Main st.', 'Prague', null, 'Czechia', '10000');
echo $user->address->street;

Conventions#

As always, you may want to change the default embeddable mapping to db colum names. Just use -> as nested separator for entity property name and use the usual API.

protected function createConventions(): IConventions
{
    $conventions = parent::createConventions();
    $conventions->setMapping('address->zipCode', 'address_postal_code');
    return $conventions;
}

Currently, there is no easy way to change prefix for all embeddable properties, just enumerate all the properties with a new column name.

Use $embeddableSeparatorPattern to change the default separator between holding & nested property.

protected function createConventions()
{
    $conventions = parent::createConventions();
    $conventions->embeddableSeparatorPattern = '__';
    return $conventions;
}

The example will generate address__street, etc.