Skip to content

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 :

<?php
$project = ($projectManager->newEntity())
    ->setProjectName('project zero')
    ->setUserId(1)
;

And load the dependence :

<?php
$project->loadToOne('user');

echo 'Project zero is assigned to user ' . $project->getUser()->getUsername();

Will output :

Project zeo is assigned to user nadia

You can also get your project with the user already loaded (in same transaction with connection) using findyOneBy :

$project = $projectManager->findOneBy(['id' => 1], [['user']]);

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 :

<?php
$userManager->findOneBy(['username' => 'nadia'], [['projects']])
    ->getProjects()
    ->delete()
;

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 :

<?php
class UserEntity extends AbstractEntity
{

    use Timestampable;

    #[PrimaryKey]
    public ?int $id;

    #[Field(FieldValueType::string)]
    public ?string $username = null;

}

Next chapter : Entity collections