1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace BZIon\Form\Transformer; |
4
|
|
|
|
5
|
|
|
use Symfony\Component\Form\DataTransformerInterface; |
6
|
|
|
use Symfony\Component\Form\Exception\TransformationFailedException; |
7
|
|
|
|
8
|
|
|
abstract class AdvancedModelTransformer implements DataTransformerInterface |
9
|
|
|
{ |
10
|
|
|
/** |
11
|
|
|
* @var string[] |
12
|
|
|
*/ |
13
|
|
|
protected $types; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* @param string[] $types The types of the models |
17
|
|
|
*/ |
18
|
1 |
|
public function __construct(array $types) |
19
|
|
|
{ |
20
|
1 |
|
if (empty($types)) { |
21
|
|
|
throw new \Exception("No type has been specified"); |
22
|
|
|
} |
23
|
|
|
|
24
|
1 |
|
$this->types = $types; |
25
|
1 |
|
} |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Transforms an object (model) to the form representation |
29
|
|
|
* |
30
|
|
|
* @param \Model|\Model[]|null $models |
31
|
|
|
* @return array |
32
|
|
|
*/ |
33
|
1 |
|
public function transform($models) |
34
|
|
|
{ |
35
|
1 |
|
if ($models === null) { |
36
|
1 |
|
$models = array(); |
37
|
1 |
|
} elseif (!is_array($models)) { |
38
|
|
|
$models = array($models); |
39
|
|
|
} |
40
|
|
|
|
41
|
1 |
|
$data = $json = array(); |
42
|
1 |
|
foreach ($models as $model) { |
43
|
1 |
|
$data[$model->getType()][] = $model->getName(); |
44
|
1 |
|
$json[] = array( |
45
|
1 |
|
'id' => $model->getID(), |
46
|
1 |
|
'name' => $model->getName(), |
47
|
1 |
|
'type' => ucfirst($model->getType()) |
48
|
|
|
); |
49
|
|
|
} |
50
|
|
|
|
51
|
1 |
|
foreach ($data as $type => &$value) { |
52
|
1 |
|
$value = implode(', ', $value); |
53
|
|
|
} |
54
|
|
|
|
55
|
1 |
|
$data['ids'] = json_encode(array( |
56
|
1 |
|
'data' => $json |
57
|
|
|
)); |
58
|
|
|
|
59
|
1 |
|
return $data; |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Get an invalid model of an acceptable type |
64
|
|
|
* |
65
|
|
|
* @param string|null $type The type of the model, or null to select one of |
66
|
|
|
* the specified types |
67
|
|
|
* @return \Model |
68
|
|
|
*/ |
69
|
|
|
protected function invalidModel($type = null) |
70
|
|
|
{ |
71
|
|
|
if ($type === null) { |
72
|
|
|
$type = reset($this->types); // Get the first value of $this->types |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
$type = ucfirst($type); |
76
|
|
|
|
77
|
|
|
return call_user_func(array($type, 'invalid')); |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Get a model from its name |
82
|
|
|
* @param string $name The name of the model |
83
|
|
|
* @param string $type The type of the model in lower case |
84
|
|
|
* @return \NamedModel|null The model or null if no name was specified |
85
|
|
|
*/ |
86
|
1 |
|
protected function getModelFromName($name, $type) |
87
|
|
|
{ |
88
|
1 |
|
if ($name === '') { |
89
|
|
|
return null; |
90
|
|
|
} |
91
|
|
|
|
92
|
1 |
|
if ($type === 'player') { |
93
|
1 |
|
return \Player::getFromUsername($name); |
94
|
|
|
} elseif ($type === 'team') { |
95
|
|
|
return \Team::getFromName($name); |
96
|
|
|
} else { |
97
|
|
|
throw new \InvalidArgumentException('Unsupported model type'); |
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Transform JSON data provided by javascript to a list of Models |
103
|
|
|
* |
104
|
|
|
* @param string $data The JSON provided to us by javascript, containing |
105
|
|
|
* a list of Model IDs and types |
106
|
|
|
* @param array $include An array of Models of each type that will be |
107
|
|
|
* included in the final result |
108
|
|
|
* @return bool|\Model[] A list of models, or false if the data was not |
109
|
|
|
* provided by javascript as JSON |
110
|
|
|
*/ |
111
|
1 |
|
protected function transformJSON(&$data, $include = array()) |
112
|
|
|
{ |
113
|
1 |
|
$json = json_decode($data['ids'], true); |
114
|
|
|
|
115
|
1 |
|
if (!isset($json['modified']) || $json['modified'] !== true) { |
116
|
|
|
// The JSON data was not modified; we can proceed to check input |
117
|
|
|
// from other sources |
118
|
1 |
|
return false; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
// Array to store IDs for quick access so we can be sure that no |
122
|
|
|
// duplicates are saved |
123
|
|
|
$ids = array(); |
124
|
|
|
|
125
|
|
|
$models = array(); |
126
|
|
|
|
127
|
|
|
foreach ($include as $type => $includedModels) { |
128
|
|
|
foreach ($includedModels as $model) { |
129
|
|
|
$ids[$type][$model->getID()] = true; // Prevent duplication |
130
|
|
|
$models[] = $model; |
131
|
|
|
} |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
foreach ($json['data'] as $key => $object) { |
135
|
|
|
if ($key === 'modified') { |
136
|
|
|
// This is just an object that lets us know javascript provided |
137
|
|
|
// data, we should ignore it |
138
|
|
|
continue; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
if (!isset($object['id']) || !isset($object['type'])) { |
142
|
|
|
throw new TransformationFailedException( |
143
|
|
|
"Invalid model provided" |
144
|
|
|
); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
$type = strtolower($object['type']); |
148
|
|
|
|
149
|
|
|
// Sanity check so that the user can't generate arbitrary classes |
150
|
|
|
if (!in_array($type, $this->types)) { |
151
|
|
|
throw new TransformationFailedException( |
152
|
|
|
"Objects of type \"{$object['type']}\" are not supported" |
153
|
|
|
); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
$class = ucfirst($object['type']); |
157
|
|
|
$model = $class::get($object['id']); |
158
|
|
|
|
159
|
|
View Code Duplication |
if ($model->isDeleted()) { |
|
|
|
|
160
|
|
|
// Show an error message if the model provided by javascript is |
161
|
|
|
// invalid - we don't let the validator handle this error, so |
162
|
|
|
// that the user doesn't see a vague warning |
163
|
|
|
throw new TransformationFailedException( |
164
|
|
|
"Invalid model ID provided" |
165
|
|
|
); |
166
|
|
|
} elseif (!isset($ids[$type][$model->getID()])) { |
167
|
|
|
// The model passed the duplication check |
168
|
|
|
$models[] = $model; |
169
|
|
|
$ids[$type][$model->getID()] = true; |
170
|
|
|
} |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
return $models; |
174
|
|
|
} |
175
|
|
|
} |
176
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.