-
-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathPorter.php
More file actions
188 lines (159 loc) · 6 KB
/
Porter.php
File metadata and controls
188 lines (159 loc) · 6 KB
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
<?php
namespace ScriptFUSION\Porter;
use Psr\Container\ContainerInterface;
use ScriptFUSION\Porter\Collection\CountablePorterRecords;
use ScriptFUSION\Porter\Collection\CountableProviderRecords;
use ScriptFUSION\Porter\Collection\PorterRecords;
use ScriptFUSION\Porter\Collection\ProviderRecords;
use ScriptFUSION\Porter\Collection\RecordCollection;
use ScriptFUSION\Porter\Connector\ConnectionContext;
use ScriptFUSION\Porter\Connector\ConnectionContextFactory;
use ScriptFUSION\Porter\Connector\ImportConnector;
use ScriptFUSION\Porter\Provider\ForeignResourceException;
use ScriptFUSION\Porter\Provider\ObjectNotCreatedException;
use ScriptFUSION\Porter\Provider\Provider;
use ScriptFUSION\Porter\Provider\ProviderFactory;
use ScriptFUSION\Porter\Provider\ProviderOptions;
use ScriptFUSION\Porter\Provider\Resource\ProviderResource;
use ScriptFUSION\Porter\Specification\ImportSpecification;
use ScriptFUSION\Porter\Transform\Transformer;
/**
* Imports data according to an ImportSpecification from a provider in the container of providers or internal factory.
*/
class Porter
{
/**
* @var ContainerInterface Container of user-defined providers.
*/
private $providers;
/**
* @var ProviderFactory Internal factory of first-party providers.
*/
private $providerFactory;
/**
* Initializes this instance with the specified container of providers.
*
* @param ContainerInterface $providers Container of providers.
*/
public function __construct(ContainerInterface $providers)
{
$this->providers = $providers;
}
/**
* Imports data according to the design of the specified import specification.
*
* @param ImportSpecification $specification Import specification.
*
* @return PorterRecords|CountablePorterRecords
*
* @throws ImportException Provider failed to return an iterator.
*/
public function import(ImportSpecification $specification)
{
$specification = clone $specification;
$records = $this->fetch(
$specification->getResource(),
$specification->getProviderName(),
ConnectionContextFactory::create($specification)
);
if (!$records instanceof ProviderRecords) {
$records = $this->createProviderRecords($records, $specification->getResource());
}
$records = $this->transformRecords($records, $specification->getTransformers(), $specification->getContext());
return $this->createPorterRecords($records, $specification);
}
/**
* Imports one record according to the design of the specified import specification.
*
* @param ImportSpecification $specification Import specification.
*
* @return array|null Record.
*
* @throws ImportException More than one record was imported.
*/
public function importOne(ImportSpecification $specification)
{
$results = $this->import($specification);
if (!$results->valid()) {
return null;
}
$one = $results->current();
if ($results->next() || $results->valid()) {
throw new ImportException('Cannot import one: more than one record imported.');
}
return $one;
}
private function fetch(ProviderResource $resource, $providerName, ConnectionContext $context)
{
$provider = $this->getProvider($providerName ?: $resource->getProviderClassName());
if ($resource->getProviderClassName() !== get_class($provider)) {
throw new ForeignResourceException(sprintf(
'Cannot fetch data from foreign resource: "%s".',
get_class($resource)
));
}
$records = $resource->fetch(
new ImportConnector($provider->getConnector(get_class($resource)), $context),
$provider instanceof ProviderOptions ? clone $provider->getOptions() : null
);
if (!$records instanceof \Iterator) {
throw new ImportException(get_class($resource) . '::fetch() did not return an Iterator.');
}
return $records;
}
/**
* @param RecordCollection $records
* @param Transformer[] $transformers
* @param mixed $context
*
* @return RecordCollection
*/
private function transformRecords(RecordCollection $records, array $transformers, $context)
{
foreach ($transformers as $transformer) {
if ($transformer instanceof PorterAware) {
$transformer->setPorter($this);
}
$records = $transformer->transform($records, $context);
}
return $records;
}
private function createProviderRecords(\Iterator $records, ProviderResource $resource)
{
if ($records instanceof \Countable) {
return new CountableProviderRecords($records, count($records), $resource);
}
return new ProviderRecords($records, $resource);
}
private function createPorterRecords(RecordCollection $records, ImportSpecification $specification)
{
if ($records instanceof \Countable) {
return new CountablePorterRecords($records, count($records), $specification);
}
return new PorterRecords($records, $specification);
}
/**
* Gets the provider matching the specified name.
*
* @param string $name Provider name.
*
* @return Provider
*
* @throws ProviderNotFoundException The specified provider was not found.
*/
private function getProvider($name)
{
if ($this->providers->has($name)) {
return $this->providers->get($name);
}
try {
return $this->getOrCreateProviderFactory()->createProvider("$name");
} catch (ObjectNotCreatedException $exception) {
throw new ProviderNotFoundException("No such provider registered: \"$name\".", $exception);
}
}
private function getOrCreateProviderFactory()
{
return $this->providerFactory ?: $this->providerFactory = new ProviderFactory;
}
}