Start here when you want to get the ORM running quickly.
- Use the standalone path if you are writing plain PHP, a script, a worker, or a small library and want direct control over configuration.
- Use the Assegai path if you already have an app and want module-level
data_sourcedefaults, repository injection, and CLI help.
Standalone quick start
1. Install the package
composer require assegaiphp/orm
2. Enable the driver extension you actually need
Only enable the PDO extension for the backend you plan to use:
pdo_mysqlfor MySQL or MariaDBpdo_pgsqlfor PostgreSQLpdo_sqlitefor SQLitepdo_sqlsrvfor MSSQL
3. Decide how you want to configure the script
There are two valid standalone patterns.
Use OrmRuntime::configure() when:
- you want named data sources such as
catalogoranalytics - you want to reuse the same connection definition in several places
- you want static helpers such as schema tools to resolve the same named config
Use a direct DataSource when:
- you are writing one small script
- you already know the exact connection details in that file
- you want the fewest moving parts possible
4. The simplest one-file approach: build the DataSource directly
If all the connection details are already known in the script, you can skip OrmRuntime::configure() entirely.
<?php
require __DIR__ . '/vendor/autoload.php';
use App\Entities\MovieEntity;
use Assegai\Orm\DataSource\DataSource;
use Assegai\Orm\DataSource\DataSourceOptions;
use Assegai\Orm\Enumerations\DataSourceType;
$dataSource = new DataSource(new DataSourceOptions(
entities: [MovieEntity::class],
name: 'catalog',
type: DataSourceType::SQLITE,
path: __DIR__ . '/catalog.sqlite',
));
$movies = $dataSource->getRepository(MovieEntity::class);
$entityManager = $dataSource->manager;
$pdo = $dataSource->getClient();
Why this works:
DataSourceOptionsalready contains the connection details- the ORM does not need runtime lookup for this specific data source
- the repository, entity manager, and raw PDO client all come from that one object
5. Use OrmRuntime::configure() when you want named configuration
If you want the rest of the script to refer to a named data source instead of repeating the path or credentials, configure the runtime first.
<?php
use Assegai\Orm\Support\OrmRuntime;
OrmRuntime::configure([
'databases' => [
'sqlite' => [
'catalog' => [
'path' => __DIR__ . '/catalog.sqlite',
],
],
],
]);
Then the DataSource only needs the entity list plus the data source identity:
<?php
use App\Entities\MovieEntity;
use Assegai\Orm\DataSource\DataSource;
use Assegai\Orm\DataSource\DataSourceOptions;
use Assegai\Orm\Enumerations\DataSourceType;
$dataSource = new DataSource(new DataSourceOptions(
entities: [MovieEntity::class],
name: 'catalog',
type: DataSourceType::SQLITE,
));
What changed here:
name: 'catalog'andtype: DataSourceType::SQLITEtell the ORM which named config to load- the runtime fills in the missing path from the configured
sqlite.catalogentry - schema helpers and other named-data-source workflows can now resolve the same config too
6. All together: a single-file SQLite example
This example shows a full standalone script that you can use as a realistic starting point. It:
- defines an entity
- configures a named SQLite data source
- creates the table if it does not exist
- creates a repository
- inserts a row
- reads the rows back
<?php
require __DIR__ . '/vendor/autoload.php';
use Assegai\Orm\Attributes\Columns\Column;
use Assegai\Orm\Attributes\Columns\PrimaryGeneratedColumn;
use Assegai\Orm\Attributes\Entity;
use Assegai\Orm\DataSource\DataSource;
use Assegai\Orm\DataSource\DataSourceOptions;
use Assegai\Orm\DataSource\Schema;
use Assegai\Orm\DataSource\SchemaOptions;
use Assegai\Orm\Enumerations\DataSourceType;
use Assegai\Orm\Enumerations\SQLDialect;
use Assegai\Orm\Queries\Sql\ColumnType;
use Assegai\Orm\Support\OrmRuntime;
#[Entity(
table: 'movies',
database: 'catalog',
driver: DataSourceType::SQLITE,
)]
class MovieEntity
{
#[PrimaryGeneratedColumn]
public ?int $id = null;
#[Column(type: ColumnType::VARCHAR, nullable: false)]
public string $title = '';
#[Column(type: ColumnType::TEXT, nullable: true)]
public ?string $synopsis = null;
#[Column(type: ColumnType::BOOLEAN, nullable: false)]
public bool $isNowShowing = false;
}
OrmRuntime::configure([
'databases' => [
'sqlite' => [
'catalog' => [
'path' => __DIR__ . '/catalog.sqlite',
],
],
],
]);
Schema::createIfNotExists(
MovieEntity::class,
new SchemaOptions(
dbName: 'catalog',
dialect: SQLDialect::SQLITE,
),
);
$dataSource = new DataSource(new DataSourceOptions(
entities: [MovieEntity::class],
name: 'catalog',
type: DataSourceType::SQLITE,
));
$movies = $dataSource->getRepository(MovieEntity::class);
$movie = $movies->create([
'title' => 'The Lantern City',
'synopsis' => 'A detective follows a string of disappearing film reels.',
'isNowShowing' => true,
]);
$saveResult = $movies->save($movie);
if ($saveResult->isError()) {
throw $saveResult->getLatestError();
}
$allMovies = $movies->find([
'order' => ['id' => 'DESC'],
])->getData();
var_dump($allMovies);
If you want the shortest possible one-file script, you can remove the OrmRuntime::configure() block and pass path: __DIR__ . '/catalog.sqlite' directly into DataSourceOptions instead.
7. The same standalone flow works for the other SQL drivers
The standalone shape does not change when you move to MySQL, MariaDB, PostgreSQL, or MSSQL. You swap the driver and connection details.
For example, a direct MSSQL data source looks like this:
<?php
$dataSource = new DataSource(new DataSourceOptions(
entities: [MovieEntity::class],
name: 'reporting',
type: DataSourceType::MSSQL,
host: '127.0.0.1',
port: 1433,
username: 'sa',
password: 'secret',
));
That is already enough to be productive in a standalone PHP project.
Assegai quick start
1. Add the ORM to the app
assegai add orm
That command is the preferred starting point inside Assegai because it can:
- require
assegaiphp/ormif it is missing - import
OrmModule - expose the ORM console commands through package discovery
2. Configure and create a database with the CLI
The CLI path covers:
- MySQL
- MariaDB
- PostgreSQL
- SQLite
- MSSQL
assegai database:configure cinema --pgsql
assegai database:setup cinema --pgsql
assegai migration:setup cinema --pgsql
Each command has a different job:
database:configurewrites connection details into app configdatabase:setupcreates the database if it does not already exist and the driver can do somigration:setupprepares the migration workspace for that named data source
Swap --pgsql for --mysql, --mariadb, --sqlite, or --mssql to match your backend.
3. Give the module a default data source
<?php
namespace Assegaiphp\CinemaHub\Movies;
use Assegai\Core\Attributes\Modules\Module;
#[Module(
providers: [MoviesService::class],
controllers: [MoviesController::class],
config: ['data_source' => 'pgsql:cinema'],
)]
class MoviesModule
{
}
That driver:name format is the preferred module-level form:
pgsqlis the driver tokencinemais the named configured data source
4. Inject a repository into a service
<?php
namespace Assegaiphp\CinemaHub\Movies;
use Assegai\Core\Attributes\Injectable;
use Assegai\Orm\Attributes\InjectRepository;
use Assegai\Orm\Management\Repository;
use Assegaiphp\CinemaHub\Movies\Entities\MovieEntity;
#[Injectable]
class MoviesService
{
public function __construct(
#[InjectRepository(MovieEntity::class)]
private readonly Repository $movies,
) {
}
public function all(): array
{
return $this->movies->find([
'order' => ['id' => 'DESC'],
])->getData();
}
}
This is the normal Assegai day-to-day path:
- the module decides the default data source
- the entity defines the persistence shape
- the service works through a repository instead of manually opening connections
What to learn next
Now that you have a path that runs, move into the concepts in order:
If you already know you want lower-level control, continue with Working with Entity Manager and Query Builder.