Completed
Push — master ( 4ea1d7...a5f18a )
by Bart
18s
created

SourcesBehavior::setMockFolder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace NerdsAndCompany\Schematic\Behaviors;
4
5
use Craft;
6
use TypeError;
7
use yii\base\Behavior;
8
use craft\base\Model;
9
use craft\records\VolumeFolder;
10
use NerdsAndCompany\Schematic\Schematic;
11
use NerdsAndCompany\Schematic\Events\SourceMappingEvent;
12
13
/**
14
 * Schematic Sources Behavior.
15
 *
16
 * Sync Craft Setups.
17
 *
18
 * @author    Nerds & Company
19
 * @copyright Copyright (c) 2015-2018, Nerds & Company
20
 * @license   MIT
21
 *
22
 * @see      http://www.nerds.company
23
 */
24
class SourcesBehavior extends Behavior
25
{
26
    /** Hack to be able to avoid the active record call in VolumeFolder::findOne() */
27
    public $mockFolder = null;
28
29
    /**
30
     * Recursively find sources in definition attributes.
31
     *
32
     * @param string $fieldType
33
     * @param array  $attributes
34
     * @param string $indexFrom
35
     * @param string $indexTo
36
     *
37
     * @return array
38
     */
39 32
    public function findSources(string $fieldType, array $attributes, string $indexFrom, string $indexTo): array
40
    {
41 32
        foreach ($attributes as $key => $attribute) {
42 32
            if ($key === 'source') {
43 4
                $attributes[$key] = $this->getSource($fieldType, $attribute, $indexFrom, $indexTo);
44 32
            } elseif ($key === 'sources') {
45 4
                $attributes[$key] = $this->getSources($fieldType, $attribute, $indexFrom, $indexTo);
46 32
            } elseif (is_array($attribute)) {
47 32
                $attributes[$key] = $this->findSources($fieldType, $attribute, $indexFrom, $indexTo);
48
            }
49
        }
50
51 32
        return $attributes;
52
    }
53
54
    /**
55
     * Get sources based on the indexFrom attribute and return them with the indexTo attribute.
56
     *
57
     * @param string       $fieldType
58
     * @param string|array $sources
59
     * @param string       $indexFrom
60
     * @param string       $indexTo
61
     *
62
     * @return array|string
63
     */
64 9
    public function getSources(string $fieldType, $sources, string $indexFrom, string $indexTo)
65
    {
66 9
        $mappedSources = $sources;
67 9
        if (is_array($sources)) {
68 7
            $mappedSources = [];
69 7
            $sources = array_filter($sources);
70 7
            foreach ($sources as $source) {
71 7
                $mappedSources[] = $this->getSource($fieldType, $source, $indexFrom, $indexTo);
72
            }
73
        }
74
75 9
        return $mappedSources;
76
    }
77
78
    /**
79
     * Gets a source by the attribute indexFrom, and returns it with attribute $indexTo.
80
     *
81
     * @TODO Break up and simplify this method
82
     *
83
     * @param string $fieldType
84
     * @param string $source
85
     * @param string $indexFrom
86
     * @param string $indexTo
87
     *
88
     * @return string|null
89
     *
90
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
91
     * @SuppressWarnings(PHPMD.NPathComplexity)
92
     */
93 9
    public function getSource(string $fieldType, string $source = null, string $indexFrom, string $indexTo)
94
    {
95 9
        if (false === strpos($source, ':')) {
96 7
            return $source;
97
        }
98
99
        /** @var Model $sourceObject */
100 9
        $sourceObject = null;
101
102
        // Get service and method by source
103 9
        list($sourceType, $sourceFrom) = explode(':', $source);
104 9
        switch ($sourceType) {
105 9
            case 'editSite':
106
                $service = Craft::$app->sites;
107
                $method = 'getSiteBy';
108
                break;
109 9
            case 'single':
110 9
            case 'section':
111 9
            case 'createEntries':
112 9
            case 'editPeerEntries':
113 9
            case 'deleteEntries':
114 9
            case 'deletePeerEntries':
115 9
            case 'deletePeerEntryDrafts':
116 9
            case 'editEntries':
117 9
            case 'editPeerEntryDrafts':
118 9
            case 'publishEntries':
119 9
            case 'publishPeerEntries':
120 9
            case 'publishPeerEntryDrafts':
121 5
                $service = Craft::$app->sections;
122 5
                $method = 'getSectionBy';
123 5
                break;
124 9
            case 'group':
125 7
            case 'editCategories':
126 5
                $service = 'Users' == $fieldType ? Craft::$app->userGroups : Craft::$app->categories;
127 5
                $method = 'getGroupBy';
128 5
                break;
129 7
            case 'folder':
130 2
                $service = $this;
131 2
                $method = 'getFolderBy';
132 2
                break;
133 5
            case 'createFoldersInVolume':
134 5
            case 'deleteFilesAndFoldersInVolume':
135 5
            case 'saveAssetInVolume':
136 5
            case 'viewVolume':
137 3
                $service = Craft::$app->volumes;
138 3
                $method = 'getVolumeBy';
139 3
                break;
140 2
            case 'taggroup':
141
                $service = Craft::$app->tags;
142
                $method = 'getTagGroupBy';
143
                break;
144 2
            case 'field':
145 2
                $service = Craft::$app->fields;
146 2
                $method = 'getFieldBy';
147 2
                break;
148
            case 'editGlobalSet':
149
                $service = Craft::$app->globals;
150
                $method = 'getSetBy';
151
                break;
152
            case 'utility':
153
                return $source;
154
        }
155
156
        // Send event
157 9
        $plugin = Craft::$app->controller->module;
158 9
        $event = new SourceMappingEvent([
159 9
            'source' => $source,
160 9
            'service' => $service ?? null,
161 9
            'method' => $method ?? null,
162
        ]);
163 9
        $plugin->trigger($plugin::EVENT_MAP_SOURCE, $event);
164 9
        $service = $event->service;
165 9
        $method = $event->method;
166
167
        // Try service and method
168 9
        if (isset($service) && isset($method) && isset($sourceFrom)) {
169 9
            $method = $method.ucfirst($indexFrom);
170
            try {
171 9
                $sourceObject = $service->$method($sourceFrom);
0 ignored issues
show
Bug introduced by
The method $method cannot be called on $service (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
172
            } catch (TypeError $e) {
173
                Schematic::error('An error occured mapping source '.$source.' from '.$indexFrom.' to '.$indexTo);
174
                Schematic::error($e->getMessage());
175
176
                return null;
177
            }
178
        }
179
180 9
        if ($sourceObject) {
181 8
            return $sourceType.':'.$sourceObject->$indexTo;
182
        }
183
184 1
        Schematic::warning('No mapping found for source '.$source);
185
186 1
        return null;
187
    }
188
189
    /**
190
     * Get a folder by id
191
     * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
192
     *
193
     * @param int $folderId
194
     * @return object
195
     */
196 1
    private function getFolderById(int $folderId): \stdClass
197
    {
198 1
        $folder = Craft::$app->assets->getFolderById($folderId);
199 1
        if ($folder) {
200 1
            $volume = $folder->getVolume();
201
            return  (object) [
202 1
                'id' => $folderId,
203 1
                'handle' => $volume->handle
204
            ];
205
        }
206
        return null;
207
    }
208
209
    /**
210
     * Get folder by volume id
211
     *
212
     * @param int $volumeId
213
     * @return VolumeFolder
214
     */
215 1
    private function getFolderByVolumeId(int $volumeId): VolumeFolder
216
    {
217 1
        return $this->mockFolder ? $this->mockFolder : VolumeFolder::findOne(['volumeId' => $volumeId]);
218
    }
219
220
    /**
221
     * Set a mock folder for the tests
222
     *
223
     * @param VolumeFolder $mockFolder
224
     */
225 1
    public function setMockFolder(VolumeFolder $mockFolder)
226
    {
227 1
        $this->mockFolder = $mockFolder;
228 1
    }
229
230
    /**
231
     * Get a folder by volume handle
232
     * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
233
     *
234
     * @param string $folderHandle
235
     * @return object
236
     */
237 1
    private function getFolderByHandle(string $folderHandle): \stdClass
238
    {
239 1
        $volume = Craft::$app->volumes->getVolumeByHandle($folderHandle);
240 1
        if ($volume) {
241 1
            $folder = $this->getFolderByVolumeId($volume->id);
242 1
            if ($folder) {
243
                return  (object) [
244 1
                    'id' => $folder->id,
245 1
                    'handle' => $folderHandle
246
                ];
247
            }
248
        }
249
        return null;
250
    }
251
}
252