Embeddable ऑब्जेक्ट्स
एक embeddable (DDD terminology में value object भी कहलाता है) एक PHP क्लास है जिसकी properties parent एंटिटी की टेबल में कॉलम के रूप में स्टोर की जाती हैं — कोई join नहीं, कोई अलग टेबल नहीं। Weaver एक समर्पित AbstractEmbeddableMapper के माध्यम से कॉलम मैपिंग और prefix लॉजिक प्रबंधित करता है।
Embeddables क्या समस्या हल करते हैं
billing और shipping addresses के साथ एक Customer पर विचार करें। आप सभी address कॉलम सीधे CustomerMapper में जोड़ सकते हैं, लेकिन यह address लॉजिक को बिखेर देता है। या आप addresses को एक अलग टेबल में normalize कर सकते हैं, लेकिन यह हर customer क्वेरी के लिए एक join जोड़ता है।
Embeddables आपको एक तीसरा विकल्प देते हैं: address कॉलम को एक पुन: उपयोग योग्य value object (Address) में group करें जो parent टेबल में transparently flatten हो जाता है।
customers
─────────────────────────────────────────
id
name
billing_street ← Address से
billing_city ← Address से
billing_country ← Address से
billing_postal_code ← Address से
shipping_street ← Address से (अलग prefix के साथ पुन: उपयोग)
shipping_city
shipping_country
shipping_postal_code
एक embeddable क्लास परिभाषित करना
Embeddable क्लास ORM निर्भरताओं के बिना एक सामान्य PHP ऑब्जेक्ट है:
<?php
// src/ValueObject/Address.php
declare(strict_types=1);
namespace App\ValueObject;
final class Address
{
public function __construct(
public readonly string $street,
public readonly string $city,
public readonly string $country,
public readonly ?string $postalCode = null,
) {}
public function withCity(string $city): self
{
return new self(
street: $this->street,
city: $city,
country: $this->country,
postalCode: $this->postalCode,
);
}
}
एक EmbeddableMapper परिभाषित करना
Weaver\ORM\Mapping\AbstractEmbeddableMapper को एक्सटेंड करने वाली एक क्लास बनाएं:
<?php
// src/Mapper/AddressMapper.php
declare(strict_types=1);
namespace App\Mapper;
use App\ValueObject\Address;
use Weaver\ORM\Mapping\AbstractEmbeddableMapper;
use Weaver\ORM\Mapping\ColumnDefinition;
/** @extends AbstractEmbeddableMapper<Address> */
final class AddressMapper extends AbstractEmbeddableMapper
{
public function columns(): array
{
return [
ColumnDefinition::string('street', 200)->notNull(),
ColumnDefinition::string('city', 100)->notNull(),
ColumnDefinition::string('country', 3)->notNull(),
ColumnDefinition::string('postal_code', 20)->nullable(),
];
}
public function embeddableClass(): string
{
return Address::class;
}
public function hydrate(array $row): Address
{
return new Address(
street: $row['street'],
city: $row['city'],
country: $row['country'],
postalCode: $row['postal_code'] ?? null,
);
}
public function extract(Address $embeddable): array
{
return [
'street' => $embeddable->street,
'city' => $embeddable->city,
'country' => $embeddable->country,
'postal_code' => $embeddable->postalCode,
];
}
}
एंटिटी मैपर में Embedding
Parent मैपर के columns() रिटर्न वैल्यू के अंदर EmbedMap::embed() का उपयोग करें:
<?php
// src/Mapper/CustomerMapper.php
declare(strict_types=1);
namespace App\Mapper;
use App\Entity\Customer;
use Weaver\ORM\Mapping\AbstractMapper;
use Weaver\ORM\Mapping\ColumnDefinition;
use Weaver\ORM\Mapping\EmbedMap;
use Weaver\ORM\Mapping\SchemaDefinition;
final class CustomerMapper extends AbstractMapper
{
public function __construct(
private readonly AddressMapper $addressMapper,
) {}
public function table(): string
{
return 'customers';
}
public function primaryKey(): string
{
return 'id';
}
public function schema(): SchemaDefinition
{
return SchemaDefinition::define(
ColumnDefinition::integer('id')->autoIncrement()->unsigned(),
ColumnDefinition::string('name', 120)->notNull(),
// Address कॉलम को अलग-अलग prefixes के साथ दो बार embed करें
EmbedMap::embed($this->addressMapper, prefix: 'billing_'),
EmbedMap::embed($this->addressMapper, prefix: 'shipping_'),
);
}
public function hydrate(array $row): Customer
{
return new Customer(
id: (int) $row['id'],
name: $row['name'],
billingAddress: $this->addressMapper->hydrateWithPrefix($row, 'billing_'),
shippingAddress: $this->addressMapper->hydrateWithPrefix($row, 'shipping_'),
);
}
public function dehydrate(object $entity): array
{
/** @var Customer $entity */
$data = [
'id' => $entity->id,
'name' => $entity->name,
];
foreach ($this->addressMapper->extractWithPrefix($entity->billingAddress, 'billing_') as $col => $val) {
$data[$col] = $val;
}
foreach ($this->addressMapper->extractWithPrefix($entity->shippingAddress, 'shipping_') as $col => $val) {
$data[$col] = $val;
}
return $data;
}
}