Completed
Push — master ( da6b7c...20aa8d )
by David
02:24
created

CrudApi::getRelatedField()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
3
namespace Taskforcedev\CrudApi\Helpers;
4
5
use Illuminate\Console\AppNamespaceDetectorTrait;
6
7
class CrudApi
8
{
9
    use AppNamespaceDetectorTrait;
10
11
    public $model;
12
    public $instance;
13
    public $namespace;
14
    public $modelHelper;
15
    public $fieldHelper;
16
17
    public function __construct($options = [])
18
    {
19
        /* Set the namespace */
20
        if (array_key_exists('namespace', $options)) {
21
            $this->namespace = $options['namespace'];
22
        } else {
23
            $this->namespace = $this->getAppNamespace();
24
        }
25
26
        /* Set the model */
27
        if (array_key_exists('model', $options)) {
28
            $this->model = $options['model'];
29
        }
30
31
        $this->modelHelper = new Model($this);
32
        $this->fieldHelper = new Field($this);
33
    }
34
35
    /**
36
     * Set the namespace to search within.
37
     *
38
     * @param string $namespace
39
     */
40
    public function setNamespace($namespace)
41
    {
42
        $this->namespace = $namespace;
43
    }
44
45
    /**
46
     * Set the model to work with.
47
     *
48
     * @param $model
49
     *
50
     * @return $this
51
     */
52
    public function setModel($model)
53
    {
54
        $this->model = $model;
55
56
        return $this;
57
    }
58
59
    /**
60
     * Set a model instance from which to work.
61
     *
62
     * @param $item
63
     *
64
     * @return $this
65
     */
66
    public function setInstance($item)
67
    {
68
        $this->instance = $item;
69
70
        return $this;
71
    }
72
73
    public function getFields()
74
    {
75
        $model = $this->getModelInstance();
76
        $fields = $model->getFillable();
77
        $filtered_fields = [];
78
79
        foreach ($fields as $f) {
80
            if ($f !== 'password' && $f !== 'Password') {
81
                $filtered_fields[] = $f;
82
            }
83
        }
84
85
        return $filtered_fields;
86
    }
87
88
    public function getModelDisplayName()
89
    {
90
        $instance = $this->getModelInstance();
91
        $display = isset($instance->display) ? $instance->display : ucfirst($this->model);
92
93
        return $display;
94
    }
95
96
    public function getModelInstance()
97
    {
98
        if ($this->instance !== null) {
99
            return $this->instance;
100
        }
101
        $model = $this->getModel();
1 ignored issue
show
Bug introduced by
The method getModel() does not exist on Taskforcedev\CrudApi\Helpers\CrudApi. Did you maybe mean getModelDisplayName()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
102
        if ($model === false) {
103
            return false;
104
        }
105
        $instance = new $model();
106
107
        return $instance;
108
    }
109
110
    public function modal($type)
111
    {
112
        $trimmed_item = $this->trimmedModel();
113
114
        $modalId = $type.$trimmed_item.'Modal';
115
116
        $display = $this->getModelDisplayName();
117
118
        $modal = (object) [
119
            'id' => $modalId,
120
            'title' => ucfirst($type).' '.$display,
121
            'url' => $this->modalUrl($type),
122
        ];
123
124
        return $modal;
125
    }
126
127
    private function modalUrl($type)
1 ignored issue
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
128
    {
129
        switch ($type) {
130
        case 'edit':
131
            $action = 'update';
132
            break;
133
        default:
134
            $action = $type;
135
            break;
136
        }
137
138
        return route('crudapi.'.$action.'.item', $this->model);
139
    }
140
141
    public function jsMethodName($method)
142
    {
143
        // Method == create
144
        $jsMethod = $method.$this->trimmedModel();
145
146
        return $jsMethod;
147
    }
148
149
    public function renderFields($type)
150
    {
151
        $fields = $this->getFields();
152
        $instance = $this->getModelInstance();
0 ignored issues
show
Unused Code introduced by
$instance 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...
153
        $output = '';
154
155
        switch ($type) {
156 View Code Duplication
        case 'form-create':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
157
            foreach ($fields as $f) {
158
                $ucF = ucfirst($f);
159
160
                $input_attr = [
161
                    'class' => 'form-control',
162
                    'id' => 'createItem'.$f,
163
                    'name' => $f,
164
                ];
165
166
                $output .= '<fieldset class="form-group">';
167
168
                $output .= '<label for="'.$input_attr['id'].'">'.$ucF.'</label>';
169
170
                if ($this->fieldHelper->isIdField($f)) {
171
                    $input_attr['type'] = 'select';
172
173
                    $output .= '<select ';
174
                    foreach ($input_attr as $attr => $value) {
175
                        $output .= "{$attr}='{$value}'";
176
                    }
177
178
                    $relation = $this->getRelatedModel($f);
179
                    $output .= '>';
180
181
                    $output .= $this->getRelatedOptions($relation);
182
183
                    $output .= '</select>';
184
                } else {
185
                    $input_attr['type'] = 'text';
186
187
                    $output .= '<input ';
188
                    foreach ($input_attr as $attr => $value) {
189
                        $output .= "{$attr}='{$value}'";
190
                    }
191
                    $output .= '>';
192
                }
193
194
                $output .= '</fieldset>';
195
            }
196
            break;
197 View Code Duplication
        case 'form-edit':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
198
            foreach ($fields as $f) {
199
                $ucF = ucfirst($f);
200
201
                $input_attr = [
202
                    'class' => 'form-control',
203
                    'id' => 'editItem'.$ucF,
204
                    'name' => $f,
205
                ];
206
207
                $output .= '<fieldset class="form-group">';
208
209
                $output .= '<label for="'.$input_attr['id'].'">'.$ucF.'</label>';
210
211
                if ($this->fieldHelper->isIdField($f)) {
212
                    $input_attr['type'] = 'select';
213
214
                    $output .= '<select ';
215
                    foreach ($input_attr as $attr => $value) {
216
                        $output .= "{$attr}='{$value}'";
217
                    }
218
219
                    $relation = $this->getRelatedModel($f);
220
                    $output .= '>';
221
222
                    $output .= $this->getRelatedOptions($relation);
223
                    $output .= '</select>';
224
                } else {
225
                    $input_attr['type'] = 'text';
226
227
                    $output .= '<input ';
228
                    foreach ($input_attr as $attr => $value) {
229
                        $output .= "{$attr}='{$value}'";
230
                    }
231
                    $output .= '>';
232
                }
233
234
                $output .= '</fieldset>';
235
            }
236
            break;
237
        case 'table-headings':
238
            foreach ($fields as $f) {
239
                $output .= '<th>'.ucfirst($f).'</th>';
240
            }
241
            break;
242
        case 'table-content':
243
            foreach ($fields as $f) {
244
                if ($this->fieldHelper->isIdField($f)) {
245
                    $display = $this->getRelatedDisplay($f);
246
                    $output .= '<td>'.$display.'</td>';
247
                } else {
248
                    $output .= '<td>'.$this->instance->$f.'</td>';
249
                }
250
            }
251
            break;
252
            // JavaScript Variables
253
        case 'js-var':
254
            foreach ($fields as $f) {
255
                $output .= 'var '.$f.' = '.$this->instance->$f.'; ';
256
            }
257
            break;
258
        case 'js-modal-create':
259
            foreach ($fields as $f) {
260
                $output .= '"'.$f.'": $(\'#createItem'.$f.'\').val(), ';
261
            }
262
            break;
263
        default:
264
            return;
265
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
266
        }
267
268
        echo $output;
269
    }
270
271
    public function trimmedModel()
272
    {
273
        return strpos($this->model, ' ') !== false ? implode('', explode(' ', $this->model)) : $this->model;
274
    }
275
276
    public function getRelatedOptions($relation)
277
    {
278
        $output = '';
279
280
        if (!method_exists($relation, 'toOptions')) {
281
            $relationItems = $relation::all();
282
            foreach ($relationItems as $item) {
283
                if (!isset($item->name)) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
284
                    // Error - there is no toOptions or name property.
285
                } else {
286
                    $output .= '<option value="'.$item->id.'">'.$item->name.'</option>';
287
                }
288
            }
289
        } else {
290
            $output .= $relation->toOptions();
291
        }
292
293
        return $output;
294
    }
