Configuring an Entity
This section describes the configuration options for entities.
Id
If nothing else is defined, the property named "id" is assumed to represent the Id (Primary Key) and the value of the associated column is automatically generated by the database (in MySQL AUTO_INCREMENT). You can also define a different property as the Id by annotating it with n2n\persistence\orm\annotation\AnnoId.
class User extends EntityAdapter {
#[Id]
public string $name;
public string $password;
}
Using the parameters of AnnoId you can additionally tell the ORM whether the value is generated by the database or by the application, and whether a sequence is required to generate this value (e.g. in Oracle).
Table and Column Names
If nothing further is configured, by default class and property names are "hyphenated" and these are used as table and column names. For an entity with the class name BlogArticle, a table blog_article is looked up, and for a property $orderIndex a column order_index. You can also define the table and column names directly in the entity using the annotations n2n\persistence\orm\annotation\AnnoTable and n2n\persistence\orm\annotation\AnnoColumn.
#[Table('bloguser')]
class BlogUser extends EntityAdapter {
public int $id;
#[Column('firstname')]
public string $firstName;
public string $lastName;
}
For the entity User in the example above, the following table would be appropriate:
CREATE TABLE `bloguser` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`firstname` VARCHAR(50) NULL DEFAULT NULL,
`last_name` VARCHAR(50) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) COLLATE='utf8_general_ci' ENGINE=InnoDB;
Naming Strategy
You can override the algorithm for generating table and column names by defining your own "Naming Strategy". To do this, you must create a class that implements n2n\persistence\orm\model\ CustomNamingStrategy and register it in the app.ini under the group [orm].
[orm]
naming_strategy = \"atusch\config\CustomNamingStrategy\"
You can also use one of the implementations provided by n2n:
n2n\persistence\orm\model\HyphenatedNamingStrategy(Default)n2n\persistence\orm\model\IdenticalNamingStrategyn2n\persistence\orm\model\LowercasedNamingStrategy
Using the annotation n2n\persistence\orm\annotation\AnnoNamingStrategy you can also override the "Naming Strategy" for individual entities.
class User extends EntityAdapter {
private static function _anno(AnnoInit $ai) {
$ai->c(new AnnoNamingStrategy(new LowercasedNamingStrategy()));
}
// class body
}
Caution: If you define your own "Naming Strategy" in the app.ini, this affects the entities of all installed modules. This can cause modules, for example those installed via the store, to no longer find their tables and columns. It is therefore recommended to override the "Naming Strategy" only for your own entities (via AnnoNamingStrategy).
Mapped Supperclass
You can outsource properties into so-called "Mapped Superclasses", which can be extended by entities. The properties are stored in the table of the entity as if they were defined in it itself. "Mapped Superclasses" must be annotated with n2n\persistence\orm\attribute\MappedSuperclass. It is not necessary to register them in the app.ini.
#[MappedSuperclass]
class Teaser extends ObjectAdapter {
public string $title;
public string $description;
}
Teaser can now be extended by any entities.
class News extends Teaser {
public int $id;
public string $text;
}
The following table fits the entity News:
CREATE TABLE IF NOT EXISTS `news` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`title` VARCHAR(50),
`description` VARCHAR(255),
`text` TEXT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Embedded
You can also outsource properties into a separate class and reference their objects via a property. Such properties must be annotated with n2n\persistence\orm\annotation\AnnoEmbedded.
Let's assume we need an entity Order to record orders in a shop. The order should contain an invoice address and, if desired, a separate delivery address. This means we need properties like $forename and $surname for both the invoice and the delivery address. Instead of declaring these address properties twice, it is easier to outsource them into a separate class Address and declare two "embedded" properties of type Address in the entity Order. Address could later also be used by other entities to outsource address properties. Separate classes do not need to be registered in the app.ini.
class Order extends ObjectAdapter {
public int $id;
public string $productName;
#[Embedded(Address::class)]
public Address $address;
#[Embedded(Address::class), 'delivery_']
public Address $deliveryAddress;
public function hasDeliveryAddress() {
return $this->deliveryAddress !== null;
}
}
So that the properties of the separate class Address for the invoice and delivery address do not use the same columns in the "order" table, AnnoEmbedded offers the possibility via the second and third parameter to define a prefix as well as a suffix for the columns of the respective separate class.
Note: The specified prefix and suffix is also used for the names of the join columns and join tables for Relationship properties.
AnnoEmbedded expects as its first parameter the ReflectionClass of the respective separate class, which is why it also makes sense here for the separate class to extend n2n\reflection\ObjectAdapter.
class Address extends ObjectAdapter {
public string $forename;
public string $surname;
// more properties
public function __construct($forename = null, $surname = null) {
$this->forename = $forename;
$this->surname = $surname;
}
}
The following table fits the entity Order:
CREATE TABLE `order` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`product_name` VARCHAR(50) NOT NULL,
`forename` VARCHAR(50) NOT NULL,
`surname` VARCHAR(50) NOT NULL,
`delivery_forename` VARCHAR(50) DEFAULT NULL,
`delivery_surname` VARCHAR(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
If Order::$deliveryAddress is null when saving, NULL is written to the two fields "delivery_forename" and "delivery_surname". Likewise, Order::$deliveryAddress will contain null when reading if the two fields "delivery_forename" and "delivery_surname" are NULL.
In the following example we create one order with and one without a delivery address:
public function doOrm(EntityManager $em) {
$this->beginTransaction();
$order = new Order();
$order->setProductName('Broccoli');
$order->setAddress(new Address('Peter', 'Awesome'));
$order->setDeliveryAddress(new Address('Amanda', 'Pretty'));
$em->persist($order);
$order = new Order();
$order->setProductName('ZSC Playoff Tickets');
$order->setAddress(new Address('Peter', 'Awesome'));
$em->persist($order);
$this->commit();
}
The example above writes the following two rows into the "order" table:
| id | product_name | forename | surname | delivery_forename | delivery_surname |
|---|---|---|---|---|---|
| 1 | Broccoli | Peter | Awesome | Amanda | Pretty |
| 2 | ZSC Playoff Tickets | Peter | Awesome | NULL | NULL |
Queries
The following examples show how you can use "embedded" properties in queries.
Example 1
Criteria API
$criteria = $em->createSimpleCriteria(Order::getClass(), ['address.forename' => 'Peter']);
NQL
$criteria = $em->createNqlCriteria('SELECT o FROM \"Order\" o WHERE o.address.forename = :forename', ['forename' => 'Peter']);
Example 2
Criteria API
$address = new Address('Peter', 'Awesome');
$criteria = $em->createSimpleCriteria(Order::getClass(), ['address' => $address]);
NQL
$address = new Address('Peter', 'Awesome');
$criteria = $em->createNqlCriteria('SELECT o FROM \"Order\" o WHERE o.address = :address', ['address' => $address]);
Example 3
Criteria API
$criteria = $em->createSimpleCriteria(Order::getClass(), ['deliveryAddress' => null]);
NQL
$criteria = $em->createNqlCriteria('SELECT o FROM \"Order\" o WHERE o.deliveryAddress = :deliveryAddress', ['deliveryAddress' => null]);
Attribute Overrides
Using the annotation n2n\persistence\orm\attribute\AttributeOverrides you can override the associated column names for all properties of an entity. AttributeOverrides is therefore an alternative to n2n\persistence\orm\annotation\AnnoColumn. AttributeOverrides also allows you to override the associated column names of the properties of a Mapped Supperclass.
#[AttributeOverrides([
'id' => 'news_id',
'title' => 'news_title',
'description' => 'news_description',
'text' => 'news_text'
])]
class News extends Teaser {
public int $id;
public string $text;
}
In this example we adapted the example from the section Mapped Supperclass and overrode all column names of the entity News and its Mapped Supperclass Teaser via AttributeOverrides.
The table of News now looks as follows:
CREATE TABLE `news` (
`news_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`news_title` VARCHAR(50),
`news_description` VARCHAR(255),
`news_text` TEXT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
You can also apply AttributeOverrides to separate classes by annotating the corresponding Embedded properties.
class Order extends ObjectAdapter {
public int $id;
public string $productName;
#[Embedded(Address::class)]
#[AttributeOverrides(['forename' => 'bill_forename',
'surname' => 'bill_surname'])]
public string $address;
#[Embedded(Address::class, 'delivery_')]
public string $deliveryAddress;
}
Here we adapted the example from the section Embedded and overrode the column names of the separate class Address via AttributeOverrides.
The table of Order now looks as follows:
CREATE TABLE `order` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`product_name` VARCHAR(50) NOT NULL,
`bill_forename` VARCHAR(50) NOT NULL,
`bill_surname` VARCHAR(50) NOT NULL,
`delivery_forename` VARCHAR(50) DEFAULT NULL,
`delivery_surname` VARCHAR(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;