Passed
Push — 5.2 ( 2654b8...acaf63 )
by liu
02:34
created

Conversion::toArray()   B

Complexity

Conditions 7
Paths 24

Size

Total Lines 32
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 16
nc 24
nop 0
dl 0
loc 32
rs 8.8333
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
6
// +----------------------------------------------------------------------
7
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
8
// +----------------------------------------------------------------------
9
// | Author: liu21st <[email protected]>
10
// +----------------------------------------------------------------------
11
declare (strict_types = 1);
12
13
namespace think\model\concern;
14
15
use think\App;
16
use think\Collection;
17
use think\Exception;
18
use think\Model;
19
use think\model\Collection as ModelCollection;
20
use think\model\relation\OneToOne;
21
22
/**
23
 * 模型数据转换处理
24
 */
25
trait Conversion
26
{
27
    /**
28
     * 数据输出显示的属性
29
     * @var array
30
     */
31
    protected $visible = [];
32
33
    /**
34
     * 数据输出隐藏的属性
35
     * @var array
36
     */
37
    protected $hidden = [];
38
39
    /**
40
     * 数据输出需要追加的属性
41
     * @var array
42
     */
43
    protected $append = [];
44
45
    /**
46
     * 数据集对象名
47
     * @var string
48
     */
49
    protected $resultSetType;
50
51
    /**
52
     * 设置需要附加的输出属性
53
     * @access public
54
     * @param  array $append   属性列表
55
     * @return $this
56
     */
57
    public function append(array $append = [])
58
    {
59
        $this->append = $append;
60
61
        return $this;
62
    }
63
64
    /**
65
     * 设置附加关联对象的属性
66
     * @access public
67
     * @param  string       $attr    关联属性
68
     * @param  string|array $append  追加属性名
69
     * @return $this
70
     * @throws Exception
71
     */
72
    public function appendRelationAttr(string $attr, array $append)
73
    {
74
        $relation = App::parseName($attr, 1, false);
75
76
        if (isset($this->relation[$relation])) {
77
            $model = $this->relation[$relation];
78
        } else {
79
            $model = $this->getRelationData($this->$relation());
0 ignored issues
show
Bug introduced by
It seems like getRelationData() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

79
            /** @scrutinizer ignore-call */ 
80
            $model = $this->getRelationData($this->$relation());
Loading history...
80
        }
81
82
        if ($model instanceof Model) {
83
            foreach ($append as $key => $attr) {
84
                $key = is_numeric($key) ? $attr : $key;
85
                if (isset($this->data[$key])) {
86
                    throw new Exception('bind attr has exists:' . $key);
87
                } else {
88
                    $this->data[$key] = $model->$attr;
0 ignored issues
show
Bug Best Practice introduced by
The property data does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
89
                }
90
            }
91
        }
92
93
        return $this;
94
    }
95
96
    /**
97
     * 设置需要隐藏的输出属性
98
     * @access public
99
     * @param  array $hidden   属性列表
100
     * @return $this
101
     */
102
    public function hidden(array $hidden = [])
103
    {
104
        $this->hidden = $hidden;
105
106
        return $this;
107
    }
108
109
    /**
110
     * 设置需要输出的属性
111
     * @access public
112
     * @param  array $visible
113
     * @return $this
114
     */
115
    public function visible(array $visible = [])
116
    {
117
        $this->visible = $visible;
118
119
        return $this;
120
    }
121
122
    /**
123
     * 转换当前模型对象为数组
124
     * @access public
125
     * @return array
126
     */
127
    public function toArray(): array
128
    {
129
        $item    = [];
130
        $visible = [];
131
        $hidden  = [];
132
133
        // 合并关联数据
134
        $data = array_merge($this->data, $this->relation);
135
136
        // 过滤属性
137
        if (!empty($this->visible)) {
138
            $array = $this->parseAttr($this->visible, $visible);
139
            if (!empty($array)) {
140
                $data = array_intersect_key($data, array_flip($array));
141
            }
142
        }
143
144
        if (empty($array) && !empty($this->hidden)) {
145
            $array = $this->parseAttr($this->hidden, $hidden);
146
            $data  = array_diff_key($data, array_flip($array));
147
        }
148
149
        foreach ($data as $key => $val) {
150
            $item[$key] = $this->getArrayData($key, $val, $visible, $hidden);
151
        }
152
153
        // 追加属性(必须定义获取器)
154
        foreach ($this->append as $key => $name) {
155
            $this->appendAttrToArray($item, $key, $name);
156
        }
157
158
        return $item;
159
    }
160
161
    protected function appendAttrToArray(array &$item, $key, string $name)
162
    {
163
        if (is_array($name)) {
0 ignored issues
show
introduced by
The condition is_array($name) is always false.
Loading history...
164
            // 追加关联对象属性
165
            $relation = $this->getRelation($key);
166
167
            if (!$relation) {
168
                $relation = $this->getAttr($key);
169
                $relation->visible($name);
170
            }
171
172
            $item[$key] = $relation->append($name)->toArray();
173
        } elseif (strpos($name, '.')) {
174
            list($key, $attr) = explode('.', $name);
175
            // 追加关联对象属性
176
            $relation = $this->getRelation($key);
0 ignored issues
show
Bug introduced by
It seems like getRelation() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

176
            /** @scrutinizer ignore-call */ 
177
            $relation = $this->getRelation($key);
Loading history...
177
178
            if (!$relation) {
179
                $relation = $this->getAttr($key);
0 ignored issues
show
Bug introduced by
The method getAttr() does not exist on think\model\concern\Conversion. Did you maybe mean getBindAttr()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

179
                /** @scrutinizer ignore-call */ 
180
                $relation = $this->getAttr($key);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
180
                $relation->visible([$attr]);
181
            }
182
183
            $item[$key] = $relation->append([$attr])->toArray();
184
        } else {
185
            $value       = $this->getAttr($name);
186
            $item[$name] = $value;
187
188
            $this->getBindAttr($name, $value, $item);
189
        }
190
    }
191
192
    protected function getBindAttr(string $name, $value, array &$item = [])
193
    {
194
        $relation = $this->isRelationAttr($name);
0 ignored issues
show
Bug introduced by
It seems like isRelationAttr() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

194
        /** @scrutinizer ignore-call */ 
195
        $relation = $this->isRelationAttr($name);
Loading history...
195
        if (!$relation) {
196
            return false;
197
        }
198
199
        $modelRelation = $this->$relation();
200
201
        if ($modelRelation instanceof OneToOne) {
202
            $bindAttr = $modelRelation->getBindAttr();
203
204
            if ($bindAttr) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $bindAttr of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
205
                unset($item[$name]);
206
            }
207
208
            foreach ($bindAttr as $key => $attr) {
209
                $key = is_numeric($key) ? $attr : $key;
210
211
                if (isset($item[$key])) {
212
                    throw new Exception('bind attr has exists:' . $key);
213
                }
214
215
                $item[$key] = $value ? $value->getAttr($attr) : null;
216
            }
217
        }
218
    }
