Completed
Push — master ( 8fd216...6ebbcb )
by Jelle
03:53
created

EntityManager::sanitizeProperty()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 16
rs 8.2222
cc 7
eloc 10
nc 6
nop 2
1
<?php
2
/**
3
 * @file
4
 * Contains \TheSportsDb\Entity\EntityManager.
5
 */
6
7
namespace TheSportsDb\Entity;
8
9
use FastNorth\PropertyMapper\Map;
10
use FastNorth\PropertyMapper\MapperInterface;
11
use TheSportsDb\Entity\Factory\FactoryContainerInterface;
12
use TheSportsDb\Entity\Repository\RepositoryContainerInterface;
13
use TheSportsDb\PropertyMapper\PropertyDefinition;
14
use TheSportsDb\PropertyMapper\Transformer\Callback;
15
16
/**
17
 * Default implementation for entity managers.
18
 *
19
 * @author Jelle Sebreghts
20
 */
21
class EntityManager implements EntityManagerInterface {
22
23
  /**
24
   * The factory container.
25
   *
26
   * @var \TheSportsDb\Entity\Factory\FactoryContainerInterface
27
   */
28
  protected $factoryContainer;
29
30
  /**
31
   * The repository container.
32
   *
33
   * @var \TheSportsDb\Entity\Repository\RepositoryContainerInterface
34
   */
35
  protected $repositoryContainer;
36
37
  /**
38
   * Map entity types to classes.
39
   *
40
   * @var array
41
   */
42
  protected $classes = array();
43
44
  /**
45
   * Property map definitions.
46
   *
47
   * @var array
48
   */
49
  protected $propertyMapDefinitions = array();
50
51
  /**
52
   * Property map definitions.
53
   *
54
   * @var array
55
   */
56
  protected $propertyMaps = array();
57
58
  /**
59
   * The property mapper.
60
   *
61
   * @var \FastNorth\PropertyMapper\MapperInterface
62
   */
63
  protected $propertyMapper;
64
65
66
  /**
67
   * Placeholder for empty properties.
68
   *
69
   * @var string
70
   */
71
  const EMPTYPROPERTYPLACEHOLDER = '__EMPTY_PROPERTY_PLACEHOLDER__';
72
73
74
  /**
75
   * {@inheritdoc}
76
   */
77
  public function __construct(MapperInterface $propertyMapper, FactoryContainerInterface $factoryContainer = NULL, RepositoryContainerInterface $repositoryContainer = NULL) {
78
    if ($factoryContainer instanceof FactoryContainerInterface) {
79
      $this->factoryContainer = $factoryContainer;
80
    }
81
    if ($repositoryContainer instanceof RepositoryContainerInterface) {
82
      $this->repositoryContainer = $repositoryContainer;
83
    }
84
    $this->propertyMapper = $propertyMapper;
85
  }
86
87
  /**
88
   * {@inheritdoc}
89
   */
90
  public function setFactoryContainer(FactoryContainerInterface $factoryContainer) {
91
    if ($this->factoryContainer instanceof FactoryContainerInterface) {
92
      throw new \Exception('Factory container already set.');
93
    }
94
    $this->factoryContainer = $factoryContainer;
95
  }
96
97
  /**
98
   * {@inheritdoc}
99
   */
100
  public function setRepositoryContainer(RepositoryContainerInterface $repositoryContainer) {
101
    if ($this->repositoryContainer instanceof RepositoryContainerInterface) {
102
      throw new \Exception('Repository container already set.');
103
    }
104
    $this->repositoryContainer = $repositoryContainer;
105
  }
106
107
108
  /**
109
   * {@inheritdoc}
110
   */
111
  public function repository($entityType) {
112
    if ($this->repositoryContainer instanceof RepositoryContainerInterface) {
113
      return $this->repositoryContainer->getRepository($entityType);
114
    }
115
    throw new \Exception('No repository container set.');
116
  }
117
118
  /**
119
   * {@inheritdoc}
120
   */
121
  public function factory($entityType) {
122
    if ($this->factoryContainer instanceof FactoryContainerInterface) {
123
      return $this->factoryContainer->getFactory($entityType);
124
    }
125
    throw new \Exception('No factory container set.');
126
  }
127
128
  /**
129
   * {@inheritdoc}
130
   */
131
  public function registerClass($entityType, $realClass = NULL, $proxyClass = NULL) {
132
    if (is_null($realClass)) {
133
      $realClass = (new \ReflectionClass(static::class))->getNamespaceName() . '\\' . ucfirst($entityType);
134
    }
135
    if (is_null($proxyClass)) {
136
      $proxyClass = (new \ReflectionClass($realClass))->getNamespaceName() . '\\Proxy\\' . ucfirst($entityType) . 'Proxy';
137
    }
138
    if (!class_exists($realClass)) {
139
      throw new \Exception('Class ' . $realClass . 'not found.');
140
    }
141
    if (!class_exists($proxyClass)) {
142
      throw new \Exception('Class ' . $proxyClass . 'not found.');
143
    }
144
    $this->classes[$entityType] = array(
145
      'real' => $realClass,
146
      'proxy' => $proxyClass
147
    );
148
    return $this->classes[$entityType];
149
  }
150
151
  /**
152
   * {@inheritdoc}
153
   */
154
  public function getPropertyMapDefinition($entityType) {
155
    if (!isset($this->propertyMapDefinitions[$entityType])) {
156
      $propertyMapDefinition = new \ReflectionMethod($this->getClass($entityType), 'getPropertyMapDefinition');
157
      $this->propertyMapDefinitions[$entityType] = $propertyMapDefinition->invoke(NULL);
158
    }
159
    return $this->propertyMapDefinitions[$entityType];
160
  }
161
162
  /**
163
   * {@inheritdoc}
164
   */
165
  public function getClass($entityType, $type = 'real') {
166
    if (!isset($this->classes[$entityType][$type])) {
167
      throw new \Exception(ucfirst($type) . ' class for ' . $entityType . ' not registered.');
168
    }
169
    return $this->classes[$entityType][$type];
170
  }
171
172
  /**
173
   * {@inheritdoc}
174
   */
175
  public function mapProperties(\stdClass $values, $entityType) {
176
    $mapped = new \stdClass();
177
    foreach ($this->getPropertyMapDefinition($entityType)->getPropertyMaps() as $map) {
178
      // The property on the destination must exist or it will not map.
179
      $mapped->{$map->getDestination()->getName()} = NULL;
180
      if (!isset($values->{$map->getSource()->getName()})) {
181
        // If a source property is not set, we must unset it on the destination
182
        // later on, so give it a value we can recognize.
183
        $values->{$map->getSource()->getName()} = static::EMPTYPROPERTYPLACEHOLDER;
184
      }
185
    }
186
    return $this->sanitizeObject($this->propertyMapper->process($values, $mapped, $this->getPropertyMap($entityType)));
187
  }
188
189
190
  /**
191
   * {@inheritdoc}
192
   */
193
  public function reverseMapProperties(\stdClass $values, $entityType) {
194
    $reversed = new \stdClass();
195
    foreach ($this->getPropertyMapDefinition($entityType)->getPropertyMaps() as $map) {
196
      if (!isset($reversed->{$map->getSource()->getName()})) {
197
        $reversed->{$map->getSource()->getName()} = static::EMPTYPROPERTYPLACEHOLDER;
198
      }
199
      if (!isset($values->{$map->getDestination()->getName()})) {
200
        $values->{$map->getDestination()->getName()} = static::EMPTYPROPERTYPLACEHOLDER;
201
      }
202
    }
203
    return $this->sanitizeObject($this->propertyMapper->reverse($reversed, $values, $this->getPropertyMap($entityType)));
204
  }
205
206
  /**
207
   * {@inheritdoc}
208
   */
209
  public function isFullObject(\stdClass $object, $entityType) {
210
    $reflection = new \ReflectionClass($this->getClass($entityType));
211
    $defaultProperties = $reflection->getDefaultProperties();
212
    $properties = array_flip(array_filter(array_keys($defaultProperties), function($prop) use ($reflection) {
213
      // Filter out static properties.
214
      return !$reflection->getProperty($prop)->isStatic();
215
    }));
216
    return count(array_intersect_key($properties, (array) $object)) === count($properties);
217
  }
218
219
  /**
220
   * Initializes the property map.
221
   *
222
   * @param string $entityType
223
   *   The entity type to initialize the property map for.
224
   *
225
   * @return void
226
   */
227
  protected function initPropertyMap($entityType) {
228
    $this->propertyMaps[$entityType] = new Map();
229
    $entityManager = $this;
230
    foreach ($this->getPropertyMapDefinition($entityType)->getPropertyMaps() as $map) {
231
      $args = [
232
        $map->getSource()->getName(),
233
        $map->getDestination()->getName(),
234
      ];
235
      if ($map->getTransform()) {
236
        $transform = $map->getTransform();
237
        $reverse = $map->getReverse();
238
        $args[] = new Callback(
239
240
          /**
241
           * @param string $value
242
           */
243
          function($value, $context) use ($entityManager, $transform) {
244
            if ($entityManager->isEmptyValue($value)) {
245
              return $value;
246
            }
247
            return call_user_func_array($transform, array($value, $context, $entityManager));
248
          },
249
250
          /**
251
           * @param string $value
252
           */
253
          function($value, $context) use ($entityManager, $reverse) {
254
            if ($entityManager->isEmptyValue($value)) {
255
              return $value;
256
            }
257
            return call_user_func_array($reverse, array($value, $context, $entityManager));
258
          }
259
        );
260
      }
261
      call_user_func_array(array($this->propertyMaps[$entityType], 'map'), $args);
262
    }
263
  }
264
265
  /**
266
   * Gets the property map.
267
   *
268
   * @param string $entityType
269
   *   The entity type to get the map for.
270
   *
271
   * @return \FastNorth\PropertyMapper\MapInterface
272
   *   The property map.
273
   */
274
  protected function getPropertyMap($entityType) {
275
    if (!isset($this->propertyMaps[$entityType])) {
276
      $this->initPropertyMap($entityType);
277
    }
278
    return $this->propertyMaps[$entityType];
279
  }
280
281
  /**
282
   * Sanitize empty values from an object.
283
   *
284
   * @param \stdClass $object
285
   *   The object to sanitize.
286
   *
287
   * @return \stdClass
288
   *   The sanitized object.
289
   */
290
  protected function sanitizeObject(\stdClass $object) {
291
    $arr = (array) $object;
292
    foreach ($arr as $prop => $val) {
293
      if ($this->isEmptyValue($val)) {
294
        unset($arr[$prop]);
295
      }
296
    }
297
    return (object) $arr;
298
  }
299
300
  /**
301
   * {@inheritdoc}
302
   */
303
  public function isEmptyValue($value) {
304
    return $value === static::EMPTYPROPERTYPLACEHOLDER;
305
  }
306
307
  /**
308
   * {@inheritdoc}
309
   */
310
  public function sanitizeValues(\stdClass &$values, $entityType) {
311
    foreach ($this->getPropertyMapDefinition($entityType)->getPropertyMaps() as $map) {
312
      $map->getDestination()->sanitizeProperty($values, $this->factoryContainer);
0 ignored issues
show
Bug introduced by
The method sanitizeProperty() cannot be called from this context as it is declared protected in class TheSportsDb\PropertyMapper\PropertyDefinition.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
313
    }
314
  }
315
}
316