Database

Assegai can be used with any type of database, whether it is SQL or NoSQL. This flexibility allows you to choose the database that best fits your needs. To connect Assegai to a database, you will need the PHP PDO extension installed and enabled along with any PHP drivers that are compatible with the database you have chosen. Once the extension is installed, you will be able to easily integrate Assegai with your database.

ORM integration

Assegai includes the assegaiphp/orm package to facilitate integration with both SQL and NoSQL databases.

$ composer require assegaiphp/orm ext-pdo

After completing the installation process, the default ORM data source can be configured in the root AppModule.

config/default.php
return [
  ...
  'databases' => [
    'mysql' => [
      'test' => [
        'host' => 'localhost',
        'user' => 'root',
        'password' => 'root',
        'port' => 3306,
      ],
    ],
  ],
  ...
];
src/AppModule.php
use Assegai\Core\Attributes\Modules\Module;

#[Module(
  providers: [AppService::class],
  controllers: [AppController::class],
  imports: [],
  config: [
    'data_source' => 'test'
  ]
)]
class AppModule
{
}

After completing this step, the ORM DataSource and EntityManager objects will be able to be injected into any part of the project without the need to import any additional modules, for example:

src/AppModule.php
use Assegai\Core\Attributes\Modules\Module;
use Assegai\Orm\DataSource\DataSource;

#[Module(
  providers: [AppService::class],
  controllers: [AppController::class],
  imports: [],
  config: [
    'data_source' => 'test'
  ]
)]
class AppModule
{
  public function __construct(private DataSource $dataSource)
  {
  }
}

Repository pattern

The Assegai ORM implements the repository design pattern, which means that each entity has a dedicated repository that can be accessed through the database data source.

To continue the example, we need to define at least one entity. In this case, we will create a User entity.

src/Users/Entities/UserEntity.php
use Assegai\Orm\Attributes\Columns\Column;
use Assegai\Orm\Attributes\Columns\EmailColumn;
use Assegai\Orm\Attributes\Columns\PrimaryGeneratedColumn;
use Assegai\Orm\Attributes\Entity;
use Assegai\Orm\Queries\Sql\ColumnType;

#[Entity(table: 'users')]
class UserEntity
{
  #[PrimaryGeneratedColumn]
  public int $id = 0;

  #[EmailColumn]
  public string $email = '';

  #[Column]
  public string $firstName = '';

  #[Column]
  public string $lastName = '';

  #[Column(type: ColumnType::BOOLEAN, default: false)]
  public bool $isVerified = false;
}
Tip Find out more about entities in the ORM documentation

The UserEntity file is located in the users directory, which contains all files related to the UsersModule. While you can choose where to store your model files, it is generally recommended to place them in the corresponding module directory to keep them organized by domain.

Now, let's examine the UsersModule

src/Users/UsersModule.php
use Assegai\Core\Attributes\Modules\Module;

#[Module(
  providers: [UsersService::class],
  controllers: [UsersController::class],
  imports: [],
  exports: [],
)]
class UsersModule {}

Next, we can use the #[InjectRepository] attribute to inject the UsersRepository into the UsersService:

src/Users/UsersService.php
use Assegai\App\Users\Dto\CreateUserDto;
use Assegai\App\Users\Dto\UpdateUserDto;
use Assegai\App\Users\Entities\UserEntity;
use Assegai\Core\Attributes\Injectable;
use Assegai\Orm\Attributes\InjectRepository;
use Assegai\Orm\Management\Repository;

#[Injectable]
class UsersService
{
  public function __construct(
    #[InjectRepository(UserEntity::class)]
    private readonly Repository $usersRepository
  )
  {}

  function create(CreateUserDto $createUserDto)
  {
    $user = $this->usersRepository->create($createUserDto);
    $results = $this->usersRepository->save($user);

    return $this->findOne($results->id);
  }

  function findAll()
  {
    return $this->usersRepository->find();
  }

