Completed
Pull Request — master (#114)
by Bart
13:02 queued 03:06
created

Base::getRecordDefinition()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 25
ccs 0
cts 9
cp 0
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 15
nc 8
nop 1
crap 20
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
        if (isset($definition['attributes']['sources'])) {
83
            $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...
84
        }
85
86
        if (isset($definition['attributes']['source'])) {
87
            $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...
88
        }
89
90
        if (isset($definition['attributes']['fieldLayoutId'])) {
91
            $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...
92
            unset($definition['attributes']['fieldLayoutId']);
93
        }
94
95
        return $definition;
96
    }
97
98
    //==============================================================================================================
99
    //================================================  IMPORT  ====================================================
100
    //==============================================================================================================
101
102
    /**
103
     * Import asset volumes.
104
     *
105
     * @param array $definitions
106
     * @param Model $records The existing records
107
     * @param array $defaultAttributes Default attributes to use for each record
108
     */
109
    public function import(array $definitions, array $records = [], array $defaultAttributes = [])
110
    {
111
        $records = $records ?: $this->getRecords();
112
        $recordsByHandle = ArrayHelper::index($records, 'handle');
113 4
        foreach ($definitions as $handle => $definition) {
114
            $record = new $definition['class']();
115 4
            if (array_key_exists($handle, $recordsByHandle)) {
116 4
                $record = $recordsByHandle[$handle];
117
                if ($this->getRecordDefinition($record) === $definition) {
118
                    Schematic::info('- Skipping '.get_class($record).' '.$handle);
119
                    unset($recordsByHandle[$handle]);
120
                    continue;
121
                }
122
            }
123
124 11
            Schematic::info('- Saving '.get_class($record).' '.$handle);
125
            $this->setRecordAttributes($record, $definition, $defaultAttributes);
126 11
            if (!$this->saveRecord($record, $definition)) {
127 11
                $this->importError($record, $handle);
128
            }
129
            unset($recordsByHandle[$handle]);
130
        }
131
132
        if (Schematic::$force) {
133
            // Delete volumes not in definitions
134
            foreach ($recordsByHandle as $handle => $record) {
135
                Schematic::info('- Deleting '.get_class($record).' '.$handle);
136
                $this->deleteRecord($record);
137
            }
138
        }
139
    }
140
141
    /**
142
     * Log an import error
143
     *
144
     * @param  Model $record
145
     * @param  string $handle
146
     */
147
    protected function importError($record, $handle)
148
    {
149
        Schematic::warning('- Error importing record '.$handle);
150
        foreach ($record->getErrors() as $errors) {
151
            foreach ($errors as $error) {
152
                Schematic::error('   - '.$error);
153
            }
154
        }
155
    }
156
157
    /**
158
     * Set record attributes from definition
159
     *
160
     * @param Model $record
161
     * @param array $definition
162
     */
163
    private function setRecordAttributes(Model &$record, array $definition, array $defaultAttributes)
164
    {
165
        $attributes = array_merge($definition['attributes'], $defaultAttributes);
166
        $record->setAttributes($attributes);
167
168
        if (array_key_exists('fieldLayout', $definition)) {
169
            $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...
170
        }
171
    }
172
173
    /**
174
     * Save a record
175
     *
176
     * @param Model $record
177
     * @param array $definition
178
     * @return boolean
179
     */
180
    abstract protected function saveRecord(Model $record, array $definition);
181
182
    /**
183
     * Delete a record
184
     *
185
     * @param Model $record
186
     * @return boolean
187
     */
188
    abstract protected function deleteRecord(Model $record);
189
}
190