Completed
Push — master ( 77c4c6...fbe049 )
by Ori
04:20
created

Schema::fields()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 2
nop 0
dl 0
loc 15
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
namespace frictionlessdata\tableschema;
4
5
/**
6
 *  Table Schema representation.
7
 *  Loads and validates a Table Schema descriptor from a descriptor / path to file / url containing the descriptor.
8
 */
9
class Schema
10
{
11
    protected $DEFAULT_FIELD_CLASS = '\\frictionlessdata\\tableschema\\Fields\\StringField';
12
13
    /**
14
     * Schema constructor.
15
     *
16
     * @param mixed $descriptor
17
     *
18
     * @throws Exceptions\SchemaLoadException
19
     * @throws Exceptions\SchemaValidationFailedException
20
     */
21
    public function __construct($descriptor)
22
    {
23
        if (Utils::isJsonString($descriptor)) {
24
            // it's a json encoded string
25
            try {
26
                $this->descriptor = json_decode($descriptor);
27
            } catch (\Exception $e) {
28
                throw new Exceptions\SchemaLoadException($descriptor, null, $e->getMessage());
29
            }
30
        } elseif (is_string($descriptor)) {
31
            // it's a url or file path
32
            $descriptorSource = $descriptor;
33
            try {
34
                $descriptor = file_get_contents($descriptorSource);
35
            } catch (\Exception $e) {
36
                throw new Exceptions\SchemaLoadException(null, $descriptorSource, $e->getMessage());
37
            }
38
            try {
39
                $this->descriptor = json_decode($descriptor);
40
            } catch (\Exception $e) {
41
                throw new Exceptions\SchemaLoadException($descriptor, $descriptorSource, $e->getMessage());
42
            }
43
        } else {
44
            $this->descriptor = $descriptor;
45
        }
46
        if (!is_object($this->descriptor())) {
47
            throw new Exceptions\SchemaLoadException($descriptor, null, 'descriptor must be an object');
48
        }
49
        $validationErrors = SchemaValidator::validate($this->descriptor());
50
        if (count($validationErrors) > 0) {
51
            throw new Exceptions\SchemaValidationFailedException($validationErrors);
52
        }
53
    }
54
55
    /**
56
     * loads and validates the given descriptor source (php object / string / path to file / url)
57
     * returns an array of validation error objects.
58
     *
59
     * @param mixed $descriptor
60
     *
61
     * @return array
62
     */
63
    public static function validate($descriptor)
64
    {
65
        try {
66
            new static($descriptor);
67
68
            return [];
69
        } catch (Exceptions\SchemaLoadException $e) {
70
            return [
71
                new SchemaValidationError(SchemaValidationError::LOAD_FAILED, $e->getMessage()),
72
            ];
73
        } catch (Exceptions\SchemaValidationFailedException $e) {
74
            return $e->validationErrors;
75
        }
76
    }
77
78
    /**
79
     * @return object
80
     */
81
    public function descriptor()
82
    {
83
        return $this->descriptor;
84
    }
85
86
    public function fullDescriptor()
87
    {
88
        $fullDescriptor = $this->descriptor();
89
        $fullFieldDescriptors = [];
90
        foreach ($this->fields() as $field) {
91
            $fullFieldDescriptors[] = $field->fullDescriptor();
92
        }
93
        $fullDescriptor->fields = $fullFieldDescriptors;
94
        $fullDescriptor->missingValues = $this->missingValues();
95
96
        return $fullDescriptor;
97
    }
98
99
    public function field($name)
100
    {
101
        $fields = $this->fields();
102
        if (array_key_exists($name, $fields)) {
103
            return $fields[$name];
104
        } else {
105
            throw new \Exception("unknown field name: {$name}");
106
        }
107
    }
108
109
    /**
110
     * @return Fields\BaseField[] array of field name => field object
111
     */
112
    public function fields()
113
    {
114
        if (empty($this->fieldsCache)) {
115
            foreach ($this->descriptor()->fields as $fieldDescriptor) {
116
                if (!array_key_exists('type', $fieldDescriptor)) {
117
                    $field = new $this->DEFAULT_FIELD_CLASS($fieldDescriptor);
118
                } else {
119
                    $field = Fields\FieldsFactory::field($fieldDescriptor);
120
                }
121
                $this->fieldsCache[$field->name()] = $field;
122
            }
123
        }
124
125
        return $this->fieldsCache;
126
    }
127
128
    public function missingValues()
129
    {
130
        return isset($this->descriptor()->missingValues) ? $this->descriptor()->missingValues : [''];
131
    }
132
133
    public function primaryKey()
134
    {
135
        $primaryKey = isset($this->descriptor()->primaryKey) ? $this->descriptor()->primaryKey : [];
136
137
        return is_array($primaryKey) ? $primaryKey : [$primaryKey];
138
    }
139
140
    public function foreignKeys()
141
    {
142
        $foreignKeys = isset($this->descriptor()->foreignKeys) ? $this->descriptor()->foreignKeys : [];
143
        foreach ($foreignKeys as &$foreignKey) {
144
            if (!is_array($foreignKey->fields)) {
145
                $foreignKey->fields = [$foreignKey->fields];
146
            }
147
            if (!is_array($foreignKey->reference->fields)) {
148
                $foreignKey->reference->fields = [$foreignKey->reference->fields];
149
            }
150
        }
151
152
        return $foreignKeys;
153
    }
154
155
    /**
156
     * @param mixed[] $row
157
     *
158
     * @return mixed[]
159
     *
160
     * @throws Exceptions\FieldValidationException
161
     */
162
    public function castRow($row)
163
    {
164
        $outRow = [];
165
        $validationErrors = [];
166
        foreach ($this->fields() as $fieldName => $field) {
167
            $value = array_key_exists($fieldName, $row) ? $row[$fieldName] : null;
168
            if (in_array($value, $this->missingValues())) {
169
                $value = null;
170
            }
171
            try {
172
                $outRow[$fieldName] = $field->castValue($value);
173
            } catch (Exceptions\FieldValidationException $e) {
174
                $validationErrors = array_merge($validationErrors, $e->validationErrors);
175
            }
176
        }
177
        if (count($validationErrors) > 0) {
178
            throw new Exceptions\FieldValidationException($validationErrors);
179
        }
180
181
        return $outRow;
182
    }
183
184
    /**
185
     * @param array $row
186
     *
187
     * @return SchemaValidationError[]
188
     */
189
    public function validateRow($row)
190
    {
191
        try {
192
            $this->castRow($row);
193
194
            return [];
195
        } catch (Exceptions\FieldValidationException $e) {
196
            return $e->validationErrors;
197
        }
198
    }
199
200
    public function save($filename)
201
    {
202
        file_put_contents($filename, json_encode($this->fullDescriptor()));
203
    }
204
205
    protected $descriptor;
206
    protected $fieldsCache = null;
207
}
208