Failed Conditions
Pull Request — master (#26)
by Bob Olde
07:33
created

src/services/Fields.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace NerdsAndCompany\Schematic\Services;
4
5
use Craft\Craft;
6
use Craft\Exception;
7
use Craft\FieldModel;
8
use Craft\FieldGroupModel;
9
use Craft\FieldLayoutModel;
10
use Craft\ElementType;
11
use NerdsAndCompany\Schematic\Models\FieldFactory;
12
13
/**
14
 * Schematic Fields Service.
15
 *
16
 * Sync Craft Setups.
17
 *
18
 * @author    Nerds & Company
19
 * @copyright Copyright (c) 2015, Nerds & Company
20
 * @license   MIT
21
 *
22
 * @link      http://www.nerds.company
23
 */
24
class Fields extends Base
25
{
26
    /**
27
     * @var FieldModel[]
28
     */
29
    private $fields = [];
30
31
    /**
32
     * @var FieldGroupModel[]
33
     */
34
    private $groups = [];
35
36
    /**
37
     * @var FieldFactory
38
     */
39
    private $fieldFactory;
40
41
    /**
42
     * @return FieldFactory
43
     */
44
    public function getFieldFactory()
45
    {
46
        return isset($this->fieldFactory) ? $this->fieldFactory : new FieldFactory();
47
    }
48
49
    //==============================================================================================================
50
    //===============================================  SERVICES  ===================================================
51
    //==============================================================================================================
52
53
    /**
54
     * Returns fields service.
55
     *
56
     * @return FieldsService
57
     */
58
    private function getFieldsService()
59
    {
60
        return Craft::app()->fields;
61
    }
62
63
    /**
64
     * Returns content service.
65
     *
66
     * @return ContentService
67
     */
68
    private function getContentService()
69
    {
70
        return Craft::app()->content;
71
    }
72
73
    //==============================================================================================================
74
    //================================================  EXPORT  ====================================================
75
    //==============================================================================================================
76
77
    /**
78
     * Export fields.
79
     *
80
     * @param FieldGroupModel[] $groups
81
     *
82
     * @return array
83
     */
84
    public function export(array $groups = [])
85
    {
86
        Craft::log(Craft::t('Exporting Fields'));
87
88
        $groupDefinitions = [];
89
90
        foreach ($groups as $group) {
91
            $fieldDefinitions = [];
92
93
            foreach ($group->getFields() as $field) {
94
                $fieldDefinitions[$field->handle] = $this->getFieldDefinition($field);
95
            }
96
97
            $groupDefinitions[$group->name] = $fieldDefinitions;
98
        }
99
100
        return $groupDefinitions;
101
    }
102
103
    /**
104
     * Get field definition.
105
     *
106
     * @param FieldModel $field
107
     *
108
     * @return array
109
     */
110
    private function getFieldDefinition(FieldModel $field)
111
    {
112
        $fieldFactory = $this->getFieldFactory();
113
        $schematicFieldModel = $fieldFactory->build($field->type);
114
        $definition = $schematicFieldModel->getDefinition($field, true);
115
116
        return $definition;
117
    }
118
119
    //==============================================================================================================
120
    //================================================  IMPORT  ====================================================
121
    //==============================================================================================================
122
123
    /**
124
     * Attempt to import fields.
125
     *
126
     * @param array $groupDefinitions
127
     * @param bool  $force            if set to true items not in the import will be deleted
128
     *
129
     * @return Result
130
     */
131
    public function import(array $groupDefinitions, $force = false)
132
    {
133
        Craft::log(Craft::t('Importing Fields'));
134
135
        if (!empty($groupDefinitions)) {
136
            $contentService = $this->getContentService();
137
138
            $contentService->fieldContext = 'global';
139
            $contentService->contentTable = 'content';
140
141
            $this->groups = $this->getFieldsService()->getAllGroups('name');
142
            $this->fields = $this->getFieldsService()->getAllFields('handle');
143
144
            foreach ($groupDefinitions as $name => $fieldDefinitions) {
145
                try {
146
                    $this->beginTransaction();
147
148
                    $group = $this->createFieldGroupModel($name);
149
150
                    $this->importFields($fieldDefinitions, $group);
151
152
                    $this->commitTransaction();
153
                } catch (\Exception $e) {
154
                    $this->rollbackTransaction();
155
156
                    $this->addError($e->getMessage());
157
                }
158
159
                $this->unsetData($name, $fieldDefinitions);
160
            }
161
162
            if ($force) { // Remove not imported data
163
                $this->deleteFieldsAndGroups();
164
            }
165
        }
166
167
        return $this->getResultModel();
168
    }
169
170
    /**
171
     * Save field group.
172
     *
173
     * @param FieldGroupModel $group
174
     *
175
     * @throws Exception
176
     */
177
    private function saveFieldGroupModel(FieldGroupModel $group)
178
    {
179
        if (!$this->getFieldsService()->saveGroup($group)) {
180
            $this->addErrors($group->getAllErrors());
181
182
            throw new Exception('Failed to save group');
183
        }
184
    }
185
186
    /**
187
     * Save field.
188
     *
189
     * @param FieldModel $field
190
     *
191
     * @throws \Exception
192
     */
193
    private function saveFieldModel(FieldModel $field)
194
    {
195
        $this->validateFieldModel($field); // Validate field
196
        if (!$this->getFieldsService()->saveField($field)) {
197
            $this->addErrors($field->getAllErrors());
198
199
            throw new Exception('Failed to save field');
200
        }
201
    }
202
203
    /**
204
     * Removes fields that where not imported.
205
     */
206
    private function deleteFields()
207
    {
208
        $fieldsService = $this->getFieldsService();
209
        foreach ($this->fields as $field) {
210
            $fieldsService->deleteFieldById($field->id);
211
        }
212
    }
213
214
    /**
215
     * Removes groups that where not imported.
216
     */
217
    private function deleteGroups()
218
    {
219
        $fieldsService = $this->getFieldsService();
220
        foreach ($this->groups as $group) {
221
            $fieldsService->deleteGroupById($group->id);
222
        }
223
    }
224
225
    /**
226
     * Removes fields and groups that where not imported.
227
     */
228
    private function deleteFieldsAndGroups()
229
    {
230
        $this->deleteFields();
231
        $this->deleteGroups();
232
    }
233
234
    /**
235
     * Creates new or updates existing group model.
236
     *
237
     * @param string $group
238
     *
239
     * @return FieldGroupModel
240
     */
241
    private function createFieldGroupModel($group)
242
    {
243
        $groupModel = (array_key_exists($group, $this->groups) ? $this->groups[$group] : new FieldGroupModel());
244
        $groupModel->name = $group;
245
246
        $this->saveFieldGroupModel($groupModel);
247
248
        return $groupModel;
249
    }
250
251
    /**
252
     * @param string $field
253
     *
254
     * @return FieldModel
255
     */
256
    private function getFieldModel($field)
257
    {
258
        return (array_key_exists($field, $this->fields) ? $this->fields[$field] : new FieldModel());
259
    }
260
261
    /**
262
     * Validates field type, throw error when it's incorrect.
263
     *
264
     * @param FieldModel $field
265
     *
266
     * @throws \Exception
267
     */
268
    private function validateFieldModel(FieldModel $field)
269
    {
270
        if (!$field->getFieldType()) {
271
            $fieldType = $field->type;
272
            ($fieldType == 'Matrix')
273
                ? $this->addError("One of the field's types does not exist. Are you missing a plugin?")
274
                : $this->addError("Field type '$fieldType' does not exist. Are you missing a plugin?");
275
276
            throw new Exception('Failed to save field');
277
        }
278
    }
279
280
    /**
281
     * Import field group fields.
282
     *
283
     * @param array           $fieldDefinitions
284
     * @param FieldGroupModel $group
285
     *
286
     * @throws \Exception
287
     */
288
    private function importFields(array $fieldDefinitions, FieldGroupModel $group)
289
    {
290
        $fieldFactory = $this->getFieldFactory();
291
292
        foreach ($fieldDefinitions as $fieldHandle => $fieldDef) {
293
            $field = $this->getFieldModel($fieldHandle);
294
            $schematicFieldModel = $fieldFactory->build($fieldDef['type']);
295
            $schematicFieldModel->populate($fieldDef, $field, $fieldHandle, $group);
296
            $this->saveFieldModel($field);
297
        }
298
    }
299
300
    /**
301
     * Unset group and field data else $force flag will delete it.
302
     *
303
     * @param string $name
304
     * @param array  $definitions
305
     */
306
    private function unsetData($name, array $definitions)
307
    {
308
        if (array_key_exists($name, $this->groups)) {
309
            unset($this->groups[$name]);
310
            foreach ($definitions as $handle => $definition) {
311
                unset($this->fields[$handle]);
312
            }
313
        }
314
    }
315
316
    //==============================================================================================================
317
    //=============================================  FIELD LAYOUT  =================================================
318
    //==============================================================================================================
319
320
    /**
321
     * Get field layout definition.
322
     *
323
     * @param FieldLayoutModel $fieldLayout
324
     *
325
     * @return array
326
     */
327
    public function getFieldLayoutDefinition(FieldLayoutModel $fieldLayout)
328
    {
329
        if ($fieldLayout->getTabs()) {
330
            $tabDefinitions = [];
331
332
            foreach ($fieldLayout->getTabs() as $tab) {
333
                $tabDefinitions[$tab->name] = $this->getFieldLayoutFieldsDefinition($tab->getFields());
334
            }
335
336
            return ['tabs' => $tabDefinitions];
337
        }
338
339
        return ['fields' => $this->getFieldLayoutFieldsDefinition($fieldLayout->getFields(]));
0 ignored issues
show
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected ']'
Loading history...
340
    }
341
342
    /**
343
     * Get field layout fields definition.
344
     *
345
     * @param FieldLayoutFieldModel[] $fields
346
     *
347
     * @return array
348
     */
349
    private function getFieldLayoutFieldsDefinition(array $fields)
350
    {
351
        $fieldDefinitions = [];
352
353
        foreach ($fields as $field) {
354
            $fieldDefinitions[$field->getField()->handle] = $field->required;
355
        }
356
357
        return $fieldDefinitions;
358
    }
359
360
    /**
361
     * Attempt to import a field layout.
362
     *
363
     * @param array $fieldLayoutDef
364
     *
365
     * @return FieldLayoutModel
366
     */
367
    public function getFieldLayout(array $fieldLayoutDef)
368
    {
369
        $layoutFields = [];
370
        $requiredFields = [];
371
372
        if (array_key_exists('tabs', $fieldLayoutDef)) {
373
            foreach ($fieldLayoutDef['tabs'] as $tabName => $tabDef) {
374
                $layoutTabFields = $this->getPrepareFieldLayout($tabDef);
375
                $requiredFields = array_merge($requiredFields, $layoutTabFields['required']);
376
                $layoutFields[$tabName] = $layoutTabFields['fields'];
377
            }
378
        } elseif (array_key_exists('fields', $fieldLayoutDef)) {
379
            $layoutTabFields = $this->getPrepareFieldLayout($fieldLayoutDef);
380
            $requiredFields = $layoutTabFields['required'];
381
            $layoutFields = $layoutTabFields['fields'];
382
        }
383
384
        $fieldLayout = Craft::app()->fields->assembleLayout($layoutFields, $requiredFields);
385
        $fieldLayout->type = ElementType::Entry;
386
387
        return $fieldLayout;
388
    }
389
390
    /**
391
     * Get a prepared fieldLayout for the craft assembleLayout function.
392
     *
393
     * @param array $fieldLayoutDef
394
     *
395
     * @return array
396
     */
397
    private function getPrepareFieldLayout(array $fieldLayoutDef)
398
    {
399
        $layoutFields = [];
400
        $requiredFields = [];
401
402
        foreach ($fieldLayoutDef as $fieldHandle => $required) {
403
            $field = Craft::app()->fields->getFieldByHandle($fieldHandle);
404
405
            if ($field instanceof FieldModel) {
406
                $layoutFields[] = $field->id;
407
408
                if ($required) {
409
                    $requiredFields[] = $field->id;
410
                }
411
            }
412
        }
413
414
        return [
415
            'fields' => $layoutFields,
416
            'required' => $requiredFields,
417
        ];
418
    }
419
}
420