Completed
Push — master ( c2ab20...6fd91f )
by Alex
14s queued 12s
created

ModelReflectionHelper   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 78
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 9
eloc 41
c 1
b 0
f 0
dl 0
loc 78
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A getCodeForMethod() 0 16 2
A getModelClassMethods() 0 8 1
A getRelationshipsFromMethods() 0 21 6
1
<?php
2
3
4
namespace AlgoWeb\PODataLaravel\Models;
5
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Database\Eloquent\Relations\Relation;
8
use Mockery\Mock;
9
use POData\Common\InvalidOperationException;
10
use ReflectionException;
11
use ReflectionMethod;
12
use SplFileObject;
13
14
abstract class ModelReflectionHelper
15
{
16
    protected static $relTypes = [
17
        'hasMany',
18
        'hasManyThrough',
19
        'belongsToMany',
20
        'hasOne',
21
        'belongsTo',
22
        'morphOne',
23
        'morphTo',
24
        'morphMany',
25
        'morphToMany',
26
        'morphedByMany'
27
    ];
28
29
    /**
30
     * @param  ReflectionMethod         $method
31
     * @return string
32
     */
33
    public static function getCodeForMethod(ReflectionMethod $method): string
34
    {
35
        $fileName = $method->getFileName();
36
37
        $file = new SplFileObject($fileName);
38
        $file->seek($method->getStartLine() - 1);
39
        $code = '';
40
        while ($file->key() < $method->getEndLine()) {
41
            $code .= $file->current();
42
            $file->next();
43
        }
44
45
        $code = trim(preg_replace('/\s\s+/', '', $code));
46
        $begin = strpos($code, 'function(');
47
        $code = substr($code, $begin, strrpos($code, '}') - $begin + 1);
48
        return $code;
49
    }
50
51
    /**
52
     * @param  Model $model
53
     * @return array
54
     */
55
    public static function getModelClassMethods(Model $model): array
56
    {
57
        // TODO: Handle case when Mock::class not present
58
        return array_diff(
59
            get_class_methods($model),
60
            get_class_methods(Model::class),
61
            get_class_methods(Mock::class),
62
            get_class_methods(MetadataTrait::class)
63
        );
64
    }
65
66
    /**
67
     * @param Model $model
68
     * @return array|string[]
69
     * @throws ReflectionException
70
     */
71
    public static function getRelationshipsFromMethods(Model $model): array
72
    {
73
        $relationships = [];
74
        $methods = self::getModelClassMethods($model);
75
        foreach ($methods as $method) {
76
            //Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
77
            $reflection = new ReflectionMethod($model, $method);
78
            $code = self::getCodeForMethod($reflection);
79
            foreach (static::$relTypes as $relation) {
80
                //Resolve the relation's model to a Relation object.
81
                if (
82
                    !stripos($code, sprintf('$this->%s(', $relation)) ||
83
                    !(($relationObj = $model->$method()) instanceof Relation) ||
84
                    !in_array(MetadataTrait::class, class_uses($relationObj->getRelated()))
85
                ) {
86
                    continue;
87
                }
88
                $relationships[]= $method;
89
            }
90
        }
91
        return $relationships;
92
    }
93
}
94