Skip to content
fperugini-b2p edited this page Nov 15, 2022 · 4 revisions

Getting started

This guide covers how to install, setup and basic use of prime.

Installation with Composer

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

Declare ConnectionManager and ServiceLocator

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

Instantiation

<?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);

Declare your connections

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',
]);

Available options

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.

Declare entity and mapper

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

Entity

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;
    }
}

Mapper

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());
            }
        ];
    }
}

CRUD

<?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();