Entity
Primary key
The primary key is required in order to distinct results.
For now, only one primary key can be added.
Example :
<?php
/* File : Entity/CustomerEntity.php */
namespace MyApp\Entity;
use Small\SwooleEntityManager\Entity\AbstractEntity;
use Small\SwooleEntityManager\Entity\Attribute\PrimaryKey;
class CustomerEntity extends AbstractEntity
{
#[PrimaryKey]
public ?int $id = null;
}
Regular fields
You can define fields using Field attribute :
<?php
/* File : Entity/CustomerEntity.php */
namespace MyApp\Entity;
use Small\SwooleEntityManager\Entity\AbstractEntity;
use Small\SwooleEntityManager\Entity\Attribute\PrimaryKey;
use Small\SwooleEntityManager\Entity\Attribute\Field;
use Small\SwooleEntityManager\Entity\Enum\FieldValueType;
class CustomerEntity extends AbstractEntity
{
#[PrimaryKey]
public ?int $id = null;
#[Field(
type: FieldValueType::string,
)]
public ?string $companyName = null;
}
The property $companyName must be nullable.
Fields types
List of allowed types
Here are allowed types :
- string
- dateTime
- date
- boolean
- float
- int
- timestamp
- json
Datetime and date types
For dateTime and date types, we need to specify format to select which filter to apply :
<?php
/* File : Entity/CustomerEntity.php */
namespace MyApp\Entity;
use Small\SwooleEntityManager\Entity\AbstractEntity;
use Small\SwooleEntityManager\Entity\Attribute\PrimaryKey;
use Small\SwooleEntityManager\Entity\Attribute\Field;
use Small\SwooleEntityManager\Entity\Enum\FieldValueType;
class CustomerEntity extends AbstractEntity
{
#[PrimaryKey]
public ?int $id = null;
#[Field(
type: FieldValueType::string,
)]
public ?string $companyName = null;
#[Field(
type: FieldValueType::dateTime,
format: 'Y-m-d H:i:s'
)]
public ?DateTime $firstOrderDate;
#[Field(
type: FieldValueType::date,
format: 'Y-m-d'
)]
public ?DateTime $date;
}
In PHP, we will manipulate DateTime object as ORM will discuss to connection with the format specified.
Timestamp type
The timestamp type, work as dateTime but his format is always the number of seconds since 1970-01-01 :
<?php
/* File : Entity/CustomerEntity.php */
namespace MyApp\Entity;
use Small\SwooleEntityManager\Entity\AbstractEntity;
use Small\SwooleEntityManager\Entity\Attribute\PrimaryKey;
use Small\SwooleEntityManager\Entity\Attribute\Field;
use Small\SwooleEntityManager\Entity\Enum\FieldValueType;
class CustomerEntity extends AbstractEntity
{
#[PrimaryKey]
public ?int $id = null;
#[Field(
type: FieldValueType::string,
)]
public ?string $companyName = null;
#[Field(
type: FieldValueType::timestamp
)]
public ?DateTime $firstOrderDate;
}
In PHP, we will manipulate DateTime object as ORM will discuss to connection with the format specified.
Json type
The json type allow you to manipulate array and stdClass and will be stored in database as json string.
<?php
/* File : Entity/CustomerEntity.php */
namespace MyApp\Entity;
use Small\SwooleEntityManager\Entity\AbstractEntity;
use Small\SwooleEntityManager\Entity\Attribute\PrimaryKey;
use Small\SwooleEntityManager\Entity\Attribute\Field;
use Small\SwooleEntityManager\Entity\Enum\FieldValueType;
use Small\SwooleEntityManager\EntityManager\Enum\JsonFormatType;
class CustomerEntity extends AbstractEntity
{
#[PrimaryKey]
public ?int $id = null;
#[Field(
type: FieldValueType::json,
format: JsonFormatType::stdClass,
)]
public ?\stdClass $companyResults = null;
#[Field(
type: FieldValueType::json
format: JsonFormatType::array,
)]
public ?array $topTenBuyedProducts;
}
Default values
You can specify a default value for a field. On entity creation, this value will be set :
<?php
/* File : Entity/CustomerEntity.php */
namespace MyApp\Entity;
use Small\SwooleEntityManager\Entity\AbstractEntity;
use Small\SwooleEntityManager\Entity\Attribute\PrimaryKey;
use Small\SwooleEntityManager\Entity\Attribute\Field;
use Small\SwooleEntityManager\Entity\Enum\FieldValueType;
class CustomerEntity extends AbstractEntity
{
#[PrimaryKey]
public ?int $id = null;
#[Field(
type: FieldValueType::string,
default: 'unknown',
)]
public ?string $firstname;
#[Field(
type: FieldValueType::int,
default: 0,
)]
public ?int $points;
}
Accessors
To simplify our documentation, we have used public properties for entity fields.
It is possible to change this behaviour to private in order to implement more complex behaviours :
<?php
/* File : Entity/CustomerEntity.php */
namespace MyApp\Entity;
use Small\SwooleEntityManager\Entity\AbstractEntity;
use Small\SwooleEntityManager\Entity\Attribute\PrimaryKey;
use Small\SwooleEntityManager\Entity\Attribute\Field;
use Small\SwooleEntityManager\Entity\Enum\FieldValueType;
class CustomerEntity extends AbstractEntity
{
#[PrimaryKey]
private ?int $id = null;
#[Field(
type: FieldValueType::string,
default: 'unknown',
)]
private ?string $firstname;
#[Field(
type: FieldValueType::int,
default: 0,
)]
private ?int $points;
/**
* Get customer id
* @return int|null
*/
public function getId(): ?int
{
return $this->id;
}
/**
* Get customer firstname
* @return string|null
*/
public function getFirstname(): ?string {
return $this->firstname;
}
/**
* Set customer firstname
* @param string|null $firstname
* @return $this
*/
public function setFirstname(?string $firstname): self
{
$this->firstname = $firstname;
return $this;
}
/**
* Get customer points
* @return int
*/
public function getPoints(): int
{
return $this->points;
}
/**
* Add points
* @param int $num
* @return $this
* @throws \Exception
*/
public function addPoints(int $num): self
{
if ($num < 1) {
throw new \Exception(
'You can add only positive number to points !
');
}
$this->points += $num;
return $this;
}
}
Working with relations
To one relation
To illustrate to one relation, here is a user entity :
<?php
class UserEntity extends AbstractEntity
{
#[PrimaryKey]
private ?int $id;
#[Field(
type: FieldValueType::string,
format: null,
defaultValue: 'default'
)]
private ?string $username = null;
public function getId()
{
return $this->id;
}
public function getUsername()
{
return $this->username;
}
/**
* @param mixed $username
* @return UserEntity
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
}
And this project entity :
<?php
class ProjectEntity extends AbstractEntity
{
#[PrimaryKey]
private int|null $id;
#[Field(FieldValueType::string)]
protected string|null $projectName;
#[Field(FieldValueType::int)]
protected int|null $userId;
#[ToOne(
UserManager::class,
['userId' => 'id']
)]
protected UserEntity|null $user = null;
public function getId(): int|null
{
return $this->id;
}
public function getProjectName(): ?string
{
return $this->projectName;
}
public function setProjectName(?string $projectName): void
{
$this->projectName = $projectName;
}
public function getUserId(): ?int
{
return $this->userId;
}
public function setUserId(?int $userId): void
{
$this->userId = $userId;
}
public function getUser(): ?UserEntity
{
return $this->user;
}
public function setUser(?UserEntity $user): void
{
$this->user = $user;
}
}
The property user is declared WITH ToOne attribute :
- The first parameter indicate the manager of entity to link with
- The second parameter indicates that user can be found by linking property userId of project entity and id of entity user.
You can create a new project :
And load the dependence :
<?php
$project->loadToOne('user');
echo 'Project zero is assigned to user ' . $project->getUser()->getUsername();
Will output :
You can also get your project with the user already loaded (in same transaction with connection) using findyOneBy :
The Queries with join load all joined tables automatically (see new chapter "Query builder" for more details) :
<?php
$query = $projectManager->createQueryBuilder('project');
->innerJoin('project', 'user')->endJoin();
$query->where()->firstCondition(
$query->getFieldForCondition('id'),
ConditionOperatorType::equal,
1
);
$projects = $projectManager->getResult($query);
if ($projects->offsetExists(0)) {
echo 'Project zero is assigned to user ' .
$project->getUser()->getUsername();
} else {
echo 'Project zero not found';
}
To many relation
As to one relation, we can define to many relation.
Here is updated userEntity implementing projects relation :
<?php
class UserEntity extends AbstractEntity
{
#[PrimaryKey]
private ?int $id;
#[Field(
type: FieldValueType::string,
format: null,
defaultValue: 'default'
)]
private ?string $username = null;
#[ToMany(ProjectManager::class, ['id' => 'userId'])]
private ?ProjectCollection $projects = null;
public function getId()
{
return $this->id;
}
public function getUsername()
{
return $this->username;
}
/**
* @param mixed $username
* @return UserEntity
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
public function getProjects() {
return $this->projects;
}
}
As to one relation, parameters of ToMany attribute are :
- The first parameter indicate the manager of entity to link with
- The second parameter indicates that user can be found by linking property userId of project entity and id of entity user.
For example, if we want to delete all nadia's projects :
See next chapter "Entity collections" for more details on how to use collections.
Behaviour interfaces
Several interfaces allow you to implement different behaviours when entity is manipulated by manager.
These interfaces are :
- Small\SwooleEntityManager\Entity\Contract\OnNewInterface
- Small\SwooleEntityManager\Entity\Contract\OnLoadInterface
- Small\SwooleEntityManager\Entity\Contract\BeforePersistInterface
- Small\SwooleEntityManager\Entity\Contract\BeforeInsertInterface
- Small\SwooleEntityManager\Entity\Contract\BeforeUpdateInterface
- Small\SwooleEntityManager\Entity\Contract\BeforeDeleteInterface
- Small\SwooleEntityManager\Entity\Contract\AfterPersistInterface
- Small\SwooleEntityManager\Entity\Contract\AfterInsertInterface
- Small\SwooleEntityManager\Entity\Contract\AfterUpdateInterface
- Small\SwooleEntityManager\Entity\Contract\AfterDeleteInterface
OnNewInterface
This interface allow you to implement a method onNew called each time an entity is created by newEntity manager method.
<?php
class UserEntity extends AbstractEntity
implements OnNewInterface
{
#[PrimaryKey]
private ?int $id;
#[Field(
type: FieldValueType::string,
defaultValue: 'default'
)]
private ?string $username = null;
#[Field(FieldValueType::string)]
private ?string $code = null;
#[Field(FieldValueType::string)]
private ?string $code = null;
public onNew(): void
{
$this->code = md5(microtime());
}
public function getId()
{
return $this->id;
}
public getCode(): ?string
{
return $this->code;
}
public function getUsername()
{
return $this->username;
}
/**
* @param mixed $username
* @return UserEntity
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
}
OnLoadInterface
This interface allow you to implement a method onLoad called each time an entity is loaded from database (findOneBy, findBy, queries) by manager.
<?php
class UserEntity extends AbstractEntity
implements OnLoadInterface
{
#[PrimaryKey]
public ?int $id;
#[Field(
type: FieldValueType::string,
)]
public ?string $firstname = null;
#[Field(
type: FieldValueType::string,
)]
public ?string $lastname = null;
public ?string $fullname = null;
public onLoad(): void
{
$this->fullname = $this->firstname . ' ' . $this->lastname;
}
}
BeforePersistInterface and AfterPersistInterface
These interface allow you to implements :
- beforePersist method called just before writing entity to connection
- afterPersist method called just after writing entity to connection
For example :
<?php
class UserEntity extends AbstractEntity
implements BeforePersistInterface, AfterPersistInterface
{
#[PrimaryKey]
public ?int $id;
#[Field(FieldValueType::string)]
public ?string $username = null;
#[ToMany(ProjectManager::class, ['id' => 'userId'])]
public ?ProjectCollection $projects = null;
publc function beforePersist(): void
{
// Username can't be null in database
if ($this->username === null) {
$this->username = '';
}
}
public function afterPersist(): void
{
// Persist in cascade
if ($this->projects !== null) {
$this->projects->persist();
}
}
}
BeforeInsertInterface and AfterInsertInterface
These interface allow you to implements :
- beforeInsert method called just before writing new entity to connection
- afterInsert method called just after writing new entity to connection
For example :
<?php
class UserEntity extends AbstractEntity
implements BeforeInsertInterface, AfterInsertInterface
{
public static int $numUsers = 0;
#[PrimaryKey]
public ?int $id;
#[Field(FieldValueType::string)]
public ?string $username = null;
#[ToMany(ProjectManager::class, ['id' => 'userId'])]
public ?ProjectCollection $projects = null;
publc function beforeInsert(): void
{
EventDispatcher::dispatch(
new UserCreatedEvent($this);
);
}
public function afterInsert(): void
{
self::numUsers++;
}
}
BeforeUpdateInterface and AfterUpdateInterface
These interface allow you to implements :
- beforeUpdate method called just before updating entity to connection
- afterUpdate method called just after updating entity to connection
For example :
<?php
class UserEntity extends AbstractEntity
implements BeforeUpdateInterface, AfterUpdateInterface
{
public static int $numUsers = 0;
#[PrimaryKey]
public ?int $id;
#[Field(FieldValueType::string)]
public ?string $username = null;
#[Field(FieldValueType::dateTime)]
public ?\DateTime $updatedAt = null;
#[ToMany(ProjectManager::class, ['id' => 'userId'])]
public ?ProjectCollection $projects = null;
publc function beforeUpdate(): void
{
$this->updatedAt = new \DateTime();
}
public function afterUpdate(): void
{
EventDispatcher::dispatch(
new UserUpdatedEvent($this);
);
}
}
BeforeDeleteInterface and AfterDeleteInterface
These interface allow you to implements :
- beforeDelete method called just before deleting entity to connection
- afterDelete method called just after deleting new entity to connection
For example :
<?php
class UserEntity extends AbstractEntity
implements BeforeDeleteInterface, AfterDeleteInterface
{
public static int $numUsers = 0;
#[PrimaryKey]
public ?int $id;
#[Field(FieldValueType::string)]
public ?string $username = null;
#[Field(FieldValueType::dateTime)]
public ?\DateTime $updatedAt = null;
#[ToMany(ProjectManager::class, ['id' => 'userId'])]
public ?ProjectCollection $projects = null;
publc function beforeDelete(): void
{
// Delete in cascade
if ($this->projects !== null) {
$this->projects->delete();
}
}
public function afterDelete(): void
{
self::numUsers--;
}
}
Example of behaviour though trait
For example of behaviour implementation, here is a Timestampable trait :
<?php
trait Timestampable
implements BeforeInsertInterface, BeforeUpdateInterface
{
#[Field(FieldValueType::dateTime)]
protected ?\DateTimeImmutable $createdAt = null;
#[Field(FieldValueType::dateTime)]
protected ?\DateTimeImmutable $updatedAt = null;
public function getCreatedAt(): ?\Datetime
{
return $this->createdAt;
}
public function getUpdatedAt(): ?\Datetime
{
return $this->updatedAt;
}
public function beforeInsert()
{
$this->createdAt = new \DateTime();
}
public function beforeUpdate() {
{
$this->updatedAt = new \DateTime();
}
}
And to implement it on our user :