1 | <?php |
||||||
2 | |||||||
3 | namespace SleepingOwl\Admin\Traits; |
||||||
4 | |||||||
5 | use Illuminate\Database\Eloquent\Model; |
||||||
6 | use Illuminate\Database\Eloquent\Relations\HasOneOrMany; |
||||||
7 | use Illuminate\Support\Arr; |
||||||
8 | use Illuminate\Support\Collection; |
||||||
9 | use SleepingOwl\Admin\Contracts\Repositories\RepositoryInterface; |
||||||
10 | use SleepingOwl\Admin\Exceptions\Form\Element\SelectException; |
||||||
11 | |||||||
12 | trait SelectOptionsFromModel |
||||||
13 | { |
||||||
14 | /** |
||||||
15 | * @var Model |
||||||
16 | */ |
||||||
17 | protected $modelForOptions; |
||||||
18 | |||||||
19 | /** |
||||||
20 | * @var string |
||||||
21 | */ |
||||||
22 | protected $display = 'title'; |
||||||
23 | |||||||
24 | /** |
||||||
25 | * @var string|null |
||||||
26 | */ |
||||||
27 | protected $foreignKey = null; |
||||||
28 | |||||||
29 | /** |
||||||
30 | * @var string|null |
||||||
31 | */ |
||||||
32 | protected $usageKey = null; |
||||||
33 | |||||||
34 | /** |
||||||
35 | * @var array |
||||||
36 | */ |
||||||
37 | protected $fetchColumns = []; |
||||||
38 | |||||||
39 | /** |
||||||
40 | * @var \Closure|object callable |
||||||
41 | */ |
||||||
42 | protected $loadOptionsQueryPreparer; |
||||||
43 | |||||||
44 | /** |
||||||
45 | * @var bool |
||||||
46 | */ |
||||||
47 | protected $isEmptyRelation = false; |
||||||
48 | |||||||
49 | /** |
||||||
50 | * @return Model |
||||||
51 | */ |
||||||
52 | public function getModelForOptions() |
||||||
53 | { |
||||||
54 | return $this->modelForOptions; |
||||||
55 | } |
||||||
56 | |||||||
57 | /** |
||||||
58 | * @param @param string|Model $modelForOptions |
||||||
59 | * |
||||||
60 | * @return $this |
||||||
61 | * @throws SelectException |
||||||
62 | */ |
||||||
63 | public function setModelForOptions($modelForOptions, $display = null) |
||||||
64 | { |
||||||
65 | if ($display) { |
||||||
66 | $this->display = $display; |
||||||
67 | } |
||||||
68 | |||||||
69 | if (is_string($modelForOptions)) { |
||||||
70 | $modelForOptions = app($modelForOptions); |
||||||
71 | } |
||||||
72 | |||||||
73 | if (! ($modelForOptions instanceof Model)) { |
||||||
74 | throw new SelectException('Class must be instanced of Illuminate\Database\Eloquent\Model'); |
||||||
75 | } |
||||||
76 | |||||||
77 | $this->modelForOptions = $modelForOptions; |
||||||
78 | |||||||
79 | return $this; |
||||||
80 | } |
||||||
81 | |||||||
82 | /** |
||||||
83 | * @param string $key |
||||||
84 | * |
||||||
85 | * @return $this |
||||||
86 | */ |
||||||
87 | public function setUsageKey($key) |
||||||
88 | { |
||||||
89 | $this->usageKey = $key; |
||||||
90 | |||||||
91 | return $this; |
||||||
92 | } |
||||||
93 | |||||||
94 | /** |
||||||
95 | * @return string |
||||||
96 | */ |
||||||
97 | public function getDisplay() |
||||||
98 | { |
||||||
99 | return $this->display; |
||||||
100 | } |
||||||
101 | |||||||
102 | /** |
||||||
103 | * @param string|\Closure $display |
||||||
104 | * |
||||||
105 | * @return $this |
||||||
106 | */ |
||||||
107 | public function setDisplay($display) |
||||||
108 | { |
||||||
109 | $this->display = $display; |
||||||
0 ignored issues
–
show
|
|||||||
110 | |||||||
111 | return $this; |
||||||
112 | } |
||||||
113 | |||||||
114 | /** |
||||||
115 | * Set Only fetch columns. |
||||||
116 | * |
||||||
117 | * If use {@link Select#setModelForOptions($model)}, on fetch |
||||||
118 | * data from the $model table, only specified columns has be |
||||||
119 | * feched. |
||||||
120 | * |
||||||
121 | * Examples: <code>setFetchColumns('title')</code> or |
||||||
122 | * <code>setFetchColumns(['title'])</code> or |
||||||
123 | * <code>setFetchColumns('title', 'position')</code> or |
||||||
124 | * <code>setFetchColumns(['title', 'position'])</code>. |
||||||
125 | * |
||||||
126 | * @param string|array $columns |
||||||
127 | * |
||||||
128 | * @return $this |
||||||
129 | */ |
||||||
130 | public function setFetchColumns($columns) |
||||||
131 | { |
||||||
132 | if (! is_array($columns)) { |
||||||
133 | $columns = func_get_args(); |
||||||
134 | } |
||||||
135 | |||||||
136 | $this->fetchColumns = $columns; |
||||||
137 | |||||||
138 | return $this; |
||||||
139 | } |
||||||
140 | |||||||
141 | /** |
||||||
142 | * Get the fetch columns. |
||||||
143 | * |
||||||
144 | * @return array |
||||||
145 | */ |
||||||
146 | public function getFetchColumns() |
||||||
147 | { |
||||||
148 | return $this->fetchColumns; |
||||||
149 | } |
||||||
150 | |||||||
151 | /** |
||||||
152 | * Set Callback for prepare load options Query. |
||||||
153 | * |
||||||
154 | * Example: |
||||||
155 | * <code> |
||||||
156 | * <?php |
||||||
157 | * AdminFormElement::select('column', 'Label') |
||||||
158 | * ->modelForOptions(MyModel::class) |
||||||
159 | * ->setLoadOptionsQueryPreparer(function($item, QueryBuilder $query) { |
||||||
160 | * return $query |
||||||
161 | * ->where('column', 'value') |
||||||
162 | * ->were('owner_id', Auth::user()->id) |
||||||
163 | * }); |
||||||
164 | * ?> |
||||||
165 | * </code> |
||||||
166 | * |
||||||
167 | * @param callable $callback The Callback with $item and $options args. |
||||||
168 | * |
||||||
169 | * @return $this |
||||||
170 | */ |
||||||
171 | public function setLoadOptionsQueryPreparer($callback) |
||||||
172 | { |
||||||
173 | $this->loadOptionsQueryPreparer = $callback; |
||||||
174 | |||||||
175 | return $this; |
||||||
176 | } |
||||||
177 | |||||||
178 | /** |
||||||
179 | * Get Callback for prepare load options Query. |
||||||
180 | * |
||||||
181 | * @return callable |
||||||
182 | */ |
||||||
183 | public function getLoadOptionsQueryPreparer() |
||||||
184 | { |
||||||
185 | return $this->loadOptionsQueryPreparer; |
||||||
0 ignored issues
–
show
|
|||||||
186 | } |
||||||
187 | |||||||
188 | /** |
||||||
189 | * @param null|string $foreignKey |
||||||
190 | * |
||||||
191 | * @return $this |
||||||
192 | */ |
||||||
193 | public function setForeignKey($foreignKey) |
||||||
194 | { |
||||||
195 | $this->foreignKey = $foreignKey; |
||||||
196 | |||||||
197 | return $this; |
||||||
198 | } |
||||||
199 | |||||||
200 | /** |
||||||
201 | * @return $this |
||||||
202 | */ |
||||||
203 | public function onlyEmptyRelation() |
||||||
204 | { |
||||||
205 | $this->isEmptyRelation = true; |
||||||
206 | |||||||
207 | return $this; |
||||||
208 | } |
||||||
209 | |||||||
210 | /** |
||||||
211 | * @return bool |
||||||
212 | */ |
||||||
213 | public function isEmptyRelation() |
||||||
214 | { |
||||||
215 | return $this->isEmptyRelation; |
||||||
216 | } |
||||||
217 | |||||||
218 | /** |
||||||
219 | * @return array |
||||||
220 | */ |
||||||
221 | protected function loadOptions() |
||||||
222 | { |
||||||
223 | $repository = app(RepositoryInterface::class); |
||||||
224 | $repository->setModel($this->getModelForOptions()); |
||||||
225 | $key = ($this->usageKey) ? $this->usageKey : $repository->getModel()->getKeyName(); |
||||||
226 | |||||||
227 | $options = $repository->getQuery(); |
||||||
228 | |||||||
229 | if ($this->isEmptyRelation() && ! is_null($foreignKey = $this->getForeignKey())) { |
||||||
0 ignored issues
–
show
It seems like
getForeignKey() 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
![]() |
|||||||
230 | $relation = $this->getModelAttributeKey(); |
||||||
0 ignored issues
–
show
It seems like
getModelAttributeKey() 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
![]() |
|||||||
231 | $model = $this->getModel(); |
||||||
0 ignored issues
–
show
The method
getModel() does not exist on SleepingOwl\Admin\Traits\SelectOptionsFromModel . Did you maybe mean getModelForOptions() ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
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. ![]() |
|||||||
232 | |||||||
233 | if ($model->{$relation}() instanceof HasOneOrMany) { |
||||||
234 | $options->where($foreignKey, 0)->orWhereNull($foreignKey); |
||||||
235 | } |
||||||
236 | } |
||||||
237 | |||||||
238 | if (count($this->getFetchColumns()) > 0) { |
||||||
239 | $options->select(array_merge([$key], $this->getFetchColumns())); |
||||||
240 | } |
||||||
241 | |||||||
242 | // call the pre load options query preparer if has be set |
||||||
243 | if (! is_null($preparer = $this->getLoadOptionsQueryPreparer())) { |
||||||
0 ignored issues
–
show
|
|||||||
244 | $options = $preparer($this, $options); |
||||||
245 | } |
||||||
246 | $options = $options->get(); |
||||||
247 | |||||||
248 | //some fix for setUsage |
||||||
249 | $key = str_replace('->', '.', $key); |
||||||
250 | |||||||
251 | if (is_callable($makeDisplay = $this->getDisplay())) { |
||||||
252 | // make dynamic display text |
||||||
253 | if ($options instanceof Collection) { |
||||||
254 | $options = $options->all(); |
||||||
255 | } |
||||||
256 | |||||||
257 | // iterate for all options and redefine it as |
||||||
258 | // list of KEY and TEXT pair |
||||||
259 | $options = array_map(function ($opt) use ($key, $makeDisplay) { |
||||||
260 | // get the KEY and make the display text |
||||||
261 | return [data_get($opt, $key), $makeDisplay($opt)]; |
||||||
262 | }, $options); |
||||||
263 | |||||||
264 | // take options as array with KEY => VALUE pair |
||||||
265 | $options = Arr::pluck($options, 1, 0); |
||||||
266 | } elseif ($options instanceof Collection) { |
||||||
267 | // take options as array with KEY => VALUE pair |
||||||
268 | $options = Arr::pluck($options->all(), $this->getDisplay(), $key); |
||||||
269 | } else { |
||||||
270 | // take options as array with KEY => VALUE pair |
||||||
271 | $options = Arr::pluck($options, $this->getDisplay(), $key); |
||||||
272 | } |
||||||
273 | |||||||
274 | return $options; |
||||||
275 | } |
||||||
276 | } |
||||||
277 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.