-
Notifications
You must be signed in to change notification settings - Fork 0
Home
This guide covers how to install, setup and basic use of prime.
To install prime, you can simply use composer
command :
composer require b2pweb/prime
Or you can define b2pweb/bdf-prime
as dependency on composer.json
:
{
// ...
"require": {
"b2pweb/bdf-prime": "~2.1"
},
// ...
}
Prime internally uses doctrine/dbal
version 3, so if you have some dependencies on a specific version of doctrine dbal, you can set it on composer.json
:
{
// ...
"require": {
"b2pweb/bdf-prime": "~2.1",
"doctrine/dbal": "~3.4.0"
},
// ...
}
Once composer is configured, simply run :
composer update
Both classes are the entry point of Prime :
-
ConnectionManager
for the DBAL part, for example connection declaration -
ServiceLocator
for the ORM part, for example repository creation
<?php
use Bdf\Prime\ConnectionManager;
use Bdf\Prime\Locatorizable;
use Bdf\Prime\ServiceLocator;
// declare your connection manager
$connections = new ConnectionManager();
$connections->declareConnection('myDB', [
'adapter' => 'mysql',
'host' => 'localhost',
]);
// Create and configure prime ORM
$prime = new ServiceLocator($connections);
// Set a PSR-11 container instance, which make it accessible from mappers
$prime->setDI($kernel->getContainer());
// Set the serializer instance
$prime->setSerializer($kernel->getContainer()->get('serializer'));
// Enable active record system
Locatorizable::configure($prime);
// Use the service locator to create your repositories
$repository = $prime->repository(User::class);
To declare a new connection, you can call ConnectionManager::declareConnection(string $name, string|array $parametersOrDsn)
.
Note: if a connection with the same name already exists and is already loaded, the connection will not be redeclared by calling this method. You must call
ConnectionManager::removeConnection(string $name)
first.
<?php
use Bdf\Prime\ConnectionManager;
// declare your connection manager
$connections = new ConnectionManager();
// MySQL
$connections->declareConnection('mysql', 'mysql://user:password@localhost/database');
// Sqlite
$connections->declareConnection('sqlite', 'sqlite://path/to/database.sqlite');
You can also use DBAL-compatible configuration arrays instead of DSN strings if you prefer:
<?php
use Bdf\Prime\ConnectionManager;
$connections = new ConnectionManager();
$connections->declareConnection('mysql', [
'dbname' => 'mydb',
'user' => 'user',
'password' => 'secret',
'host' => 'localhost',
'driver' => 'pdo_mysql',
// OR
'adapter' => 'mysql',
]);
Option | Type | Description |
---|---|---|
driver |
string | The driver name (ex: pdo_mysql , mysqli ). See doctrine drivers for more information. |
adapter |
string | PDO adapter. pdo_ prefix will be added. |
dbname |
string | The database name. |
host |
string | The host name. |
port |
string | The connection port. |
user |
string | The user credentials. |
password |
string | The password credentials. |
path |
string | (SQLite) The file path used by sqlite to store data. |
url |
string | The DSN string. All data extracted from the dsn will erase the others options. |
driverOptions |
array | The driver options. |
memory |
bool | (SQLite) The sqlite option for memory. |
unix_socket |
string | The unix socket file. |
charset |
string | The client charset. |
serverVersion |
string | (MySQL) Define the used MySQL server version. |
wrapperClass |
string | (internal) FQCN of the connection object. Must implement Bdf\Prime\Connection\ConnectionInterface . |
driverClass |
string | (internal) FQCN of the database driver object. Do not use directly, use driver or adapter parameter instead. |
shards |
array | Sharding connection: contains an array with the shard name as key and the options as value. The shard options will be merged onto the master connection. |
read |
array | Master/slave connection: contains an array of options for the read connection. |
Once connections and ORM are configured, you can declare entities. Prime use data mapper for declaring entities, and query build for querying it. Two classes should be declared :
- The entity
- The mapper, which should get the same name and namespace, but suffixed by Mapper
To declare an entity you should :
- Inherit from
Bdf\Prime\Entity\Model
(enable active record capabilities) - Add a property for each column
- Those properties should be
protected
to allow optimizations on hydrator - If you want to use typed properties, all properties without default value must be declared as nullable with
null
as default value - Add necessary getters and setters (setters are not necessary if the property is read only)
- Add a constructor which takes as parameter an array of values, and call
$this->import($data)
<?php
use Bdf\Prime\Entity\Model;
class User extends Model
{
// Declare properties with type. Default value must be set to null
protected ?int $id = null;
protected ?string $login = null;
protected ?string $password = null;
/**
* Nullable type and default value is not required here because the default value is an empty array
* @var list<int>
*/
public array $roles = [];
// Declare constructor which allows simple hydration
public function __construct(array $data = [])
{
$this->import($data);
}
public function id(): ?int
{
return $this->id;
}
public function setId(?int $id): self
{
$this->id = $id;
return $this;
}
public function login(): ?string
{
return $this->login;
}
public function setLogin(?string $login): self
{
$this->login = $login;
return $this;
}
public function password(): ?string
{
return $this->password;
}
public function setPassword(?string $password): self
{
$this->password = $password;
return $this;
}
public function roles(): array
{
return $this->roles;
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
}
The mapper declares the connection and the table to use, the properties used for entity / database columns mapping and also custom filters or queries. For advanced configuration, see Advanced Mapper usage.
<?php
use Bdf\Prime\Mapper\Mapper;
use Bdf\Prime\Mapper\Builder\FieldBuilder;
use Bdf\Prime\Mapper\Builder\IndexBuilder;
use Bdf\Prime\Query\QueryInterface;
use Bdf\Prime\Query\Expression\Like;
class UserMapper extends Mapper
{
/**
* Schema declaration
*
* Definition
* - connection : The connection name declared in the connection manager (mandatory).
* - database : The database name.
* - table : The table name (mandatory).
* - tableOptions : The table options (ex: engine => myisam).
*
* <code>
* return [
* 'connection' => (string),
* 'database' => (string),
* 'table' => (string),
* 'tableOptions' => (array),
* ];
* </code>
*
* @return array|null
*/
public function schema(): array
{
return [
'connection' => 'myDB',
'table' => 'users',
];
}
/**
* {@inheritdoc}
*/
public function buildFields(FieldBuilder $builder): void
{
$builder
->bigint('id')->autoincrement() // autoincrement will define id as primary key
->string('login')->unique() // define unique constraint and index
->string('password')
->arrayOfInt('roles') // roles will be saved as CSV
;
}
// Define custom query filters
public function filters() : array
{
return [
// Filter a user that has the given role
'hasRole' => function (QueryInterface $query, int $role): void {
// Use "Like" expression because roles are stored as CSV
// The generated query will be "SELECT ... WHERE roles LIKE '%,[role],%'"
$query->where('role', (new Like($role))->searchableArray());
}
];
}
}
<?php
// Create user entity
$user = (new User())
->setLogin('foo')
->setPassword('bar')
->setRoles([1, 2, 3])
;
// You can also use an array on constructor
// Note: this is discouraged because it does not allow efficient static analysis
$user = new User([
'login' => 'foo',
'password' => 'bar',
'roles' => [1, 2, 3],
]);
// Save user into database, and fill the id property
// Execute the query "INSERT INTO users(login, password, roles) VALUES('foo', 'bar', ',1,2,3,')"
$user->save();
$user->insert(); // Or use insert to ensure that the entity will be inserted and not updated
// Search an user using query builder
// Execute the query "SELECT * FROM users WHERE login = $login AND password = $password LIMIT 1"
$loggedUser = User::builder()
->where('login', $login)
->where('password', $password)
->first()
;
// Search all users with role 2, using a custom filter
// Execute the query "SELECT * FROM users WHERE roles LIKE '%,2,%'" and iterate over entities using a walker
// So rows will be loaded by chunks of 150 entities
foreach (User::builder()->where('hasRole', 2) as $user) {
// ...
}
// Same as above, but loads all entities and store it as array
foreach (User::builder()->where('hasRole', 2)->all() as $user) {
// ...
}
// To update an entity, simply call setters and then Model::save() or Model::update()
// Note: when calling save(), all properties will be updated, not only the changed one
$user->setRoles([2, 4])->save();
// update() ensures that an UPDATE query will be performed and not an INSERT
// And also allows to specify properties/columns to update, to optimize query
// Execute the query "UPDATE users SET roles = ',2,4,' WHERE id = ?"
$user->setRoles([2, 4])->update(['roles']);
// To delete an entity, simply call Model::delete()
// Execute the query "DELETE FROM users WHERE id = ?"
$user->delete();