Completed
Pull Request — master (#114)
by Bart
09:44
created

Base   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 188
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 28.13%

Importance

Changes 0
Metric Value
wmc 26
lcom 1
cbo 6
dl 0
loc 188
ccs 9
cts 32
cp 0.2813
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A behaviors() 0 7 1
getRecords() 0 1 ?
A export() 0 10 3
B getRecordDefinition() 0 35 6
C import() 0 31 8
A importError() 0 9 3
B setRecordAttributes() 0 26 5
saveRecord() 0 1 ?
deleteRecord() 0 1 ?
1
<?php
2
3
namespace NerdsAndCompany\Schematic\Services;
4
5
use craft\base\Model;
6
use craft\helpers\ArrayHelper;
7
use yii\base\Component as BaseComponent;
8
use NerdsAndCompany\Schematic\Behaviors\FieldLayoutBehavior;
9
use NerdsAndCompany\Schematic\Behaviors\SourcesBehavior;
10
use NerdsAndCompany\Schematic\Interfaces\MappingInterface;
11
use NerdsAndCompany\Schematic\Schematic;
12
13
/**
14
 * Schematic Base Service.
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
abstract class Base extends BaseComponent implements MappingInterface
25
{
26
    /**
27
     * Load fieldlayout and sources behaviors.
28
     *
29
     * @return array
30
     */
31
    public function behaviors()
32
    {
33
        return [
34
          FieldLayoutBehavior::className(),
0 ignored issues
show
Deprecated Code introduced by
The method yii\base\BaseObject::className() has been deprecated with message: since 2.0.14. On PHP >=5.5, use `::class` instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
35
          SourcesBehavior::className(),
0 ignored issues
show
Deprecated Code introduced by
The method yii\base\BaseObject::className() has been deprecated with message: since 2.0.14. On PHP >=5.5, use `::class` instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
36
        ];
37
    }
38
39
    /**
40
     * Get all records.
41
     *
42
     * @return Model[]
43
     */
44
    abstract protected function getRecords();
45
46
    /**
47 57
     * Get all record definitions.
48
     *
49 57
     * @return array
50 57
     */
51
    public function export(array $records = null)
52
    {
53
        $records = $records ?: $this->getRecords();
54
        $result = [];
55
        foreach ($records as $record) {
56
            $result[$record->handle] = $this->getRecordDefinition($record);
57
        }
58
59
        return $result;
60
    }
61
62
    /**
63
     * Get single record definition.
64
     *
65
     * @param Model $record
66
     *
67
     * @return array
68
     */
69
    protected function getRecordDefinition(Model $record)
70
    {
71
        $definition = [
72
          'class' => get_class($record),
73
          'attributes' => $record->attributes,
74
        ];
75
        unset($definition['attributes']['id']);
76
        unset($definition['attributes']['dateCreated']);
77
        unset($definition['attributes']['dateUpdated']);
78
79
        // Define sources
80
        if (isset($definition['attributes']['sources'])) {
81
            $definition['sources'] = $this->getSources($definition['class'], $definition['attributes']['sources'], 'id', 'handle');
0 ignored issues
show
Documentation Bug introduced by
The method getSources does not exist on object<NerdsAndCompany\Schematic\Services\Base>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
82
        }
83
84
        if (isset($definition['attributes']['source'])) {
85
            $definition['source'] = $this->getSource($definition['class'], $definition['attributes']['sources'], 'id', 'handle');
0 ignored issues
show
Documentation Bug introduced by
The method getSource does not exist on object<NerdsAndCompany\Schematic\Services\Base>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
86
        }
87
88
        // Define field layout
89
        if (isset($definition['attributes']['fieldLayoutId'])) {
90
            $definition['fieldLayout'] = $this->getFieldLayoutDefinition($record->getFieldLayout());
0 ignored issues
show
Documentation Bug introduced by
The method getFieldLayoutDefinition does not exist on object<NerdsAndCompany\Schematic\Services\Base>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
91
            unset($definition['attributes']['fieldLayoutId']);
92
        }
93
94
        // Define site settings
95
        if (isset($record->siteSettings)) {
96
            $definition['siteSettings'] = [];
97
            foreach ($record->getSiteSettings() as $siteSetting) {
98
                $definition['siteSettings'][$siteSetting->site->handle] = $this->getRecordDefinition($siteSetting);
99
            }
100
        }
101
102
        return $definition;
103
    }
104
105
    /**
106
     * Import records.
107
     *
108
     * @param array $definitions
109
     * @param Model $records           The existing records
110
     * @param array $defaultAttributes Default attributes to use for each record
111
     */
112
    public function import(array $definitions, array $records = null, array $defaultAttributes = [])
113 4
    {
114
        $records = $records ?: $this->getRecords();
115 4
        $recordsByHandle = ArrayHelper::index($records, 'handle');
116 4
        foreach ($definitions as $handle => $definition) {
117
            $record = new $definition['class']();
118
            if (array_key_exists($handle, $recordsByHandle)) {
119
                $record = $recordsByHandle[$handle];
120
                if ($this->getRecordDefinition($record) === $definition) {
121
                    Schematic::info('- Skipping '.get_class($record).' '.$handle);
122
                    unset($recordsByHandle[$handle]);
123
                    continue;
124 11
                }
125
            }
126 11
127 11
            Schematic::info('- Saving '.get_class($record).' '.$handle);
128
            $this->setRecordAttributes($record, $definition, $defaultAttributes);
129
            if (!$this->saveRecord($record, $definition)) {
130
                $this->importError($record, $handle);
131
            }
132
            unset($recordsByHandle[$handle]);
133
        }
134
135
        if (Schematic::$force) {
136
            // Delete records not in definitions
137
            foreach ($recordsByHandle as $handle => $record) {
138
                Schematic::info('- Deleting '.get_class($record).' '.$handle);
139
                $this->deleteRecord($record);
140
            }
141
        }
142
    }
143
144
    /**
145
     * Log an import error.
146
     *
147
     * @param Model  $record
148
     * @param string $handle
149
     */
150
    protected function importError($record, $handle)
151
    {
152
        Schematic::warning('- Error importing '.get_class($record).' '.$handle);
153
        foreach ($record->getErrors() as $errors) {
154
            foreach ($errors as $error) {
155
                Schematic::error('   - '.$error);
156
            }
157
        }
158
    }
159
160
    /**
161
     * Set record attributes from definition.
162
     *
163
     * @param Model $record
164
     * @param array $definition
165
     */
166
    private function setRecordAttributes(Model &$record, array $definition, array $defaultAttributes)
167
    {
168
        $attributes = array_merge($definition['attributes'], $defaultAttributes);
169
        $record->setAttributes($attributes);
170
171
        // Set field layout
172
        if (array_key_exists('fieldLayout', $definition)) {
173
            $record->setFieldLayout($this->getFieldLayout($definition['fieldLayout']));
0 ignored issues
show
Documentation Bug introduced by
The method getFieldLayout does not exist on object<NerdsAndCompany\Schematic\Services\Base>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
174
        }
175
176
        // Set site settings
177
        if (array_key_exists('siteSettings', $definition)) {
178
            $siteSettings = [];
179
            foreach ($definition['siteSettings'] as $handle => $siteSettingDefinition) {
180
                $siteSetting = new $siteSettingDefinition['class']($siteSettingDefinition['attributes']);
181
                $site = Craft::$app->sites->getSiteByHandle($handle);
182
                if ($site) {
183
                    $siteSetting->siteId = $site->id;
184
                    $siteSettings[] = $siteSetting;
185
                } else {
186
                    Schematic::warning('  - Site '.$handle.' could not be found');
187
                }
188
            }
189
            $record->setSiteSettings($siteSettings);
190
        }
191
    }
192
193
    /**
194
     * Save a record.
195
     *
196
     * @param Model $record
197
     * @param array $definition
198
     *
199
     * @return bool
200
     */
201
    abstract protected function saveRecord(Model $record, array $definition);
202
203
    /**
204
     * Delete a record.
205
     *
206
     * @param Model $record
207
     *
208
     * @return bool
209
     */
210
    abstract protected function deleteRecord(Model $record);
211
}
212