Completed
Pull Request — master (#1)
by vistart
10:04
created

SubsidiaryTrait::findOneOrCreate()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 1
Metric Value
dl 0
loc 15
ccs 10
cts 10
cp 1
rs 8.8571
c 1
b 0
f 1
cc 5
eloc 10
nc 6
nop 3
crap 5
1
<?php
2
3
/**
4
 *   _   __ __ _____ _____ ___  ____  _____
5
 *  | | / // // ___//_  _//   ||  __||_   _|
6
 *  | |/ // /(__  )  / / / /| || |     | |
7
 *  |___//_//____/  /_/ /_/ |_||_|     |_|
8
 * @link https://vistart.me/
9
 * @copyright Copyright (c) 2016 - 2017 vistart
10
 * @license https://vistart.me/license/
11
 */
12
13
namespace rhosocial\base\models\traits;
14
15
use yii\base\InvalidConfigException;
16
17
/**
18
 * @version 1.0
19
 * @author vistart <[email protected]>
20
 */
21
trait SubsidiaryTrait
22
{
23
    /**
24
     * @var string[] Subsidiary map.
25
     * Array key represents class alias,
26
     * array value represents the full qualified class name corresponds to the alias.
27
     *
28
     * For example:
29
```php
30
public $subsidiaryMap = [
31
    'Profile' => [
32
        'class' => 'app\models\user\Profile',
33
    ],
34
];
35
```
36
     *
37
     * The other elements will be taken if subsidiary configuration does not specify.
38
     * If you want to create subsidiary model and the class is not found, the array elements will be taken.
39
     */
40
    public $subsidiaryMap = [];
41
42
    /**
43
     * Add subsidiary class to map.
44
     * @param string $name Subsidiary name, case insensitive.
45
     * @param string|array $config If this parameter is string, it will regarded as class name.
46
     * If this parameter is array, you should specify `class`, and the class should be existed.
47
     * @return boolean True if the class added.
48
     * @throws InvalidConfigException throws if subsidiary name is not specified or class is not
49
     * specified.
50
     */
51 8
    public function addSubsidiaryClass($name, $config)
52
    {
53 8
        if (!is_string($name) || empty($name)) {
54 2
            throw new InvalidConfigException('Subsidiary name not specified.');
55
        }
56 8
        $name = strtolower($name);
57 8
        if (!is_array($config)) {
58 8
            if (is_string($config) && !empty($config)) {
59 8
                $this->subsidiaryMap[$name] = ['class' => $config];
60
            } else {
61
                throw new InvalidConfigException('Subsidiary class not specified.');
62
            }
63
        } else {
64 3
            if (isset($config['class']) && class_exists($config['class'])) {
65 3
                $this->subsidiaryMap[$name] = $config;
66
            } else {
67 1
                throw new InvalidConfigException('Subsidiary class not specified.');
68
            }
69
        }
70 8
        return true;
71
    }
72
73
    /**
74
     * Remove subsidiary.
75
     * @param string $name
76
     * @return boolean
77
     */
78 1
    public function removeSubsidiary($name)
79
    {
80 1
        $name = strtolower($name);
81 1
        if (array_key_exists($name, $this->subsidiaryMap)) {
82 1
            unset($this->subsidiaryMap[$name]);
83 1
            return true;
84
        }
85 1
        return false;
86
    }
87
88
    /**
89
     * Get subsidiary class.
90
     * @param string $name
91
     * @return string
92
     */
93 8
    public function getSubsidiaryClass($name)
94
    {
95 8
        $name = strtolower($name);
96 8
        if (array_key_exists($name, $this->subsidiaryMap) && array_key_exists('class', $this->subsidiaryMap[$name])) {
97 8
            return class_exists($this->subsidiaryMap[$name]['class']) ? $this->subsidiaryMap[$name]['class'] : null;
98
        }
99 1
        return null;
100
    }
101
102
    /**
103
     * Get subsidiaries.
104
     * @param $name
105
     * @param string $limit
106
     * @param int $page
107
     * @return null
108
     */
109 2
    public function getSubsidiaries($name, $limit = 'all', $page = 0)
110
    {
111 2
        $class = $this->getSubsidiaryClass($name);
112 2
        if (empty($class)) {
113 1
            return null;
114
        }
115 2
        $query = $class::find();
116 2
        if (!method_exists($query, 'createdBy')) {
117
            return null;
118
        }
119 2
        return $query->createdBy($this)->page($limit, $page)->all();
120
    }
121
122
    /**
123
     * @param $name
124
     * @param $arguments
125
     * @return mixed
126
     */
127 8
    public function __call($name, $arguments)
128
    {
129 8
        if (strpos(strtolower($name), "create") === 0) {
130 8
            $class = strtolower(substr($name, 6));
131 8
            $config = (isset($arguments) && isset($arguments[0])) ? $arguments[0] : [];
132 8
            return $this->createSubsidiary($class, $config);
133
        }
134
        return parent::__call($name, $arguments);
135
    }
136
137
    /**
138
     * Find existed, or create new model.
139
     * If model to be found doesn't exist, and $config is null, the parameter
140
     * `$condition` will be regarded as properties of new model.
141
     * If you want to know whether the returned model is new model, please check
142
     * the return value of `getIsNewRecord()` method.
143
     * @param string $className Full qualified class name.
144
     * @param array $condition Search condition, or properties if not found and
145
     * $config is null.
146
     * @param array $config new model's configuration array. If you specify this
147
     * parameter, the $condition will be skipped when created one.
148
     * @return [[$className]] the existed model, or new model created by specified
0 ignored issues
show
Documentation introduced by
The doc-type [[$className]] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
149
     * condition or configuration.
150
     */
151 1
    public function findOneOrCreate($className, $condition = [], $config = null)
152
    {
153 1
        $entity = new $className(['skipInit' => true]);
154 1
        if (!isset($condition[$entity->createdByAttribute])) {
155 1
            $condition[$entity->createdByAttribute] = $this->getGUID();
0 ignored issues
show
Documentation Bug introduced by
The method getGUID does not exist on object<rhosocial\base\mo...traits\SubsidiaryTrait>? 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...
156
        }
157 1
        $model = $className::findOne($condition);
158 1
        if (!$model) {
159 1
            if ($config === null || !is_array($config)) {
160 1
                $config = $condition;
161
            }
162 1
            $model = $this->create($className, $config);
163
        }
164 1
        return $model;
165
    }
166
167
    /**
168
     * Create new entity model associated with current user. The model to be created
169
     * must be extended from [[BaseBlameableModel]], [[BaseMongoBlameableModel]],
170
     * [[BaseRedisBlameableModel]], or any other classes used [[BlameableTrait]].
171
     * if $config does not specify `hostClass` property, self will be assigned to.
172
     * @param string $className Full qualified class name.
173
     * @param array $config name-value pairs that will be used to initialize
174
     * the object properties.
175
     * @param boolean $loadDefault Determines whether loading default values
176
     * after entity model created.
177
     * Notice! The [[\yii\mongodb\ActiveRecord]] and [[\yii\redis\ActiveRecord]]
178
     * does not support loading default value. If you want to assign properties
179
     * with default values, please define the `default` rule(s) for properties in
180
     * `rules()` method and return them by yourself if you don't specified them in config param.
181
     * @param boolean $skipIfSet whether existing value should be preserved.
182
     * This will only set defaults for attributes that are `null`.
183
     * @return [[$className]] new model created with specified configuration.
0 ignored issues
show
Documentation introduced by
The doc-type [[$className]] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
184
     */
185 169
    public function create($className, $config = [], $loadDefault = true, $skipIfSet = true)
186
    {
187 169
        if (!isset($config['hostClass'])) {
188 169
            $config['hostClass'] = static::class;
189
        }
190 169
        if (isset($config['class'])) {
191 18
            unset($config['class']);
192
        }
193 169
        $entity = new $className($config);
194 169
        $entity->setHost($this);
195 169
        if ($loadDefault && method_exists($entity, 'loadDefaultValues')) {
196 86
            $entity->loadDefaultValues($skipIfSet);
197
        }
198 169
        return $entity;
199
    }
200
201
    /**
202
     *
203
     * @param string $name
204
     * @param array $config
205
     * @return mixed
206
     */
207 8
    public function createSubsidiary($name, $config)
208
    {
209 8
        if (!is_string($name) || empty($name)) {
210
            return null;
211
        }
212 8
        $className = '';
0 ignored issues
show
Unused Code introduced by
$className is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
213 8
        if (class_exists($name)) {
214 1
            $className = $name;
215 8
        } elseif (array_key_exists($name, $this->subsidiaryMap)) {
216 8
            $className = $this->getSubsidiaryClass($name);
217
        } else {
218 1
            return null;
219
        }
220 8
        return $this->create($className, $config);
221
    }
222
}
223