Completed
Pull Request — master (#114)
by Bart
08:31
created

Base::getRecordDefinition()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 35
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 0
loc 35
ccs 0
cts 14
cp 0
rs 8.439
c 0
b 0
f 0
cc 6
eloc 19
nc 16
nop 1
crap 42
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
use LogicException;
13
14
/**
15
 * Schematic Base Service.
16
 *
17
 * Sync Craft Setups.
18
 *
19
 * @author    Nerds & Company
20
 * @copyright Copyright (c) 2015-2018, Nerds & Company
21
 * @license   MIT
22
 *
23
 * @see      http://www.nerds.company
24
 */
25
abstract class Base extends BaseComponent implements MappingInterface
26
{
27
    /**
28
     * Load fieldlayout and sources behaviors
29
     *
30
     * @return array
31
     */
32
    public function behaviors()
33
    {
34
        return [
35
          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...
36
          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...
37
        ];
38
    }
39
40
    /**
41
     * Get all records
42
     *
43
     * @return Model[]
44
     */
45
    abstract protected function getRecords();
46
47 57
    //==============================================================================================================
48
    //================================================  EXPORT  ====================================================
49 57
    //==============================================================================================================
50 57
51
    /**
52
     * Get all record definitions
53
     *
54
     * @return array
55
     */
56
    public function export(array $records = null)
57
    {
58
        $records = $records ?: $this->getRecords();
59
        $result = [];
60
        foreach ($records as $record) {
61
            $result[$record->handle] = $this->getRecordDefinition($record);
62
        }
63
        return $result;
64
    }
65
66
    /**
67
     * Get single record definition
68
     *
69
     * @param  Model $record
70
     * @return array
71
     */
72
    protected function getRecordDefinition(Model $record)
73
    {
74
        $definition = [
75
          'class' => get_class($record),
76
          'attributes' => $record->attributes,
77
        ];
78
        unset($definition['attributes']['id']);
79
        unset($definition['attributes']['dateCreated']);
80
        unset($definition['attributes']['dateUpdated']);
81
82
        // Define sources
83
        if (isset($definition['attributes']['sources'])) {
84
            $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...
85
        }
86
87
        if (isset($definition['attributes']['source'])) {
88
            $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...
89
        }
90
91
        // Define field layout
92
        if (isset($definition['attributes']['fieldLayoutId'])) {
93
            $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...
94
            unset($definition['attributes']['fieldLayoutId']);
95
        }
96
97
        // Define site settings
98
        if (isset($record->siteSettings)) {
99
            $definition['siteSettings'] = [];
100
            foreach ($record->getSiteSettings() as $siteSetting) {
101
                $definition['siteSettings'][$siteSetting->site->handle] = $this->getRecordDefinition($siteSetting);
102
            }
103
        }
104
105
        return $definition;
106
    }
107
108
    //==============================================================================================================
109
    //================================================  IMPORT  ====================================================
110
    //==============================================================================================================
111
112
    /**
113 4
     * Import asset volumes.
114
     *
115 4
     * @param array $definitions
116 4
     * @param Model $records The existing records
117
     * @param array $defaultAttributes Default attributes to use for each record
118
     */
119
    public function import(array $definitions, array $records = [], array $defaultAttributes = [])
120
    {
121
        $records = $records ?: $this->getRecords();
122
        $recordsByHandle = ArrayHelper::index($records, 'handle');
123
        foreach ($definitions as $handle => $definition) {
124 11
            $record = new $definition['class']();
125
            if (array_key_exists($handle, $recordsByHandle)) {
126 11
                $record = $recordsByHandle[$handle];
127 11
                if ($this->getRecordDefinition($record) === $definition) {
128
                    Schematic::info('- Skipping '.get_class($record).' '.$handle);
129
                    unset($recordsByHandle[$handle]);
130
                    continue;
131
                }
132
            }
133
134
            Schematic::info('- Saving '.get_class($record).' '.$handle);
135
            $this->setRecordAttributes($record, $definition, $defaultAttributes);
136
            if (!$this->saveRecord($record, $definition)) {
137
                $this->importError($record, $handle);
138
            }
139
            unset($recordsByHandle[$handle]);
140
        }
141
142
        if (Schematic::$force) {
143
            // Delete volumes not in definitions
144
            foreach ($recordsByHandle as $handle => $record) {
145
                Schematic::info('- Deleting '.get_class($record).' '.$handle);
146
                $this->deleteRecord($record);
147
            }
148
        }
149
    }
150
151
    /**
152
     * Log an import error
153
     *
154
     * @param  Model $record
155
     * @param  string $handle
156
     */
157
    protected function importError($record, $handle)
158
    {
159
        Schematic::warning('- Error importing '.get_class($record).' '.$handle);
160
        foreach ($record->getErrors() as $errors) {
161
            foreach ($errors as $error) {
162
                Schematic::error('   - '.$error);
163
            }
164
        }
165
    }
166
167
    /**
168
     * Set record attributes from definition
169
     *
170
     * @param Model $record
171
     * @param array $definition
172
     */
173
    private function setRecordAttributes(Model &$record, array $definition, array $defaultAttributes)
174
    {
175
        $attributes = array_merge($definition['attributes'], $defaultAttributes);
176
        $record->setAttributes($attributes);
177
178
        // Set field layout
179
        if (array_key_exists('fieldLayout', $definition)) {
180
            $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...
181
        }
182
183
        // Set site settings
184
        if (array_key_exists('siteSettings', $definition)) {
185
            $siteSettings = [];
186
            foreach ($definition['siteSettings'] as $handle => $siteSettingDefinition) {
187
                $siteSetting = new $siteSettingDefinition['class']($siteSettingDefinition['attributes']);
188
                $site = Craft::$app->sites->getSiteByHandle($handle);
189
                if ($site) {
190
                    $siteSetting->siteId = $site->id;
191
                    $siteSettings[] = $siteSetting;
192
                } else {
193
                    Schematic::warning('  - Site '.$handle.' could not be found');
194
                }
195
            }
196
            $record->setSiteSettings($siteSettings);
197
        }
198
    }
199
200
    /**
201
     * Save a record
202
     *
203
     * @param Model $record
204
     * @param array $definition
205
     * @return boolean
206
     */
207
    abstract protected function saveRecord(Model $record, array $definition);
208
209
    /**
210
     * Delete a record
211
     *
212
     * @param Model $record
213
     * @return boolean
214
     */
215
    abstract protected function deleteRecord(Model $record);
216
}
217