Passed
Push — master ( 5c3f46...c58a59 )
by Anton
02:02
created

CycleBootloader::orm()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 12
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 3
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
declare(strict_types=1);
9
10
namespace Spiral\Bootloader\Cycle;
11
12
use Cycle\ORM\Config\RelationConfig;
13
use Cycle\ORM\Factory;
14
use Cycle\ORM\FactoryInterface;
15
use Cycle\ORM\ORM;
16
use Cycle\ORM\ORMInterface;
17
use Cycle\ORM\PromiseFactoryInterface;
18
use Cycle\ORM\SchemaInterface;
19
use Cycle\ORM\Select;
20
use Cycle\ORM\Transaction;
21
use Cycle\ORM\TransactionInterface;
22
use Spiral\Boot\Bootloader\Bootloader;
23
use Spiral\Boot\Bootloader\DependedInterface;
24
use Spiral\Boot\FinalizerInterface;
25
use Spiral\Bootloader\Database\DatabaseBootloader;
26
use Spiral\Core\Container;
27
use Spiral\Cycle\SelectInjector;
28
use Spiral\Database\DatabaseProviderInterface;
29
30
final class CycleBootloader extends Bootloader implements DependedInterface
31
{
32
    public const BINDINGS = [
33
        TransactionInterface::class => Transaction::class,
34
    ];
35
36
    public const SINGLETONS = [
37
        ORMInterface::class     => [self::class, 'orm'],
38
        FactoryInterface::class => [self::class, 'factory'],
39
    ];
40
41
    /**
42
     * @param Container            $container
43
     * @param FinalizerInterface   $finalizer
44
     * @param SchemaInterface|null $schema
45
     */
46
    public function boot(Container $container, FinalizerInterface $finalizer, SchemaInterface $schema = null)
47
    {
48
        $finalizer->addFinalizer(function () use ($container) {
49
            if ($container->hasInstance(ORMInterface::class)) {
50
                $container->get(ORMInterface::class)->getHeap()->clean();
51
            }
52
        });
53
54
        $container->bindInjector(Select::class, SelectInjector::class);
55
56
        if ($schema !== null) {
57
            $this->bootRepositories($container, $schema);
58
        }
59
    }
60
61
    /**
62
     * @param Container       $container
63
     * @param SchemaInterface $schema
64
     */
65
    public function bootRepositories(Container $container, SchemaInterface $schema)
66
    {
67
        foreach ($schema->getRoles() as $role) {
68
            $repository = $schema->define($role, SchemaInterface::REPOSITORY);
69
            if ($repository === Select\Repository::class || $repository === null) {
70
                // default repository can not be wired
71
                continue;
72
            }
73
74
            // initiate all repository dependencies using factory method forwarded to ORM
75
            $container->bindSingleton($repository, function (ORMInterface $orm) use ($role, $container, $repository) {
76
                // to avoid cyclic dependency since ORM use factory to resolve needed dependencies
77
                $binding = $container->getBindings()[$repository];
78
                $container->removeBinding($repository);
79
80
                try {
81
                    return $orm->getRepository($role);
82
                } finally {
83
                    $container->bind($repository, $binding);
84
                }
85
            });
86
        }
87
    }
88
89
    /**
90
     * @return array
91
     */
92
    public function defineDependencies(): array
93
    {
94
        return [
95
            DatabaseBootloader::class,
96
            SchemaBootloader::class
97
        ];
98
    }
99
100
    /**
101
     * @param FactoryInterface             $factory
102
     * @param SchemaInterface              $schema
103
     * @param PromiseFactoryInterface|null $promiseFactory
104
     * @return ORMInterface
105
     */
106
    protected function orm(
107
        FactoryInterface $factory,
108
        SchemaInterface $schema = null,
109
        PromiseFactoryInterface $promiseFactory = null
110
    ): ORMInterface {
111
        $orm = new ORM($factory, $schema);
112
113
        if ($promiseFactory !== null) {
114
            return $orm->withPromiseFactory($promiseFactory);
115
        }
116
117
        return $orm;
118
    }
119
120
    /**
121
     * @param DatabaseProviderInterface $dbal
122
     * @param Container                 $container
123
     * @return FactoryInterface
124
     */
125
    protected function factory(DatabaseProviderInterface $dbal, Container $container): FactoryInterface
126
    {
127
        return new Factory($dbal, RelationConfig::getDefault(), $container, $container);
128
    }
129
}