Test Setup Failed
Pull Request — master (#46)
by Alex
03:11
created

MetadataTrait   D

Complexity

Total Complexity 90

Size/Duplication

Total Lines 487
Duplicated Lines 6.57 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 72.85%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 90
c 1
b 0
f 0
lcom 1
cbo 7
dl 32
loc 487
ccs 110
cts 151
cp 0.7285
rs 4.8717

17 Methods

Rating   Name   Duplication   Size   Complexity  
B metadata() 0 46 5
A metadataMask() 0 16 3
A getEndpointName() 0 11 3
B getXmlSchema() 0 32 6
C hookUpRelationships() 14 32 8
F getRelationships() 18 123 38
B getAllAttributes() 0 25 4
D getRelationshipsFromMethods() 0 84 17
getVisible() 0 1 ?
getHidden() 0 1 ?
getKeyName() 0 1 ?
getConnectionName() 0 1 ?
getConnection() 0 1 ?
getAttributes() 0 1 ?
getTable() 0 1 ?
getFillable() 0 1 ?
B collectGetters() 0 18 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like MetadataTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MetadataTrait, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace AlgoWeb\PODataLaravel\Models;
3
4
use Illuminate\Database\Eloquent\Relations\BelongsTo;
5
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
6
use Illuminate\Database\Eloquent\Relations\MorphMany;
7
use Illuminate\Database\Eloquent\Relations\MorphOne;
8
use Illuminate\Database\Eloquent\Relations\MorphToMany;
9
use Illuminate\Support\Facades\App as App;
10
use Illuminate\Database\Eloquent\Relations\Relation;
11
use POData\Providers\Metadata\ResourceStreamInfo;
12
use POData\Providers\Metadata\Type\EdmPrimitiveType;
13
use Illuminate\Database\Eloquent\Model;
14
15
trait MetadataTrait
16
{
17
    /*
18
     * Array to record mapping between doctrine types and OData types
19
     */
20
    protected $mapping = [
21
        'integer' => EdmPrimitiveType::INT32,
22
        'string' => EdmPrimitiveType::STRING,
23
        'datetime' => EdmPrimitiveType::DATETIME,
24
        'float' => EdmPrimitiveType::SINGLE,
25
        'decimal' => EdmPrimitiveType::DECIMAL,
26
        'text' => EdmPrimitiveType::STRING,
27
        'boolean' => EdmPrimitiveType::BOOLEAN,
28 3
        'blob' => "stream"
29
    ];
30 3
31
    /*
32
     * Retrieve and assemble this model's metadata for OData packaging
33 2
     */
34 2
    public function metadata()
35
    {
36 2
        assert($this instanceof Model, get_class($this));
37
38 2
        // Break these out separately to enable separate reuse
39 1
        $connect = $this->getConnection();
40
        $builder = $connect->getSchemaBuilder();
41
42 1
        $table = $this->getTable();
43 1
44 1
        if (!$builder->hasTable($table)) {
45
            return [];
46 1
        }
47
48 1
        $columns = $builder->getColumnListing($table);
49 1
        $mask = $this->metadataMask();
50 1
        $columns = array_intersect($columns, $mask);
51
52 1
        $tableData = [];
53 1
54 1
        $rawFoo = $connect->getDoctrineSchemaManager()->listTableColumns($table);
55
        $foo = [];
56 1
        $getters = $this->collectGetters();
57
58 1
        foreach ($rawFoo as $key => $val) {
59 1
            // Work around glitch in Doctrine when reading from MariaDB which added ` characters to root key value
60 1
            $key = trim($key, '`');
61 1
            $foo[$key] = $val;
62 1
        }
63 1
64 1
        foreach ($columns as $column) {
65
            // Doctrine schema manager returns columns with lowercased names
66 1
            $rawColumn = $foo[strtolower($column)];
67
            $nullable = !($rawColumn->getNotNull());
68
            $fillable = in_array($column, $this->getFillable());
69
            $rawType = $rawColumn->getType();
70
            $type = $rawType->getName();
71
            $tableData[$column] = ['type' => $type, 'nullable' => $nullable, 'fillable' => $fillable];
72
        }
73 4
74
        foreach ($getters as $get) {
75 4
            $tableData[$get] = ['type' => 'text', 'nullable' => false, 'fillable' => false];
76
        }
77 4
78 4
        return $tableData;
79 4
    }
80 2
81 4
    /*
82 1
     * Return the set of fields that are permitted to be in metadata
83 1
     * - following same visible-trumps-hidden guideline as Laravel
84
     */
85 4
    public function metadataMask()
86
    {
87
        $attribs = array_keys($this->getAllAttributes());
88
89
        $visible = $this->getVisible();
90
        $hidden = $this->getHidden();
91 5
        if (0 < count($visible)) {
92
            assert(!empty($visible));
93 5
            $attribs = array_intersect($visible, $attribs);
94 5
        } elseif (0 < count($hidden)) {
95 1
            assert(!empty($hidden));
96
            $attribs = array_diff($attribs, $hidden);
97
        }
98 4
99
        return $attribs;
100 4
    }
101
102 4
    /*
103 4
     * Get the endpoint name being exposed
104 4
     *
105 4
     */
106 4
    public function getEndpointName()
107 4
    {
108
        $endpoint = isset($this->endpoint) ? $this->endpoint : null;
109 4
110 1
        if (!isset($endpoint)) {
111 1
            $bitter = get_class();
112 1
            $name = substr($bitter, strrpos($bitter, '\\')+1);
113 1
            return strtolower($name);
114
        }
115 4
        return strtolower($endpoint);
116 4
    }
117
118 4
    /*
119
     * Assemble this model's OData metadata as xml schema
120
     */
121
    public function getXmlSchema($MetaNamespace = "Data")
122
    {
123
        $raw = $this->metadata();
124
        if ([] == $raw) {
125
            return null;
126
        }
127
128
        $metadata = App::make('metadata');
129
130
        $rf = new \ReflectionClass(get_class($this));
131
        $complex = $metadata->addEntityType($rf, $rf->getShortName(), $MetaNamespace);
132
        $keyName = $this->getKeyName();
133
        if (null != $keyName) {
134
            $metadata->addKeyProperty($complex, $keyName, $this->mapping[$raw[$keyName]['type']]);
135
        }
136
137
        foreach ($raw as $key => $secret) {
138
            if ($key == $keyName) {
139
                continue;
140
            }
141
            if ($secret['type'] == "blob") {
142
                $complex->setMediaLinkEntry(true);
143
                $streamInfo = new ResourceStreamInfo($key);
144
                assert($complex->isMediaLinkEntry());
145
                $complex->addNamedStream($streamInfo);
146
                continue;
147
            }
148
            $metadata->addPrimitiveProperty($complex, $key, $this->mapping[$secret['type']]); // tag as isBag?
149
        }
150
151
        return $complex;
152
    }
153
154
    public function hookUpRelationships($entityTypes, $resourceSets)
155
    {
156
        assert(is_array($entityTypes) && is_array($resourceSets), "Both entityTypes and resourceSets must be arrays");
157
        $metadata = App::make('metadata');
158
        $rel = $this->getRelationshipsFromMethods();
159
        $thisClass = get_class($this);
160
        $thisInTypes = array_key_exists($thisClass, $entityTypes);
161
        $thisInSets = array_key_exists($thisClass, $resourceSets);
162
163
        if (!($thisInSets && $thisInTypes)) {
164
            return $rel;
165
        }
166
167
        $resourceType = $entityTypes[$thisClass];
168
        // if $r is in $combined keys, then its in keyspaces of both $entityTypes and $resourceSets
169
        $combinedKeys = array_intersect(array_keys($entityTypes), array_keys($resourceSets));
170 View Code Duplication
        foreach ($rel["HasOne"] as $n => $r) {
171
            $r = trim($r, '\\');
172
            if (in_array($r, $combinedKeys)) {
173
                $targResourceSet = $resourceSets[$r];
174
                $metadata->addResourceReferenceProperty($resourceType, $n, $targResourceSet);
175 3
            }
176
        }
177 3 View Code Duplication
        foreach ($rel["HasMany"] as $n => $r) {
1 ignored issue
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...
178
            $r = trim($r, '\\');
179 3
            if (in_array($r, $combinedKeys)) {
180 3
                $targResourceSet = $resourceSets[$r];
181 3
                $metadata->addResourceSetReferenceProperty($resourceType, $n, $targResourceSet);
182 3
            }
183 3
        }
184 3
        return $rel;
185 3
    }
186 3
187 3
    public function getRelationships()
188 3
    {
189
        $hooks = [];
190 3
191
        $rels = $this->getRelationshipsFromMethods(true);
192 3
193 3
        foreach ($rels['UnknownPolyMorphSide'] as $property => $foo) {
194 3
            $isMany = $foo instanceof MorphToMany;
195 3
            $targ = get_class($foo->getRelated());
196 3
            $mult = $isMany ? '*' : '1';
197 3
198 3 View Code Duplication
            if ($isMany) {
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...
199 3
                $fkMethodName = method_exists($foo, 'getQualifiedForeignKeyName')
200 3
                    ? 'getQualifiedForeignKeyName' : 'getQualifiedForeignPivotKeyName';
201 3
                $rkMethodName = method_exists($foo, 'getQualifiedRelatedKeyName')
202
                    ? 'getQualifiedRelatedKeyName' : 'getQualifiedRelatedPivotKeyName';
203 3
            }
204 3
205 3
            $keyRaw = $isMany ? $foo->$fkMethodName() : $foo->getForeignKey();
1 ignored issue
show
Bug introduced by
The variable $fkMethodName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
206 3
            $keySegments = explode('.', $keyRaw);
207 3
            $keyName = $keySegments[count($keySegments) - 1];
208 3
            $localRaw = $isMany ? $foo->$rkMethodName() : $foo->getQualifiedParentKeyName();
1 ignored issue
show
Bug introduced by
The variable $rkMethodName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
209 3
            $localSegments = explode('.', $localRaw);
210 3
            $localName = $localSegments[count($localSegments) - 1];
211
212 3
            $first = $keyName;
213 3
            $last = $localName;
214 3
            if (!isset($hooks[$first])) {
215
                $hooks[$first] = [];
216 3
            }
217 3
            $hooks[$first][$targ] = [
218 3
                'property' => $property,
219 3
                'local' => $last,
220 3
                'multiplicity' => $mult
221
            ];
222 1
        }
223 3
224
        foreach ($rels['KnownPolyMorphSide'] as $property => $foo) {
225 1
            $isMany = $foo instanceof MorphToMany;
226
            $targ = get_class($foo->getRelated());
227 1
            $mult = $isMany ? '*' : $foo instanceof MorphMany ? '*' : '1';
228
            $mult = $foo instanceof MorphOne ? '0..1' : $mult;
229 1
230 View Code Duplication
            if ($isMany) {
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...
231 3
                $fkMethodName = method_exists($foo, 'getQualifiedForeignKeyName')
232 2
                    ? 'getQualifiedForeignKeyName' : 'getQualifiedForeignPivotKeyName';
233 2
                $rkMethodName = method_exists($foo, 'getQualifiedRelatedKeyName')
234 3
                    ? 'getQualifiedRelatedKeyName' : 'getQualifiedRelatedPivotKeyName';
235 3
            }
236 3
237 3
            $keyRaw = $isMany ? $foo->$fkMethodName() : $foo->getForeignKeyName();
238 3
            $keySegments = explode('.', $keyRaw);
239 3
            $keyName = $keySegments[count($keySegments) - 1];
240 3
            $localRaw = $isMany ? $foo->$rkMethodName() : $foo->getQualifiedParentKeyName();
241
            $localSegments = explode('.', $localRaw);
242
            $localName = $localSegments[count($localSegments) - 1];
243
            $first = $isMany ? $keyName : $localName;
244
            $last = $isMany ? $localName : $keyName;
245
            if (!isset($hooks[$first])) {
246
                $hooks[$first] = [];
247
            }
248
            $hooks[$first][$targ] = [
249
                'property' => $property,
250
                'local' => $last,
251
                'multiplicity' => $mult
252
            ];
253
        }
254
255
        foreach ($rels['HasOne'] as $property => $foo) {
256
            if ($foo instanceof MorphOne) {
257
                continue;
258
            }
259
            $isBelong = $foo instanceof BelongsTo;
260
            $mult = $isBelong ? '1' : '0..1';
261
            $targ = get_class($foo->getRelated());
262
            $keyName = $isBelong ? $foo->getForeignKey() : $foo->getForeignKeyName();
263
            $localRaw = $isBelong ? $foo->getOwnerKey() : $foo->getQualifiedParentKeyName();
264
            $localSegments = explode('.', $localRaw);
265
            $localName = $localSegments[count($localSegments) - 1];
266
            $first = $isBelong ? $localName : $keyName;
267
            $last = $isBelong ? $keyName : $localName;
268
            if (!isset($hooks[$first])) {
269
                $hooks[$first] = [];
270
            }
271
            $hooks[$first][$targ] = [
272
                'property' => $property,
273
                'local' => $last,
274
                'multiplicity' => $mult
275
            ];
276
        }
277
        foreach ($rels['HasMany'] as $property => $foo) {
278
            if ($foo instanceof MorphMany || $foo instanceof MorphToMany) {
279
                continue;
280
            }
281
            $isBelong = $foo instanceof BelongsToMany;
282
            $mult = '*';
283
            $targ = get_class($foo->getRelated());
284
285 View Code Duplication
            if ($isBelong) {
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...
286
                $fkMethodName = method_exists($foo, 'getQualifiedForeignKeyName')
287
                    ? 'getQualifiedForeignKeyName' : 'getQualifiedForeignPivotKeyName';
288
                $rkMethodName = method_exists($foo, 'getQualifiedRelatedKeyName')
289
                    ? 'getQualifiedRelatedKeyName' : 'getQualifiedRelatedPivotKeyName';
290
            }
291
292
            $keyRaw = $isBelong ? $foo->$fkMethodName() : $foo->getForeignKeyName();
293
            $keySegments = explode('.', $keyRaw);
294
            $keyName = $keySegments[count($keySegments) - 1];
295
            $localRaw = $isBelong ? $foo->$rkMethodName() : $foo->getQualifiedParentKeyName();
296
            $localSegments = explode('.', $localRaw);
297
            $localName = $localSegments[count($localSegments) - 1];
298
            if (!isset($hooks[$keyName])) {
299
                $hooks[$keyName] = [];
300
            }
301
            $hooks[$keyName][$targ] = [
302
                'property' => $property,
303
                'local' => $localName,
304
                'multiplicity' => $mult
305
            ];
306
        }
307
308
        return $hooks;
309
    }
310
311
    protected function getAllAttributes()
312
    {
313
        // Adapted from http://stackoverflow.com/a/33514981
314
        // $columns = $this->getFillable();
315
        // Another option is to get all columns for the table like so:
316
        $builder = $this->getConnection()->getSchemaBuilder();
317
        $columns = $builder->getColumnListing($this->getTable());
318
        // but it's safer to just get the fillable fields
319
320
        $attributes = $this->getAttributes();
321
322
        foreach ($columns as $column) {
323
            if (!array_key_exists($column, $attributes)) {
324
                $attributes[$column] = null;
325
            }
326
        }
327
328
        $methods = $this->collectGetters();
329
330
        foreach ($methods as $method) {
331
            $attributes[$method] = null;
332
        }
333
334
        return $attributes;
335
    }
336
337
    protected function getRelationshipsFromMethods($biDir = false)
338
    {
339
        $model = $this;
340
        $relationships = array(
341
            "HasOne" => array(),
342
            "UnknownPolyMorphSide"=>array(),
343
            "HasMany"=>array(),
344
            "KnownPolyMorphSide"=>array()
345
        );
346
        $methods = get_class_methods($model);
347
        if (!empty($methods)) {
348
            foreach ($methods as $method) {
349
                if (!method_exists('Illuminate\Database\Eloquent\Model', $method)
350
                ) {
351
                    //Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
352
                    $reflection = new \ReflectionMethod($model, $method);
353
354
                    $file = new \SplFileObject($reflection->getFileName());
355
                    $file->seek($reflection->getStartLine()-1);
356
                    $code = '';
357
                    while ($file->key() < $reflection->getEndLine()) {
358
                        $code .= $file->current();
359
                        $file->next();
360
                    }
361
362
                    $code = trim(preg_replace('/\s\s+/', '', $code));
363
                    assert(false !== stripos($code, 'function'), 'Function definition must have keyword \'function\'');
364
                    $begin = strpos($code, 'function(');
365
                    $code = substr($code, $begin, strrpos($code, '}')-$begin+1);
366
                    $lastCode = $code[strlen($code)-1];
367
                    assert("}" == $lastCode, "Final character of function definition must be closing brace");
368
                    foreach (array(
369
                                'hasMany',
370
                                'hasManyThrough',
371
                                'belongsToMany',
372
                                'hasOne',
373
                                'belongsTo',
374
                                'morphOne',
375
                                'morphTo',
376
                                'morphMany',
377
                                'morphToMany',
378
                                'morphedByMany'
379
                                ) as $relation) {
380
                        $search = '$this->'.$relation.'(';
381
                        if ($pos = stripos($code, $search)) {
382
                            //Resolve the relation's model to a Relation object.
383
                            $relationObj = $model->$method();
384
                            if ($relationObj instanceof Relation) {
385
                                $relatedModel = '\\'.get_class($relationObj->getRelated());
386
                                $relations = [
387
                                    'hasManyThrough',
388
                                    'belongsToMany',
389
                                    'hasMany',
390
                                    'morphMany',
391
                                    'morphToMany',
392
                                    'morphedByMany'
393
                                ];
394
                                if (in_array($relation, $relations)) {
395
                                    //Collection or array of models (because Collection is Arrayable)
396
                                    $relationships["HasMany"][$method] = $biDir ? $relationObj : $relatedModel;
397
                                } elseif ($relation === "morphTo") {
398
                                    // Model isn't specified because relation is polymorphic
399
                                    $relationships["UnknownPolyMorphSide"][$method] =
400
                                        $biDir ? $relationObj : '\Illuminate\Database\Eloquent\Model|\Eloquent';
401
                                } else {
402
                                    //Single model is returned
403
                                    $relationships["HasOne"][$method] = $biDir ? $relationObj : $relatedModel;
404
                                }
405
                                if (in_array($relation, ["morphMany", "morphOne", "morphedByMany"])) {
406
                                    $relationships["KnownPolyMorphSide"][$method] =
407
                                        $biDir ? $relationObj : $relatedModel;
408
                                }
409
                                if (in_array($relation, ["morphToMany"])) {
410
                                    $relationships["UnknownPolyMorphSide"][$method] =
411
                                        $biDir ? $relationObj : $relatedModel;
412
                                }
413
                            }
414
                        }
415
                    }
416
                }
417
            }
418
        }
419
        return $relationships;
420
    }
421
422
    /**
423
     * Get the visible attributes for the model.
424
     *
425
     * @return array
426
     */
427
    public abstract function getVisible();
428
429
    /**
430
     * Get the hidden attributes for the model.
431
     *
432
     * @return array
433
     */
434
    public abstract function getHidden();
435
436
    /**
437
     * Get the primary key for the model.
438
     *
439
     * @return string
440
     */
441
    public abstract function getKeyName();
442
443
    /**
444
     * Get the current connection name for the model.
445
     *
446
     * @return string
447
     */
448
    public abstract function getConnectionName();
449
450
    /**
451
     * Get the database connection for the model.
452
     *
453
     * @return \Illuminate\Database\Connection
454
     */
455
    public abstract function getConnection();
456
457
    /**
458
     * Get all of the current attributes on the model.
459
     *
460
     * @return array
461
     */
462
    public abstract function getAttributes();
463
464
    /**
465
     * Get the table associated with the model.
466
     *
467
     * @return string
468
     */
469
    public abstract function getTable();
470
471
    /**
472
     * Get the fillable attributes for the model.
473
     *
474
     * @return array
475
     */
476
    public abstract function getFillable();
477
478
    /**
479
     * Dig up all defined getters on the model
480
     *
481
     * @return array
482
     */
483
    protected function collectGetters()
484
    {
485
        $getterz = [];
486
        $methods = get_class_methods($this);
487
        foreach ($methods as $method) {
488
            if (starts_with($method, 'get') && ends_with($method, 'Attribute') && 'getAttribute' != $method) {
489
                $getterz[] = $method;
490
            }
491
        }
492
        $methods = [];
493
494
        foreach ($getterz as $getter) {
495
            $residual = substr($getter, 3);
496
            $residual = substr($residual, 0, -9);
497
            $methods[] = $residual;
498
        }
499
        return $methods;
500
    }
501
}
502