Completed
Push — master ( 1e1451...9449be )
by David
02:05
created

CrudApi::setNamespace()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
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
    public function setModelHelper($modelHelper)
36
    {
37
        $this->modelHelper = $modelHelper;
38
    }
39
40
    /**
41
     * Set the namespace to search within.
42
     *
43
     * @param string $namespace
44
     */
45
    public function setNamespace($namespace)
46
    {
47
        $this->namespace = $namespace;
48
    }
49
50
    /**
51
     * Set the model to work with.
52
     *
53
     * @param $model
54
     *
55
     * @return $this
56
     */
57
    public function setModel($model)
58
    {
59
        $this->model = $model;
60
61
        return $this;
62
    }
63
64
    /**
65
     * Set a model instance from which to work.
66
     *
67
     * @param $item
68
     *
69
     * @return $this
70
     */
71
    public function setInstance($item)
72
    {
73
        $this->instance = $item;
74
75
        return $this;
76
    }
77
78
    public function getFields()
79
    {
80
        $model = $this->getModelInstance();
81
        $fields = $model->getFillable();
82
        $filtered_fields = [];
83
84
        foreach ($fields as $f) {
85
            if ($f !== 'password' && $f !== 'Password') {
86
                $filtered_fields[] = $f;
87
            }
88
        }
89
90
        return $filtered_fields;
91
    }
92
93
    public function getModelDisplayName()
94
    {
95
        $instance = $this->getModelInstance();
96
        $display = isset($instance->display) ? $instance->display : ucfirst($this->model);
97
98
        return $display;
99
    }
100
101
    public function getModelInstance()
102
    {
103
        if ($this->instance !== null) {
104
            return $this->instance;
105
        }
106
        $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...
107
        if ($model === false) {
108
            return false;
109
        }
110
        $instance = new $model();
111
112
        return $instance;
113
    }
114
115
    public function modal($type)
116
    {
117
        $trimmed_item = $this->trimmedModel();
118
119
        $modalId = $type.$trimmed_item.'Modal';
120
121
        $display = $this->getModelDisplayName();
122
123
        $modal = (object) [
124
            'id' => $modalId,
125
            'title' => ucfirst($type).' '.$display,
126
            'url' => $this->modalUrl($type),
127
        ];
128
129
        return $modal;
130
    }
131
132
    private function modalUrl($type)
1 ignored issue
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
133
    {
134
        switch ($type) {
135
        case 'edit':
136
            $action = 'update';
137
            break;
138
        default:
139
            $action = $type;
140
            break;
141
        }
142
143
        return route('crudapi.'.$action.'.item', $this->model);
144
    }
145
146
    public function jsMethodName($method)
147
    {
148
        // Method == create
149
        $jsMethod = $method.$this->trimmedModel();
150
151
        return $jsMethod;
152
    }
153
154
    public function renderFields($type, $fields = [])
155
    {
156
        if (empty($fields)) {
157
            $fields = $this->getFields();
158
        }
159
        $output = '';
160
161
        switch ($type) {
162
        case 'form-create':
163
            $output .= $this->fieldHelper->formCreate($fields);
164
            break;
165
        case 'form-edit':
166
            $output .= $this->fieldHelper->formEdit($fields);
167
            break;
168
        case 'table-headings':
169
            $output .= $this->fieldHelper->tableHeadings($fields);
170
            break;
171
        case 'table-content':
172
            $output .= $this->fieldHelper->tableContent($fields, $this->instance);
173
            break;
174
            // JavaScript Variables
175
        case 'js-var':
176
            foreach ($fields as $f) {
177
                $output .= 'var '.$f.' = '.$this->instance->$f.'; ';
178
            }
179
            break;
180
        case 'js-modal-create':
181
            foreach ($fields as $f) {
182
                $output .= '"'.$f.'": $(\'#createItem'.$f.'\').val(), ';
183
            }
184
            break;
185
        default:
186
            return;
187
                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...
188
        }
189
190
        echo $output;
191
    }
192
193
    public function trimmedModel()
194
    {
195
        return strpos($this->model, ' ') !== false ? implode('', explode(' ', $this->model)) : $this->model;
196
    }
197
198
    public function getRelatedOptions($relation)
199
    {
200
        $output = '';
201
202
        if (!method_exists($relation, 'toOptions')) {
203
            $relationItems = $relation::all();
204
            foreach ($relationItems as $item) {
205
                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...
206
                    // Error - there is no toOptions or name property.
207
                } else {
208
                    $output .= '<option value="'.$item->id.'">'.$item->name.'</option>';
209
                }
210
            }
211
        } else {
212
            $output .= $relation->toOptions();
213
        }
214
215
        return $output;
216
    }
217
218
    public function getRelatedModel($f)
219
    {
220
        $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...
221
        $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...
222
223
        $modelAliases = [
224
            'author' => 'user',
225
        ];
226
227
        // If class doesn't exist, check if is in aliases array.
228
        if (!class_exists($model)) {
229
            if (array_key_exists($field, $modelAliases)) {
230
                $aliasedModel = $modelAliases[$field];
231
                $model = $this->getModel($aliasedModel);
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...
232
            }
233
        }
234
235
        // Model could not be found, try via it's converted name.
236
        if (!class_exists($model)) {
237
            // Convert from DB format to Pascal
238
            $words = explode('_', $field);
239
            foreach ($words as $i => $w) {
240
                $words[$i] = ucfirst($w);
241
            }
242
            $formatted = implode('', $words);
243
            $model = 'App\\'.$formatted;
244
            if (!class_exists($model)) {
245
                return false;
246
            }
247
        }
248
249
        return new $model();
250
    }
251
252
    public function getRelatedDisplay($f)
253
    {
254
        $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...
255
256
        $field = $this->instance->$related_field;
257
258
        $class = get_class($field);
259
260
        switch ($class) {
261
        case 'App\\Helpers\\CrudApi':
262
            break;
263
        case 'App\\Indicator':
264
            return $field->indicator;
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
        case 'Taskforcedev\\CrudApi\\Helpers\\CrudApi':
267
            return false;
268
                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...
269
        default:
270
            return $field->name;
271
                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...
272
        }
273
    }
274
275
    /**
276
     * Allow certain methods to be passed through to the specified
277
     * helper in order to make methods easier to remember.
278
     *
279
     * @param $method
280
     * @param $args
281
     *
282
     * @return bool
283
     */
284
    public function __call($method, $args)
285
    {
286
        switch ($method) {
287
            case 'getRelatedField':
288
                return $this->fieldHelper->getRelatedField($args[0]);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->fieldHelpe...RelatedField($args[0]); (string) is incompatible with the return type documented by Taskforcedev\CrudApi\Helpers\CrudApi::__call of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
289
            case 'getPrimaryField':
290
                return $this->fieldHelper->getPrimaryField($args[0]);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->fieldHelpe...PrimaryField($args[0]); (string) is incompatible with the return type documented by Taskforcedev\CrudApi\Helpers\CrudApi::__call of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
291
            case 'isIdField':
292
                return $this->fieldHelper->isIdField($args[0]);
293
            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...
294
                if (isset($args[0])) {
295
                    return $this->modelHelper->getModel($args[0]);
296
                } else {
297
                    return $this->modelHelper->getModel();
298
                }
299
            default:
300
                break;
301
        }
302
    }
303
}
304