Factory::source()   A
last analyzed

Complexity

Conditions 6
Paths 5

Size

Total Lines 31
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 14
c 1
b 0
f 0
nc 5
nop 3
dl 0
loc 31
rs 9.2222
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Biurad opensource projects.
7
 *
8
 * PHP version 7.2 and above required
9
 *
10
 * @author    Divine Niiquaye Ibok <[email protected]>
11
 * @copyright 2019 Biurad Group (https://biurad.com/)
12
 * @license   https://opensource.org/licenses/BSD-3-Clause License
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 */
17
18
namespace Biurad\Cycle;
19
20
use Biurad\DependencyInjection\FactoryInterface as NetteFactoryInterface;
21
use Closure;
22
use Cycle\ORM\Config\RelationConfig;
23
use Cycle\ORM\Exception\TypecastException;
24
use Cycle\ORM\FactoryInterface;
25
use Cycle\ORM\Mapper\Mapper;
26
use Cycle\ORM\MapperInterface;
27
use Cycle\ORM\ORMInterface;
28
use Cycle\ORM\Relation;
29
use Cycle\ORM\Relation\RelationInterface;
30
use Cycle\ORM\RepositoryInterface;
31
use Cycle\ORM\Schema;
32
use Cycle\ORM\SchemaInterface;
33
use Cycle\ORM\Select;
34
use Cycle\ORM\Select\ConstrainInterface;
35
use Cycle\ORM\Select\LoaderInterface;
36
use Cycle\ORM\Select\Repository;
37
use Cycle\ORM\Select\Source;
38
use Cycle\ORM\Select\SourceInterface;
39
use Spiral\Core\Container\Autowire;
40
use Spiral\Database\DatabaseInterface;
41
use Spiral\Database\DatabaseProviderInterface;
42
43
final class Factory implements FactoryInterface
44
{
45
    /** @var RelationConfig */
46
    private $config;
47
48
    /** @var NetteFactoryInterface */
49
    private $factory;
50
51
    /** @var DatabaseProviderInterface */
52
    private $dbal;
53
54
    /** @var array<string, string> */
55
    private $defaults = [
56
        Schema::REPOSITORY => Repository::class,
57
        Schema::SOURCE     => Source::class,
58
        Schema::MAPPER     => Mapper::class,
59
        Schema::CONSTRAIN  => null,
60
    ];
61
62
    /**
63
     * @param DatabaseProviderInterface $dbal
64
     * @param RelationConfig            $config
65
     * @param NetteFactoryInterface     $factory
66
     */
67
    public function __construct(
68
        DatabaseProviderInterface $dbal,
69
        RelationConfig $config = null,
70
        NetteFactoryInterface $factory = null
71
    ) {
72
        $this->dbal    = $dbal;
73
        $this->factory = $factory;
74
        $this->config  = $config ?? RelationConfig::getDefault();
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80
    public function make(string $alias, array $parameters = [])
81
    {
82
        return $this->factory->createInstance($alias, $parameters);
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88
    public function mapper(
89
        ORMInterface $orm,
90
        SchemaInterface $schema,
91
        string $role
92
    ): MapperInterface {
93
        $class = $schema->define($role, Schema::MAPPER) ?? $this->defaults[Schema::MAPPER];
94
95
        if (!\is_subclass_of($class, MapperInterface::class)) {
96
            throw new TypecastException($class . ' does not implement ' . MapperInterface::class);
97
        }
98
99
        return $this->factory->createInstance(
100
            $class,
101
            [
102
                'orm'    => $orm,
103
                'role'   => $role,
104
                'schema' => $schema->define($role, Schema::SCHEMA),
105
            ]
106
        );
107
    }
108
109
    /**
110
     * {@inheritdoc}
111
     */
112
    public function loader(
113
        ORMInterface $orm,
114
        SchemaInterface $schema,
115
        string $role,
116
        string $relation
117
    ): LoaderInterface {
118
        $schema  = $schema->defineRelation($role, $relation);
119
        $factory = clone $this->factory;
120
121
        $loader = Closure::bind(
122
            static function (Autowire $autowire) use ($factory, $orm, $relation, $schema): LoaderInterface {
123
                return $factory->createInstance(
124
                    $autowire->alias,
0 ignored issues
show
Bug introduced by
The property alias is declared private in Spiral\Core\Container\Autowire and cannot be accessed from this context.
Loading history...
125
                    [
126
                        'orm'    => $orm,
127
                        'name'   => $relation,
128
                        'target' => $schema[Relation::TARGET],
129
                        'schema' => $schema[Relation::SCHEMA],
130
                    ]
131
                );
132
            },
133
            null,
134
            Autowire::class
135
        );
136
137
        return $loader($this->config->getLoader($schema[Relation::TYPE]));
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143
    public function relation(
144
        ORMInterface $orm,
145
        SchemaInterface $schema,
146
        string $role,
147
        string $relation
148
    ): RelationInterface {
149
        $relSchema = $schema->defineRelation($role, $relation);
150
        $type      = $relSchema[Relation::TYPE];
151
        $factory   = clone $this->factory;
152
153
        $relation = Closure::bind(
154
            static function (Autowire $autowire) use ($factory, $orm, $relation, $relSchema): RelationInterface {
155
                return $factory->createInstance(
156
                    $autowire->alias,
0 ignored issues
show
Bug introduced by
The property alias is declared private in Spiral\Core\Container\Autowire and cannot be accessed from this context.
Loading history...
157
                    [
158
                        'orm'    => $orm,
159
                        'name'   => $relation,
160
                        'target' => $relSchema[Relation::TARGET],
161
                        'schema' => $relSchema[Relation::SCHEMA] + [Relation::LOAD => $relSchema[Relation::LOAD] ?? null],
162
                    ]
163
                );
164
            },
165
            null,
166
            Autowire::class
167
        );
168
169
        return $relation($this->config->getRelation($type));
170
    }
171
172
    /**
173
     * {@inheritdoc}
174
     */
175
    public function database(string $database = null): DatabaseInterface
176
    {
177
        return $this->dbal->database($database);
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183
    public function repository(
184
        ORMInterface $orm,
185
        SchemaInterface $schema,
186
        string $role,
187
        ?Select $select
188
    ): RepositoryInterface {
189
        $class = $schema->define($role, Schema::REPOSITORY) ?? $this->defaults[Schema::REPOSITORY];
190
191
        if (!\is_subclass_of($class, RepositoryInterface::class)) {
192
            throw new TypecastException($class . ' does not implement ' . RepositoryInterface::class);
193
        }
194
195
        return $this->factory->createInstance(
196
            $class,
197
            [
198
                'select' => $select,
199
                'orm'    => $orm,
200
                'role'   => $role,
201
            ]
202
        );
203
    }
204
205
    /**
206
     * {@inheritdoc}
207
     */
208
    public function source(
209
        ORMInterface $orm,
210
        SchemaInterface $schema,
211
        string $role
212
    ): SourceInterface {
213
        $source = $schema->define($role, Schema::SOURCE) ?? $this->defaults[Schema::SOURCE];
214
215
        if (!\is_subclass_of($source, SourceInterface::class)) {
216
            throw new TypecastException($source . ' does not implement ' . SourceInterface::class);
217
        }
218
219
        if ($source !== Source::class) {
220
            return $this->factory->make($source, ['orm' => $orm, 'role' => $role]);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->factory->m...$orm, 'role' => $role)) could return the type null which is incompatible with the type-hinted return Cycle\ORM\Select\SourceInterface. Consider adding an additional type-check to rule them out.
Loading history...
221
        }
222
223
        $source = new Source(
224
            $this->database($schema->define($role, Schema::DATABASE)),
225
            $schema->define($role, Schema::TABLE)
226
        );
227
228
        $constrain = $schema->define($role, Schema::CONSTRAIN) ?? $this->defaults[Schema::CONSTRAIN];
229
230
        if (null === $constrain) {
231
            return $source;
232
        }
233
234
        if (!\is_subclass_of($constrain, ConstrainInterface::class)) {
235
            throw new TypecastException($constrain . ' does not implement ' . ConstrainInterface::class);
236
        }
237
238
        return $source->withConstrain(\is_object($constrain) ? $constrain : $this->factory->make($constrain));
239
    }
240
241
    /**
242
     * Add default classes for resolve
243
     *
244
     * @param array $defaults
245
     *
246
     * @return FactoryInterface
247
     */
248
    public function withDefaultSchemaClasses(array $defaults): FactoryInterface
249
    {
250
        $clone = clone $this;
251
252
        $clone->defaults = $defaults + $this->defaults;
253
254
        return $clone;
255
    }
256
}
257