Completed
Pull Request — master (#55)
by Bart
05:00
created

Sections::resetCraftFieldsSectionModelCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 8
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 8
loc 8
ccs 0
cts 7
cp 0
rs 9.4285
cc 1
eloc 6
nc 1
nop 1
crap 2
1
<?php
2
3
namespace NerdsAndCompany\Schematic\Services;
4
5
use Craft\Craft;
6
use Craft\SectionRecord;
7
use Craft\SectionModel;
8
use Craft\SectionLocaleModel;
9
use Craft\EntryTypeModel;
10
11
/**
12
 * Schematic Result Model.
13
 *
14
 * Sync Craft Setups.
15
 *
16
 * @author    Nerds & Company
17
 * @copyright Copyright (c) 2015-2016, Nerds & Company
18
 * @license   MIT
19
 *
20
 * @link      http://www.nerds.company
21
 */
22
class Sections extends Base
23
{
24
    /**
25
     * Export sections.
26
     *
27
     * @param SectionModel[] $sections
28
     * @param array|null     $allowedEntryTypeIds
29
     *
30
     * @return array
31
     */
32
    public function export(array $sections = [], array $allowedEntryTypeIds = null)
33
    {
34
        Craft::log(Craft::t('Exporting Sections'));
35
36
        $sectionDefinitions = [];
37
38
        foreach ($sections as $section) {
39
            $sectionDefinitions[$section->handle] = $this->getSectionDefinition($section, $allowedEntryTypeIds);
40
        }
41
42
        return $sectionDefinitions;
43
    }
44
45
    /**
46
     * Get section definition.
47
     *
48
     * @param SectionModel $section
49
     * @param $allowedEntryTypeIds
50
     *
51
     * @return array
52
     */
53
    private function getSectionDefinition(SectionModel $section, $allowedEntryTypeIds)
54
    {
55
        return [
56
            'name' => $section->name,
57
            'type' => $section->type,
58
            'hasUrls' => $section->hasUrls,
59
            'template' => $section->template,
60
            'maxLevels' => $section->maxLevels,
61
            'enableVersioning' => $section->enableVersioning,
62
            'locales' => $this->getLocaleDefinitions($section->getLocales()),
63
            'entryTypes' => $this->getEntryTypeDefinitions($section->getEntryTypes(), $allowedEntryTypeIds),
64
        ];
65
    }
66
67
    /**
68
     * Get locale definitions.
69
     *
70
     * @param SectionLocaleModel[] $locales
71
     *
72
     * @return array
73
     */
74 View Code Duplication
    private function getLocaleDefinitions(array $locales)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
75
    {
76
        $localeDefinitions = [];
77
78
        foreach ($locales as $locale) {
79
            $localeDefinitions[$locale->locale] = $this->getLocaleDefinition($locale);
80
        }
81
82
        return $localeDefinitions;
83
    }
84
85
    /**
86
     * Get locale definition.
87
     *
88
     * @param SectionLocaleModel $locale
89
     *
90
     * @return array
91
     */
92
    private function getLocaleDefinition(SectionLocaleModel $locale)
93
    {
94
        return [
95
            'enabledByDefault' => $locale->enabledByDefault,
96
            'urlFormat' => $locale->urlFormat,
97
            'nestedUrlFormat' => $locale->nestedUrlFormat,
98
        ];
99
    }
100
101
    /**
102
     * Get entry type definitions.
103
     *
104
     * @param array $entryTypes
105
     * @param $allowedEntryTypeIds
106
     *
107
     * @return array
108
     */
109
    private function getEntryTypeDefinitions(array $entryTypes, $allowedEntryTypeIds)
110
    {
111
        $entryTypeDefinitions = [];
112
113
        foreach ($entryTypes as $entryType) {
114
            if ($allowedEntryTypeIds === null || in_array($entryType->id, $allowedEntryTypeIds)) {
115
                $entryTypeDefinitions[$entryType->handle] = $this->getEntryTypeDefinition($entryType);
116
            }
117
        }
118
119
        return $entryTypeDefinitions;
120
    }
121
122
    /**
123
     * Get entry type definition.
124
     *
125
     * @param EntryTypeModel $entryType
126
     *
127
     * @return array
128
     */
129
    private function getEntryTypeDefinition(EntryTypeModel $entryType)
130
    {
131
        return [
132
            'name' => $entryType->name,
133
            'hasTitleField' => $entryType->hasTitleField,
134
            'titleLabel' => $entryType->titleLabel,
135
            'titleFormat' => $entryType->titleFormat,
136
            'fieldLayout' => Craft::app()->schematic_fields->getFieldLayoutDefinition($entryType->getFieldLayout()),
137
        ];
138
    }
139
140
    /**
141
     * Attempt to import sections.
142
     *
143
     * @param array $sectionDefinitions
144
     * @param bool  $force              If set to true sections not included in the import will be deleted
145
     *
146
     * @return Result
147
     */
148
    public function import(array $sectionDefinitions, $force = false)
149
    {
150
        Craft::log(Craft::t('Importing Sections'));
151
152
        $sections = Craft::app()->sections->getAllSections('handle');
153
154
        foreach ($sectionDefinitions as $sectionHandle => $sectionDefinition) {
155
            $section = array_key_exists($sectionHandle, $sections)
156
                ? $sections[$sectionHandle]
157
                : new SectionModel();
158
159
            unset($sections[$sectionHandle]);
160
161
            if($sectionDefinition === $this->getSectionDefinition($section, null)){
162
                Craft::log(Craft::t('Skipping `{name}`, no changes detected', ['name' => $section->name]));
163
                continue;
164
            }
165
166
            if (!array_key_exists('locales', $sectionDefinition)) {
167
                $this->addError('`sections[handle].locales` must be defined');
168
169
                continue;
170
            }
171
172
            if (!array_key_exists('entryTypes', $sectionDefinition)) {
173
                $this->addError('errors', '`sections[handle].entryTypes` must exist be defined');
174
175
                continue;
176
            }
177
178
            Craft::log(Craft::t('Importing section `{name}`', ['name' => $sectionDefinition['name']]));
179
180
            $this->populateSection($section, $sectionDefinition, $sectionHandle);
181
            $this->resetCraftFieldsSectionModelCache($section);
182
183
            // Create initial section record
184
            if (!$this->preSaveSection($section)) {
185
                $this->addErrors($section->getAllErrors());
186
187
                continue;
188
            }
189
190
            $this->importEntryTypes($section, $sectionDefinition['entryTypes'], $force);
191
192
            // Save section via craft after entrytypes have been created
193
            if (!Craft::app()->sections->saveSection($section)) {
194
                $this->addErrors($section->getAllErrors());
195
            }
196
        }
197
198
        if ($force) {
199
            foreach ($sections as $section) {
200
                Craft::app()->sections->deleteSectionById($section->id);
201
            }
202
        }
203
204
        return $this->getResultModel();
205
    }
206
207
    /**
208
     * @param SectionModel $section
209
     * @param array $entryTypeDefinitions
210
     * @param bool $force
211
     */
212
    private function importEntryTypes(SectionModel $section, array $entryTypeDefinitions, $force)
213
    {
214
        $entryTypes = Craft::app()->sections->getEntryTypesBySectionId($section->id, 'handle');
215
216
        foreach ($entryTypeDefinitions as $entryTypeHandle => $entryTypeDefinition) {
217
            $entryType = array_key_exists($entryTypeHandle, $entryTypes)
218
                ? $entryTypes[$entryTypeHandle]
219
                : new EntryTypeModel();
220
221
            unset($entryTypes[$entryTypeHandle]);
222
223
            $this->populateEntryType($entryType, $entryTypeDefinition, $entryTypeHandle, $section->id);
224
225
            if (!Craft::app()->sections->saveEntryType($entryType)) {
226
                $this->addError($entryType->getAllErrors());
227
228
                continue;
229
            }
230
        }
231
232
        if ($force) {
233
            foreach ($entryTypes as $entryType) {
234
                Craft::app()->sections->deleteEntryTypeById($entryType->id);
235
            }
236
        }
237
    }
238
239
    /**
240
     * Save the section manually if it is new to prevent craft from creating the default entry type
241
     * In case of a single we do want the default entry type and do a normal save
242
     * Todo: This method is a bit hackish, find a better way.
243
     *
244
     * @param SectionModel $section
245
     *
246
     * @return mixed
247
     */
248
    private function preSaveSection(SectionModel $section)
249
    {
250
        if ($section->type != 'single' && !$section->id) {
251
            $sectionRecord = new SectionRecord();
252
253
            // Shared attributes
254
            $sectionRecord->name = $section->name;
255
            $sectionRecord->handle = $section->handle;
256
            $sectionRecord->type = $section->type;
257
            $sectionRecord->enableVersioning = $section->enableVersioning;
258
259
            if (!$sectionRecord->save()) {
260
                $section->addErrors(['errors' => $sectionRecord->getErrors()]);
261
262
                return false;
263
            };
264
            $section->id = $sectionRecord->id;
265
266
            return true;
267
        }
268
269
        return Craft::app()->sections->saveSection($section);
270
    }
271
272
    /**
273
     * Populate section.
274
     *
275
     * @param SectionModel $section
276
     * @param array        $sectionDefinition
277
     * @param string       $sectionHandle
278
     */
279
    private function populateSection(SectionModel $section, array $sectionDefinition, $sectionHandle)
280
    {
281
        $section->setAttributes([
282
            'handle' => $sectionHandle,
283
            'name' => $sectionDefinition['name'],
284
            'type' => $sectionDefinition['type'],
285
            'hasUrls' => $sectionDefinition['hasUrls'],
286
            'template' => $sectionDefinition['template'],
287
            'maxLevels' => $sectionDefinition['maxLevels'],
288
            'enableVersioning' => $sectionDefinition['enableVersioning'],
289
        ]);
290
291
        $this->populateSectionLocales($section, $sectionDefinition['locales']);
292
    }
293
294
    /**
295
     * Populate section locales.
296
     *
297
     * @param SectionModel $section
298
     * @param $localeDefinitions
299
     */
300 View Code Duplication
    private function populateSectionLocales(SectionModel $section, $localeDefinitions)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
301
    {
302
        $locales = $section->getLocales();
303
304
        foreach ($localeDefinitions as $localeId => $localeDef) {
305
            $locale = array_key_exists($localeId, $locales) ? $locales[$localeId] : new SectionLocaleModel();
306
307
            $locale->setAttributes([
308
                'locale' => $localeId,
309
                'enabledByDefault' => $localeDef['enabledByDefault'],
310
                'urlFormat' => $localeDef['urlFormat'],
311
                'nestedUrlFormat' => $localeDef['nestedUrlFormat'],
312
            ]);
313
314
            // Todo: Is this a hack? I don't see another way.
315
            // Todo: Might need a sorting order as well? It's NULL at the moment.
316
            Craft::app()->db->createCommand()->insertOrUpdate('locales', [
317
                'locale' => $locale->locale,
318
            ], []);
319
320
            $locales[$localeId] = $locale;
321
        }
322
323
        $section->setLocales($locales);
324
    }
325
326
    /**
327
     * Populate entry type.
328
     *
329
     * @param EntryTypeModel $entryType
330
     * @param array          $entryTypeDefinition
331
     * @param string         $entryTypeHandle
332
     * @param int            $sectionId
333
     */
334 View Code Duplication
    private function populateEntryType(EntryTypeModel $entryType, array $entryTypeDefinition, $entryTypeHandle, $sectionId)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
335
    {
336
        $entryType->setAttributes([
337
            'handle' => $entryTypeHandle,
338
            'sectionId' => $sectionId,
339
            'name' => $entryTypeDefinition['name'],
340
            'hasTitleField' => $entryTypeDefinition['hasTitleField'],
341
            'titleLabel' => $entryTypeDefinition['titleLabel'],
342
            'titleFormat' => $entryTypeDefinition['titleFormat'],
343
        ]);
344
345
        $fieldLayout = Craft::app()->schematic_fields->getFieldLayout($entryTypeDefinition['fieldLayout']);
346
        $entryType->setFieldLayout($fieldLayout);
347
    }
348
349
    /**
350
     * Reset craft section model cache using reflection
351
     * @param SectionModel $section
352
     */
353 View Code Duplication
    private function resetCraftFieldsSectionModelCache(SectionModel $section)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
354
    {
355
        $obj         = $section;
356
        $refObject   = new \ReflectionObject( $obj );
357
        $refProperty = $refObject->getProperty( '_entryTypes' );
358
        $refProperty->setAccessible( true );
359
        $refProperty->setValue($obj, null);
360
    }
361
}
362