Passed
Pull Request — master (#221)
by Christopher
10:14 queued 05:03
created

MetadataRelationsTrait   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 95
Duplicated Lines 0 %

Importance

Changes 10
Bugs 0 Features 0
Metric Value
wmc 12
eloc 39
c 10
b 0
f 0
dl 0
loc 95
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A getRelationships() 0 3 1
A getRelationshipsFromMethods() 0 28 6
A getCodeForMethod() 0 25 4
A getModelClassMethods() 0 8 1
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: alex
5
 * Date: 13/02/20
6
 * Time: 1:08 PM.
7
 */
8
namespace AlgoWeb\PODataLaravel\Models;
9
10
use Illuminate\Database\Eloquent\Model;
11
use Illuminate\Database\Eloquent\Relations\BelongsTo;
12
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
13
use Illuminate\Database\Eloquent\Relations\MorphMany;
14
use Illuminate\Database\Eloquent\Relations\MorphOne;
15
use Illuminate\Database\Eloquent\Relations\MorphToMany;
16
use Illuminate\Database\Eloquent\Relations\Relation;
17
use Mockery\Mock;
18
use POData\Common\InvalidOperationException;
19
20
trait MetadataRelationsTrait
21
{
22
23
    /**
24
     * Get model's relationships.
25
     *
26
     * @throws InvalidOperationException
27
     * @throws \ReflectionException
28
     * @return array
29
     */
30
    public function getRelationships()
31
    {
32
        return  $this->getRelationshipsFromMethods();
33
    }
34
35
    /**
36
     * @param  \ReflectionMethod         $method
37
     * @throws InvalidOperationException
38
     * @return string
39
     */
40
    protected function getCodeForMethod(\ReflectionMethod $method) : string
41
    {
42
        $fileName = $method->getFileName();
43
44
        $file = new \SplFileObject($fileName);
45
        $file->seek($method->getStartLine() - 1);
46
        $code = '';
47
        while ($file->key() < $method->getEndLine()) {
48
            $code .= $file->current();
49
            $file->next();
50
        }
51
52
        $code = trim(preg_replace('/\s\s+/', '', $code));
53
        if (false === stripos($code, 'function')) {
54
            $msg = 'Function definition must have keyword \'function\'';
55
            throw new InvalidOperationException($msg);
56
        }
57
        $begin = strpos($code, 'function(');
58
        $code = substr($code, $begin, strrpos($code, '}') - $begin + 1);
59
        $lastCode = $code[strlen($code) - 1];
60
        if ('}' != $lastCode) {
61
            $msg = 'Final character of function definition must be closing brace';
62
            throw new InvalidOperationException($msg);
63
        }
64
        return $code;
65
    }
66
    /**
67
     * @param  Model $model
68
     * @return array
69
     */
70
    protected function getModelClassMethods(Model $model)
71
    {
72
        // TODO: Handle case when Mock::class not present
73
        return array_diff(
74
            get_class_methods($model),
75
            get_class_methods(\Illuminate\Database\Eloquent\Model::class),
76
            get_class_methods(Mock::class),
77
            get_class_methods(MetadataTrait::class)
78
        );
79
    }
80
    /**
81
     * @param bool $biDir
82
     *
83
     * @throws InvalidOperationException
84
     * @throws \ReflectionException
85
     * @return array
86
     */
87
    protected function getRelationshipsFromMethods()
88
    {
89
        /*$biDirVal = true;
90
        $isCached = isset(static::$relationCategories[$biDirVal]) && !empty(static::$relationCategories[$biDirVal]);
91
        if ($isCached) {
92
            return static::$relationCategories[$biDirVal];
93
        }*/
94
        $relationships = [];
95
        /** @var Model $model */
96
        $model = $this;
97
        $methods = $this->getModelClassMethods($model);
98
        foreach ($methods as $method) {
99
            //Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
100
            $reflection = new \ReflectionMethod($model, $method);
101
            $code = $this->getCodeForMethod($reflection);
102
            foreach (static::$relTypes as $relation) {
103
                //Resolve the relation's model to a Relation object.
104
                if (
105
                    !stripos($code, sprintf('$this->%s(', $relation)) ||
106
                    !(($relationObj = $model->$method()) instanceof Relation) ||
107
                    !in_array(MetadataTrait::class, class_uses($relationObj->getRelated()))
108
                ) {
109
                    continue;
110
                }
111
                $relationships[]= $method;
112
            }
113
        }
114
        return $relationships;
115
    }
116
}
117