219
220
    protected function getArrayData(string $key, $val, array $visible, array $hidden)
221
    {
222
        if ($val instanceof Model || $val instanceof ModelCollection) {
223
            // 关联模型对象
224
            if (isset($visible[$key])) {
225
                $val->visible($visible[$key]);
226
            } elseif (isset($hidden[$key])) {
227
                $val->hidden($hidden[$key]);
228
            }
229
            // 关联模型对象
230
            return $val->toArray();
231
        }
232
233
        // 模型属性
234
        return $this->getAttr($key);
235
    }
236
237
    /**
238
     * 转换当前模型对象为JSON字符串
239
     * @access public
240
     * @param  integer $options json参数
241
     * @return string
242
     */
243
    public function toJson(int $options = JSON_UNESCAPED_UNICODE): string
244
    {
245
        return json_encode($this->toArray(), $options);
246
    }
247
248
    /**
249
     * 移除当前模型的关联属性
250
     * @access public
251
     * @return $this
252
     */
253
    public function removeRelation()
254
    {
255
        $this->relation = [];
0 ignored issues
show
Bug Best Practice introduced by
The property relation does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
256
        return $this;
257
    }
258
259
    public function __toString()
260
    {
261
        return $this->toJson();
262
    }
263
264
    // JsonSerializable
265
    public function jsonSerialize()
266
    {
267
        return $this->toArray();
268
    }
269
270
    /**
271
     * 转换数据集为数据集对象
272
     * @access public
273
     * @param  array|Collection $collection 数据集
274
     * @param  string           $resultSetType 数据集类
275
     * @return Collection
276
     */
277
    public function toCollection(iterable $collection, string $resultSetType = null): Collection
278
    {
279
        $resultSetType = $resultSetType ?: $this->resultSetType;
280
281
        if ($resultSetType && false !== strpos($resultSetType, '\\')) {
282
            $collection = new $resultSetType($collection);
283
        } else {
284
            $collection = new ModelCollection($collection);
285
        }
286
287
        return $collection;
288
    }
289
290
    /**
291
     * 解析隐藏及显示属性
292
     * @access protected
293
     * @param  array $attrs  属性
294
     * @param  array $result 结果集
295
     * @return array
296
     */
297
    protected function parseAttr(array $attrs, array &$result): array
298
    {
299
        $array = [];
300
301
        foreach ($attrs as $key => $val) {
302
            if (is_array($val)) {
303
                $result[$key] = $val;
304
            } elseif (strpos($val, '.')) {
305
                list($key, $name) = explode('.', $val);
306
307
                $result[$key][] = $name;
308
            } else {
309
                $array[] = $val;
310
            }
311
        }
312
313
        return $array;
314
    }
315
}
316