1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Thinktomorrow\Chief\DynamicAttributes; |
6
|
|
|
|
7
|
|
|
trait HasDynamicAttributes |
8
|
|
|
{ |
9
|
|
|
public function dynamic(string $key, string $index = null) |
10
|
|
|
{ |
11
|
|
|
return $this->dynamicAttributesInstance()->get($index ? "$key.$index" : $key); |
12
|
|
|
} |
13
|
|
|
|
14
|
|
|
public function setDynamic(string $key, $value, string $index = null) |
15
|
|
|
{ |
16
|
|
|
return $this->dynamicAttributesInstance()->set($index ? "$key.$index" : $key, $value); |
|
|
|
|
17
|
|
|
} |
18
|
|
|
|
19
|
|
|
public function isDynamicKey($key): bool |
20
|
|
|
{ |
21
|
|
|
if (array_key_exists($key, $this->attributes) || $key === $this->getDynamicKey()) { |
22
|
|
|
return false; |
23
|
|
|
} |
24
|
|
|
|
25
|
|
|
if (in_array($key, $this->dynamicKeys())) { |
26
|
|
|
return true; |
27
|
|
|
} |
28
|
|
|
|
29
|
|
|
if (in_array('*', $this->dynamicKeys())) { |
30
|
|
|
return !in_array($key, $this->dynamicKeysBlacklist()); |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
return false; |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* The attribute key which the dynamic attributes is |
38
|
|
|
* referenced by as well as the database column name. |
39
|
|
|
* |
40
|
|
|
* @return string |
41
|
|
|
*/ |
42
|
|
|
protected function getDynamicKey(): string |
43
|
|
|
{ |
44
|
|
|
return 'values'; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* The attributes that should be treated as dynamic ones. This |
49
|
|
|
* is a list of keys matching the database column names. |
50
|
|
|
* @return array |
51
|
|
|
*/ |
52
|
|
|
protected function dynamicKeys(): array |
53
|
|
|
{ |
54
|
|
|
return property_exists($this, 'dynamicKeys') ? $this->dynamicKeys : []; |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
protected function dynamicLocales(): array |
58
|
|
|
{ |
59
|
|
|
return property_exists($this, 'dynamicLocales') ? $this->dynamicLocales : []; |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* When allowing by default for all attributes to be dynamic, you can use |
64
|
|
|
* the blacklist to mark certain attributes as non dynamic. |
65
|
|
|
* |
66
|
|
|
* @return array |
67
|
|
|
*/ |
68
|
|
|
protected function dynamicKeysBlacklist(): array |
69
|
|
|
{ |
70
|
|
|
return property_exists($this, 'dynamicKeysBlacklist') ? $this->dynamicKeysBlacklist : []; |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Part of the custom cast. Method used by the save logic on eloquent. We override this so we can |
75
|
|
|
* inject an json version of the dynamic attributes for saving into the database. |
76
|
|
|
* |
77
|
|
|
* @return mixed |
78
|
|
|
*/ |
79
|
|
|
public function getAttributes() |
80
|
|
|
{ |
81
|
|
|
return $this->injectDynamicAttributes($this->attributes, false); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/* Part of the custom cast */ |
85
|
|
|
public function setRawAttributes(array $attributes, $sync = false) |
86
|
|
|
{ |
87
|
|
|
return parent::setRawAttributes($this->injectDynamicAttributes($attributes), $sync); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/* Part of the custom cast */ |
91
|
|
|
public function fill(array $attributes) |
92
|
|
|
{ |
93
|
|
|
return parent::fill($this->injectDynamicAttributes($attributes)); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/* |
97
|
|
|
* If the dynamic attributes contain a localized value, this has preference over any non-localized |
98
|
|
|
*/ |
99
|
|
|
public function getAttribute($key) |
100
|
|
|
{ |
101
|
|
|
if (!$this->isDynamicKey($key)) { |
102
|
|
|
return parent::getAttribute($key); |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
$locale = app()->getLocale(); |
|
|
|
|
106
|
|
|
|
107
|
|
|
if ($this->dynamicAttributesInstance()->has("$key.{$locale}")) { |
108
|
|
|
return $this->dynamic("$key.{$locale}"); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
if ($this->dynamicAttributesInstance()->has($key)) { |
112
|
|
|
$value = $this->dynamic($key); |
113
|
|
|
|
114
|
|
|
// If value is localized, we wont return the entire value, but instead return null since no fallback will be provided. |
115
|
|
|
if (is_array($value) && count(array_intersect($this->dynamicLocales(), array_keys($value))) > 0 && in_array($locale, $this->dynamicLocales())) { |
116
|
|
|
return null; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
return $value; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
return parent::getAttribute($key); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/* Part of the custom cast */ |
126
|
|
|
public function setAttribute($key, $value) |
127
|
|
|
{ |
128
|
|
|
if ($this->isDynamicKey($key)) { |
129
|
|
|
return $this->setDynamic($key, $value); |
|
|
|
|
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
return parent::setAttribute($key, $value); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/* Part of the custom cast */ |
136
|
|
|
protected function dynamicAttributesInstance(): DynamicAttributes |
137
|
|
|
{ |
138
|
|
|
if (!isset($this->attributes[$this->getDynamicKey()])) { |
139
|
|
|
$this->attributes[$this->getDynamicKey()] = DynamicAttributes::fromRawValue([]); |
|
|
|
|
140
|
|
|
} elseif (!($raw = $this->attributes[$this->getDynamicKey()]) instanceof DynamicAttributes) { |
141
|
|
|
$this->attributes[$this->getDynamicKey()] = DynamicAttributes::fromRawValue($raw); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
return $this->attributes[$this->getDynamicKey()]; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Inject the dynamic attributes into the attributes array. |
149
|
|
|
* Either as a DynamicAttributes instance or back to a json format. |
150
|
|
|
* |
151
|
|
|
* @param array $attributes |
152
|
|
|
* @param bool $castToObject |
153
|
|
|
* @return array |
154
|
|
|
*/ |
155
|
|
|
private function injectDynamicAttributes(array $attributes, bool $castToObject = true): array |
156
|
|
|
{ |
157
|
|
|
if (isset($attributes[$this->getDynamicKey()])) { |
158
|
|
|
$attributes[$this->getDynamicKey()] = $castToObject |
159
|
|
|
? DynamicAttributes::fromRawValue($attributes[$this->getDynamicKey()]) |
160
|
|
|
: $attributes[$this->getDynamicKey()]->toJson(); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
return $attributes; |
164
|
|
|
} |
165
|
|
|
} |
166
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.