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\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, 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 | private function getLocaleDefinitions(array $locales) |
||
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 (!array_key_exists('locales', $sectionDefinition)) { |
||
162 | $this->addError('`sections[handle].locales` must be defined'); |
||
163 | |||
164 | continue; |
||
165 | } |
||
166 | |||
167 | if (!array_key_exists('entryTypes', $sectionDefinition)) { |
||
168 | $this->addError('errors', '`sections[handle].entryTypes` must exist be defined'); |
||
169 | |||
170 | continue; |
||
171 | } |
||
172 | |||
173 | $this->populateSection($section, $sectionDefinition, $sectionHandle); |
||
174 | |||
175 | // Create initial section record |
||
176 | if (!$this->preSaveSection($section)) { |
||
177 | $this->addErrors($section->getAllErrors()); |
||
178 | |||
179 | continue; |
||
180 | } |
||
181 | |||
182 | $entryTypes = $section->getEntryTypes('handle'); |
||
183 | |||
184 | foreach ($sectionDefinition['entryTypes'] as $entryTypeHandle => $entryTypeDefinition) { |
||
185 | $entryType = array_key_exists($entryTypeHandle, $entryTypes) |
||
186 | ? $entryTypes[$entryTypeHandle] |
||
187 | : new EntryTypeModel(); |
||
188 | |||
189 | $this->populateEntryType($entryType, $entryTypeDefinition, $entryTypeHandle, $section->id); |
||
190 | |||
191 | if (!Craft::app()->sections->saveEntryType($entryType)) { |
||
192 | $this->addError($entryType->getAllErrors()); |
||
193 | |||
194 | continue; |
||
195 | } |
||
196 | } |
||
197 | |||
198 | // Save section via craft after entrytypes have been created |
||
199 | if (!Craft::app()->sections->saveSection($section)) { |
||
200 | $this->addErrors($section->getAllErrors()); |
||
201 | } |
||
202 | } |
||
203 | |||
204 | if ($force) { |
||
205 | foreach ($sections as $section) { |
||
206 | Craft::app()->sections->deleteSectionById($section->id); |
||
207 | } |
||
208 | } |
||
209 | |||
210 | return $this->getResultModel(); |
||
211 | } |
||
212 | |||
213 | /** |
||
214 | * Save the section manually if it is new to prevent craft from creating the default entry type |
||
215 | * In case of a single we do want the default entry type and do a normal save |
||
216 | * Todo: This method is a bit hackish, find a better way. |
||
217 | * |
||
218 | * @param SectionModel $section |
||
219 | * |
||
220 | * @return mixed |
||
221 | */ |
||
222 | private function preSaveSection(SectionModel $section) |
||
223 | { |
||
224 | if ($section->type != 'single' && !$section->id) { |
||
225 | $sectionRecord = new SectionRecord(); |
||
226 | |||
227 | // Shared attributes |
||
228 | $sectionRecord->name = $section->name; |
||
229 | $sectionRecord->handle = $section->handle; |
||
230 | $sectionRecord->type = $section->type; |
||
231 | $sectionRecord->enableVersioning = $section->enableVersioning; |
||
232 | |||
233 | if (!$sectionRecord->save()) { |
||
234 | $section->addErrors(['errors' => $sectionRecord->getErrors(])); |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
235 | |||
236 | return false; |
||
237 | }; |
||
238 | $section->id = $sectionRecord->id; |
||
239 | |||
240 | return true; |
||
241 | } |
||
242 | |||
243 | return Craft::app()->sections->saveSection($section); |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * Populate section. |
||
248 | * |
||
249 | * @param SectionModel $section |
||
250 | * @param array $sectionDefinition |
||
251 | * @param string $sectionHandle |
||
252 | */ |
||
253 | private function populateSection(SectionModel $section, array $sectionDefinition, $sectionHandle) |
||
254 | { |
||
255 | $section->setAttributes([ |
||
256 | 'handle' => $sectionHandle, |
||
257 | 'name' => $sectionDefinition['name'], |
||
258 | 'type' => $sectionDefinition['type'], |
||
259 | 'hasUrls' => $sectionDefinition['hasUrls'], |
||
260 | 'template' => $sectionDefinition['template'], |
||
261 | 'maxLevels' => $sectionDefinition['maxLevels'], |
||
262 | 'enableVersioning' => $sectionDefinition['enableVersioning'], |
||
263 | ]); |
||
264 | |||
265 | $this->populateSectionLocales($section, $sectionDefinition['locales']); |
||
266 | } |
||
267 | |||
268 | /** |
||
269 | * Populate section locales. |
||
270 | * |
||
271 | * @param SectionModel $section |
||
272 | * @param $localeDefinitions |
||
273 | */ |
||
274 | private function populateSectionLocales(SectionModel $section, $localeDefinitions) |
||
275 | { |
||
276 | $locales = $section->getLocales(); |
||
277 | |||
278 | foreach ($localeDefinitions as $localeId => $localeDef) { |
||
279 | $locale = array_key_exists($localeId, $locales) ? $locales[$localeId] : new SectionLocaleModel(); |
||
280 | |||
281 | $locale->setAttributes([ |
||
282 | 'locale' => $localeId, |
||
283 | 'enabledByDefault' => $localeDef['enabledByDefault'], |
||
284 | 'urlFormat' => $localeDef['urlFormat'], |
||
285 | 'nestedUrlFormat' => $localeDef['nestedUrlFormat'], |
||
286 | ]); |
||
287 | |||
288 | // Todo: Is this a hack? I don't see another way. |
||
289 | // Todo: Might need a sorting order as well? It's NULL at the moment. |
||
290 | Craft::app()->db->createCommand()->insertOrUpdate('locales', [ |
||
291 | 'locale' => $locale->locale, |
||
292 | ], []); |
||
293 | |||
294 | $locales[$localeId] = $locale; |
||
295 | } |
||
296 | |||
297 | $section->setLocales($locales); |
||
298 | } |
||
299 | |||
300 | /** |
||
301 | * Populate entry type. |
||
302 | * |
||
303 | * @param EntryTypeModel $entryType |
||
304 | * @param array $entryTypeDefinition |
||
305 | * @param string $entryTypeHandle |
||
306 | * @param int $sectionId |
||
307 | */ |
||
308 | private function populateEntryType(EntryTypeModel $entryType, array $entryTypeDefinition, $entryTypeHandle, $sectionId) |
||
309 | { |
||
310 | $entryType->setAttributes([ |
||
311 | 'handle' => $entryTypeHandle, |
||
312 | 'sectionId' => $sectionId, |
||
313 | 'name' => $entryTypeDefinition['name'], |
||
314 | 'hasTitleField' => $entryTypeDefinition['hasTitleField'], |
||
315 | 'titleLabel' => $entryTypeDefinition['titleLabel'], |
||
316 | 'titleFormat' => $entryTypeDefinition['titleFormat'], |
||
317 | ]); |
||
318 | |||
319 | $fieldLayout = Craft::app()->schematic_fields->getFieldLayout($entryTypeDefinition['fieldLayout']); |
||
320 | $entryType->setFieldLayout($fieldLayout); |
||
321 | } |
||
322 | } |
||
323 |