ORM- Entity Manager

7/14/2021

# ORM:

Object-relational mapping (ORM, O/RM, and O/R mapping tool) in computer science is a programming technique for converting data between incompatible type systems using object-oriented programming languages. This creates, in effect, a "virtual object database" that can be used from within the programming language. There are both free and commercial packages available that perform object-relational mapping, although some programmers opt to construct their own ORM tools.

ORM là 1 kĩ thuật mapping giữa dữ liệu database và đối tượng trong hệ thống OOP. Có 2 loại: Active Record và Data Mapper. Laravel sử dụng Eloquent - thuộc loại Active Record. Thử tích hợp 1 loại ORM khác - Doctrine (opens new window) vào hệ thống.

# Obtaining the Entity Manager:

Tạo ORM service để có thể quản lí trong DI Container.

# Manager:
interface ORMManagerContract
{
    public function getEntityManager();
}
1
2
3
4
namespace Nin\Libs\ORM;

class ORMAbstractManager implements ORMManagerContract
{
    protected ORMConfigContract $config;
    protected ORMConnectionContract $connection;

    public function __construct(ORMConfigContract $config, ORMConnectionContract $connection)
    {
        $this->config = $config;
        $this->connection = $connection;
    }

    public function getEntityManager()
    {
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Config:
interface ORMConfigContract
{
    public function getEntitiesPath(): array;

    public function getIsDevMode(): bool;

    public function getProxyDir();

    public function getCache();

    public function getUseSimpleAnnotationReader(): bool;
}
1
2
3
4
5
6
7
8
9
10
11
12
class ORMConfig extends AbstractConfig implements ORMConfigContract
{
    public function getEntitiesPath(): array
    {
        $entitiesPath = $this->config->get('orm.entities_dir', ['Entities']);
        foreach ($entitiesPath as $key => $entity) {
            $entitiesPath[$key] = ROOT . "src/" . $entity;
        }
        return $entitiesPath;
    }

    public function getIsDevMode(): bool
    {
        return $this->config->get('orm.is_dev_mode', true);
    }

    public function getProxyDir()
    {
        return $this->config->get('orm.proxy_dir', null);
    }

    public function getCache()
    {
        return $this->config->get('orm.cache', null);
    }

    public function getUseSimpleAnnotationReader(): bool
    {
        return $this->config->get('orm.use_simple_annotation_reader', false);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

Get dữ liệu từ file config/app.yml

# ORM
orm:
  entities_dir:
    - Entities
  is_dev_mode: true
  connection:
    driver: "pdo_mysql"
    host: "localhost"
    dbname: "ni"
    user: "root"
    password: "secret"
1
2
3
4
5
6
7
8
9
10
11
# Connection:
interface ORMConnectionContract
{
    public function getConnector();
}
1
2
3
4
abstract class ORMConnection
{
    protected ORMConnectionPropertyContract $property;

    public function __construct(ORMConnectionPropertyContract $property)
    {
        $this->property = $property;
    }
}
1
2
3
4
5
6
7
8
9
# Connection Properties
interface ORMConnectionPropertyContract
{
    public function getDriver(): string;

    public function getHost(): string;

    public function getDbName(): string;

    public function getUser(): string;

    public function getPassword(): string;
}
1
2
3
4
5
6
7
8
9
10
11
12
class ORMConnectionProperty extends AbstractConfig implements ORMConnectionPropertyContract
{
    public function getDriver(): string
    {
        return $this->config->get('orm.connection.driver', 'pdo_mysql');
    }

    public function getHost(): string
    {
        return $this->config->get('orm.connection.host', 'localhost');
    }

    public function getDbName(): string
    {
        return $this->config->get('orm.connection.dbname', 'ni');
    }

    public function getUser(): string
    {
        return $this->config->get('orm.connection.user', 'root');
    }

    public function getPassword(): string
    {
        return $this->config->get('orm.connection.password', 'secret');
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Doctrine:

Doctrine Manager

class ORMDoctrineManager extends ORMAbstractManager implements ORMManagerContract
{
    public function getEntityManager()
    {
        $config = $this->getDoctrineConfig($this->config);
        $conn = $this->connection->getConnector();
        return EntityManager::create($conn, $config);
    }

    protected function getDoctrineConfig(ORMConfigContract $config)
    {
        $entitiesPath = $config->getEntitiesPath();
        $isDevMode = $config->getIsDevMode();
        $proxyDir = $config->getProxyDir();
        $cache = $config->getCache();
        $useSimpleAnnotationReader = $config->getUseSimpleAnnotationReader();
        return Setup::createAnnotationMetadataConfiguration($entitiesPath, $isDevMode, $proxyDir, $cache,
            $useSimpleAnnotationReader);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Doctrine có hổ trợ nhiều loại connection nên tách riêng phần này, nếu muốn thay thế chỉ cần tạo thêm class rồi binding cho ORMConnectionContract.

class ORMDoctrineConnection extends ORMConnection implements ORMConnectionContract
{
    public function getConnector()
    {
        $connectionParams = $this->getConnectionProperties($this->property);
        return DriverManager::getConnection($connectionParams);
    }

    public function getConnectionProperties(ORMConnectionPropertyContract $property)
    {
        return array(
            'dbname' => $property->getDbName(),
            'user' => $property->getUser(),
            'password' => $property->getPassword(),
            'host' => $property->getHost(),
            'driver' => $property->getDriver()
        );
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# Service Provider:

Tạo ORMServiceProvider để binding các dependency cho các contract của ORM Service

class ORMServiceProvider extends AbstractServiceProvider
{
    public $bindings = [
        ORMConnectionPropertyContract::class => ORMConnectionProperty::class,
        ORMConnectionContract::class => ORMDoctrineConnection::class,
        ORMConfigContract::class => ORMConfig::class,
        ORMManagerContract::class => ORMDoctrineManager::class
    ];

    ...
}
1
2
3
4
5
6
7
8
9
10
11

Nếu muốn thay thế Doctrine bằng Eloquent chẳng hạn. Chỉ cần binding lại contract ORMManagerContract


Packagist: https://packagist.org/packages/nin/nin (opens new window)