295
296
    public function getRelatedModel($f)
297
    {
298
        $field = $this->getRelatedField($f);
0 ignored issues
show
Documentation Bug introduced by
The method getRelatedField does not exist on object<Taskforcedev\CrudApi\Helpers\CrudApi>? 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...
299
        $model = $this->getModel($field);
0 ignored issues
show
Bug introduced by
The method getModel() does not exist on Taskforcedev\CrudApi\Helpers\CrudApi. Did you maybe mean getModelDisplayName()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
300
301
        if (!class_exists($model)) {
302
            // Convert from DB format to Pascal
303
            $words = explode('_', $field);
304
            foreach ($words as $i => $w) {
305
                $words[$i] = ucfirst($w);
306
            }
307
            $formatted = implode('', $words);
308
            $model = 'App\\'.$formatted;
309
            if (!class_exists($model)) {
310
                return false;
311
            }
312
        }
313
314
        return new $model();
315
    }
316
317
    public function getRelatedDisplay($f)
318
    {
319
        $related_field = $this->getRelatedField($f);
0 ignored issues
show
Documentation Bug introduced by
The method getRelatedField does not exist on object<Taskforcedev\CrudApi\Helpers\CrudApi>? 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...
320
321
        $field = $this->instance->$related_field;
322
323
        $class = get_class($field);
324
325
        switch ($class) {
326
        case 'App\\Helpers\\CrudApi':
327
            break;
328
        case 'App\\Indicator':
329
            return $field->indicator;
330
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
331
        case 'Taskforcedev\\CrudApi\\Helpers\\CrudApi':
332
            return false;
333
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
334
        default:
335
            return $field->name;
336
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
337
        }
338
    }
339
340
    /**
341
     * Allow certain methods to be passed through to the specified
342
     * helper in order to make methods easier to remember.
343
     *
344
     * @param $method
345
     * @param $args
346
     *
347
     * @return bool
348
     */
349
    public function __call($method, $args)
350
    {
351
        switch ($method) {
352
            case 'getRelatedField':
353
                return $this->fieldHelper->getRelatedField($args[0]);
354
            case 'getPrimaryField':
355
                return $this->fieldHelper->getPrimaryField($args[0]);
356
            case 'isIdField':
357
                return $this->fieldHelper->isIdField($args[0]);
358
            case 'getModel':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
359
                if (isset($args[0])) {
360
                    return $this->modelHelper->getModel($args[0]);
361
                } else {
362
                    return $this->modelHelper->getModel();
363
                }
364
            default:
365
                break;
366
        }
367
    }
368
}
369