  function findOne(int $id)
  {
    return $this->usersRepository->findOne([
      'where' => ['id' => $id]
    ]);
  }

  function update(int $id, UpdateUserDto $updateUserDto)
  {
    $results = $this->usersRepository->update(['id' => $id], $updateUserDto);
    return $this->findOne($id);
  }

  function remove(int $id)
  {
    $results = $this->usersRepository->delete(['id' => $id]);
    return ['ids' => $id];
  }
Notice Remember to import the UsersModule into the root AppModule

Relations

Assegai ORM supports relationships between entities. To define relations in entities you can use the following attributes:

#[OneToOne] Every row in the primary table has one and only one associated row in the foreign table. Use the #[OneToOne] attribute to define this type of relation.
#[OneToMany] Defines a one-to-many relationship between two entities. This means that a single row in the primary table can be associated with multiple rows in the foreign table. Use the #[OneToMany] attribute to define this type of relation.
#[ManyToOne] Every entity has a many-to-one relationship with another entity. This means that multiple rows in the primary table reference a single row in the foreign table. Use the #[ManyToOne] attribute to define this type of relation. Use the #[JoinColumn] attribute to specify the foreign key.
#[ManyToMany] Every row in the primary table has many related rows in the foreign table, and every record in the foreign table has many related rows in the primary table. Use the #[ManyToMany] attribute to define this type of relation. Use the #[JoinTable] to specify the junction/join table that will be used for the relation.

Examples

src/Users/Entities/UserEntity.php
use Assegai\Orm\Attributes\Columns\Column;
use Assegai\Orm\Attributes\Columns\EmailColumn;
use Assegai\Orm\Attributes\Columns\PrimaryGeneratedColumn;
use Assegai\Orm\Attributes\Entity;
use Assegai\Orm\Queries\Sql\ColumnType;
use Assegai\App\Posts\Entities\PostEntity;
use Assegai\App\Profiles\Entities\ProfileEntity;
use Assegai\App\Teams\Entities\TeamEntity;

#[Entity(table: 'users')]
class UserEntity
{
  #[PrimaryGeneratedColumn]
  public int $id = 0;

  #[EmailColumn]
  public string $email = '';

  #[Column]
  public string $firstName = '';

  #[Column]
  public string $lastName = '';

  #[Column(type: ColumnType::BOOLEAN, default: false)]
  public bool $isVerified = false;
  
  #[OneToOne(ProfileEntity::class)]
  public ?ProfileEntity $profile = null;
  
  #[ManyToOne(TeamEntity::class)]
  #[JoinColumn('team_id')]
  public ?TeamEntity $team = null;
  
  #[OneToMany(PostEntity::class)]
  public array $posts = []
}

Then you can load your relations by passing a list of relations to your FindOptions object and passing that object to your repository's find method. Like this:

src/Users/UsersService.php

#[Entity('users')]
class UsersService
{
  public function __construct(
    #[InjectRepository(UserEntity::class)]
    protected Repository $usersRepository
  )
  {}

  public function findOneById(int $id): FindResult
  {
      $options = new FindOptions(where: ['id' => $id], relations: ['profile', 'team', 'posts']);
      return $this->usersRepository->find($options);
  }
}

Now, let's assume you have a development server listening on port 3000. You can make a GET request to the /users/:id endpoint.

$ curl --location 'localhost:3000/users/1'

After that, you can expect a response similar to the following:

{
  "id": 1,
  "email": "user@example.com",
  "firstName": "John",
  "lastName": "Doe",
  "isVerified": true,
  "profile": {
    "id": 1,
    "bio": "Hello, I'm John Doe",
    "avatar": "https://example.com/avatar.jpg",
  },
  "team": {
    "id": 1,
    "name": "Team A",
  },
  "posts": [
    {
      "id": 1,
      "title": "Post 1",
      "content": "This is the content of post 1",
    },
    {
      "id": 2,
      "title": "Post 2",
      "content": "This is the content of post 2",
    }
  ]
}