Completed
Push — master ( e1ce3b...f144d1 )
by Anton
14s queued 12s
created

src/Factory.php (3 issues)

1
<?php
2
3
/**
4
 * Cycle DataMapper ORM
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Cycle\ORM;
13
14
use Cycle\ORM\Config\RelationConfig;
15
use Cycle\ORM\Exception\TypecastException;
16
use Cycle\ORM\Mapper\Mapper;
17
use Cycle\ORM\Relation\RelationInterface;
18
use Cycle\ORM\Select\ConstrainInterface;
19
use Cycle\ORM\Select\LoaderInterface;
20
use Cycle\ORM\Select\Repository;
21
use Cycle\ORM\Select\Source;
22
use Cycle\ORM\Select\SourceInterface;
23
use Psr\Container\ContainerInterface;
24
use Spiral\Core\Container;
25
use Spiral\Core\FactoryInterface as CoreFactory;
26
use Spiral\Database\DatabaseInterface;
27
use Spiral\Database\DatabaseProviderInterface;
28
29
final class Factory implements FactoryInterface
30
{
31
    /** @var RelationConfig */
32
    private $config;
33
34
    /** @var CoreFactory */
35
    private $factory;
36
37
    /** @var ContainerInterface */
38
    private $container;
39
40
    /** @var DatabaseProviderInterface */
41
    private $dbal;
42
43
    /** @var array<string, string> */
44
    private $defaults = [
45
        Schema::REPOSITORY => Repository::class,
46
        Schema::SOURCE     => Source::class,
47
        Schema::MAPPER     => Mapper::class,
48
        Schema::CONSTRAIN  => null,
49
    ];
50
51
    /**
52
     * @param DatabaseProviderInterface $dbal
53
     * @param RelationConfig            $config
54
     * @param CoreFactory|null          $factory
55
     * @param ContainerInterface|null   $container
56
     */
57
    public function __construct(
58
        DatabaseProviderInterface $dbal,
59
        RelationConfig $config = null,
60
        CoreFactory $factory = null,
61
        ContainerInterface $container = null
62
    ) {
63
        $this->dbal = $dbal;
64
        $this->config = $config ?? RelationConfig::getDefault();
65
        $this->factory = $factory ?? new Container();
66
        $this->container = $container ?? new Container();
67
    }
68
69
    /**
70
     * @inheritdoc
71
     */
72
    public function make(
73
        string $alias,
74
        array $parameters = []
75
    ) {
76
        return $this->factory->make($alias, $parameters);
77
    }
78
79
    /**
80
     * @inheritdoc
81
     */
82
    public function mapper(
83
        ORMInterface $orm,
84
        SchemaInterface $schema,
85
        string $role
86
    ): MapperInterface {
87
        $class = $schema->define($role, Schema::MAPPER) ?? $this->defaults[Schema::MAPPER];
88
89
        if (!is_subclass_of($class, MapperInterface::class)) {
90
            throw new TypecastException($class . ' does not implement ' . MapperInterface::class);
91
        }
92
93
        return $this->factory->make(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->factory->m...e\ORM\Schema::SCHEMA))) could return the type null which is incompatible with the type-hinted return Cycle\ORM\MapperInterface. Consider adding an additional type-check to rule them out.
Loading history...
94
            $class,
95
            [
96
                'orm'    => $orm,
97
                'role'   => $role,
98
                'schema' => $schema->define($role, Schema::SCHEMA)
99
            ]
100
        );
101
    }
102
103
    /**
104
     * @inheritdoc
105
     */
106
    public function loader(
107
        ORMInterface $orm,
108
        SchemaInterface $schema,
109
        string $role,
110
        string $relation
111
    ): LoaderInterface {
112
        $schema = $schema->defineRelation($role, $relation);
113
114
        return $this->config->getLoader($schema[Relation::TYPE])->resolve(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->config->ge...ORM\Relation::SCHEMA])) could return the type null which is incompatible with the type-hinted return Cycle\ORM\Select\LoaderInterface. Consider adding an additional type-check to rule them out.
Loading history...
115
            $this->factory,
116
            [
117
                'orm'    => $orm,
118
                'name'   => $relation,
119
                'target' => $schema[Relation::TARGET],
120
                'schema' => $schema[Relation::SCHEMA]
121
            ]
122
        );
123
    }
124
125
    /**
126
     * @inheritdoc
127
     */
128
    public function relation(
129
        ORMInterface $orm,
130
        SchemaInterface $schema,
131
        string $role,
132
        string $relation
133
    ): RelationInterface {
134
        $relSchema = $schema->defineRelation($role, $relation);
135
        $type = $relSchema[Relation::TYPE];
136
137
        return $this->config->getRelation($type)->resolve(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->config->ge...ation::LOAD] ?? null))) could return the type null which is incompatible with the type-hinted return Cycle\ORM\Relation\RelationInterface. Consider adding an additional type-check to rule them out.
Loading history...
138
            $this->factory,
139
            [
140
                'orm'    => $orm,
141
                'name'   => $relation,
142
                'target' => $relSchema[Relation::TARGET],
143
                'schema' => $relSchema[Relation::SCHEMA] + [Relation::LOAD => $relSchema[Relation::LOAD] ?? null],
144
            ]
145
        );
146
    }
147
148
    /**
149
     * @inheritdoc
150
     */
151
    public function database(string $database = null): DatabaseInterface
152
    {
153
        return $this->dbal->database($database);
154
    }
155
156
    /**
157
     * @inheritDoc
158
     */
159
    public function repository(
160
        ORMInterface $orm,
161
        SchemaInterface $schema,
162
        string $role,
163
        ?Select $select
164
    ): RepositoryInterface {
165
        $class = $schema->define($role, Schema::REPOSITORY) ?? $this->defaults[Schema::REPOSITORY];
166
167
        if (!is_subclass_of($class, RepositoryInterface::class)) {
168
            throw new TypecastException($class . ' does not implement ' . RepositoryInterface::class);
169
        }
170
171
        return $this->factory->make(
172
            $class,
173
            [
174
                'select' => $select,
175
                'orm' => $orm,
176
                'role'   => $role,
177
            ]
178
        );
179
    }
180
181
    /**
182
     * @inheritDoc
183
     */
184
    public function source(
185
        ORMInterface $orm,
186
        SchemaInterface $schema,
187
        string $role
188
    ): SourceInterface {
189
        $source = $schema->define($role, Schema::SOURCE) ?? $this->defaults[Schema::SOURCE];
190
191
        if (!is_subclass_of($source, SourceInterface::class)) {
192
            throw new TypecastException($source . ' does not implement ' . SourceInterface::class);
193
        }
194
195
        if ($source !== Source::class) {
196
            return $this->factory->make($source, ['orm' => $orm, 'role' => $role]);
197
        }
198
199
        $source = new Source(
200
            $this->database($schema->define($role, Schema::DATABASE)),
201
            $schema->define($role, Schema::TABLE)
202
        );
203
204
        $constrain = $schema->define($role, Schema::CONSTRAIN) ?? $this->defaults[Schema::CONSTRAIN];
205
206
        if ($constrain === null) {
207
            return $source;
208
        }
209
210
        if (!is_subclass_of($constrain, ConstrainInterface::class)) {
211
            throw new TypecastException($constrain . ' does not implement ' . ConstrainInterface::class);
212
        }
213
214
        return $source->withConstrain(is_object($constrain) ? $constrain : $this->factory->make($constrain));
215
    }
216
217
    /**
218
     * Add default classes for resolve
219
     *
220
     * @param array $defaults
221
     * @return FactoryInterface
222
     */
223
    public function withDefaultSchemaClasses(array $defaults): FactoryInterface
224
    {
225
        $clone = clone $this;
226
227
        $clone->defaults = $defaults + $this->defaults;
228
229
        return $clone;
230
    }
231
}
232