Completed
Pull Request — master (#16)
by Alex
02:59
created

MetadataTrait::hookUpRelationships()   C

Complexity

Conditions 13
Paths 25

Size

Total Lines 34
Code Lines 25

Duplication

Lines 26
Ratio 76.47 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 26
loc 34
ccs 0
cts 31
cp 0
rs 5.1234
cc 13
eloc 25
nc 25
nop 2
crap 182

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace AlgoWeb\PODataLaravel\Models;
3
4
use Illuminate\Support\Facades\App as App;
5
use Illuminate\Database\Eloquent\Relations\Relation;
6
use POData\Providers\Metadata\ResourceStreamInfo;
7
use POData\Providers\Metadata\Type\EdmPrimitiveType;
8
use Illuminate\Database\Eloquent\Model;
9
10
trait MetadataTrait
11
{
12
    /*
13
     * Array to record mapping between doctrine types and OData types
14
     */
15
    protected $mapping = [
16
        'integer' => EdmPrimitiveType::INT32,
17
        'string' => EdmPrimitiveType::STRING,
18
        'datetime' => EdmPrimitiveType::DATETIME,
19
        'decimal' => EdmPrimitiveType::DECIMAL,
20
        'text' => EdmPrimitiveType::STRING,
21
        'boolean' => EdmPrimitiveType::BOOLEAN,
22
        'blob' => "stream"
23
    ];
24
25
    /*
26
     * Retrieve and assemble this model's metadata for OData packaging
27
     */
28 3
    public function metadata()
29
    {
30 3
        assert($this instanceof Model, get_class($this));
31
32
        // Break these out separately to enable separate reuse
33 2
        $connect = $this->getConnection();
34 2
        $builder = $connect->getSchemaBuilder();
35
36 2
        $table = $this->getTable();
37
38 2
        if (!$builder->hasTable($table)) {
39 1
            return [];
40
        }
41
42 1
        $columns = $builder->getColumnListing($table);
43 1
        $mask = $this->metadataMask();
44 1
        $columns = array_intersect($columns, $mask);
45
46 1
        $tableData = [];
47
48 1
        $foo = $connect->getDoctrineSchemaManager()->listTableColumns($table);
49
50 1
        foreach ($columns as $column) {
51
            // Doctrine schema manager returns columns with lowercased names
52 1
            $rawColumn = $foo[strtolower($column)];
53 1
            $nullable = !($rawColumn->getNotNull());
54 1
            $fillable = in_array($column, $this->getFillable());
55 1
            $rawType = $rawColumn->getType();
56 1
            $type = $rawType->getName();
57 1
            $tableData[$column] = ['type' => $type, 'nullable' => $nullable, 'fillable' => $fillable];
58 1
        }
59
60 1
        return $tableData;
61
    }
62
63
    /*
64
     * Return the set of fields that are permitted to be in metadata
65
     * - following same visible-trumps-hidden guideline as Laravel
66
     */
67 4
    public function metadataMask()
68
    {
69 4
        $attribs = array_keys($this->getAllAttributes());
70
71 4
        $visible = $this->getVisible();
72 4
        $hidden = $this->getHidden();
73 4
        if (0 != count($visible)) {
74 2
            $attribs = array_intersect($visible, $attribs);
75 4
        } elseif (0 != count($hidden)) {
76 1
            $attribs = array_diff($attribs, $hidden);
77 1
        }
78
79 4
        return $attribs;
80
    }
81
82
    /*
83
     * Assemble this model's OData metadata as xml schema
84
     */
85 5
    public function getXmlSchema($MetaNamespace = "Data")
86
    {
87 5
        $raw = $this->metadata();
88 5
        if ([] == $raw) {
89 1
            return null;
90
        }
91
92 4
        $metadata = App::make('metadata');
93
94 4
        $table = $this->getTable();
95
96 4
        $complex = $metadata->addEntityType(new \ReflectionClass(get_class($this)), $table, $MetaNamespace);
97 4
        $keyName = $this->getKeyName();
98 4
        $metadata->addKeyProperty($complex, $keyName, $this->mapping[$raw[$keyName]['type']]);
99 4
        foreach ($raw as $key => $secret) {
100 4
            if ($key == $keyName) {
101 4
                continue;
102
            }
103 4
            if ($secret['type'] == "blob") {
104 1
                $complex->setMediaLinkEntry(true);
105 1
                $streamInfo = new ResourceStreamInfo($key);
106 1
                $complex->addNamedStream($streamInfo);
107 1
                continue;
108
            }
109 4
            $metadata->addPrimitiveProperty($complex, $key, $this->mapping[$secret['type']]); // tag as isBag?
110 4
        }
111
112 4
        return $complex;
113
    }
114
115
    public function hookUpRelationships($entityTypes, $resourceSets)
116
    {
117
        $metadata = \App::make('metadata');
118
        $rel = $this->getRelationshipsFromMethods();
119
        $thisClass = get_class($this);
120 View Code Duplication
        foreach ($rel["HasOne"] as $n => $r) {
121
            if ($r[0] == "\\") {
122
                $r = substr($r, 1);
123
            }
124
            if (array_key_exists($r, $entityTypes)
125
                && array_key_exists($r, $resourceSets)
126
                && array_key_exists($thisClass, $entityTypes)
127
                && array_key_exists($thisClass, $resourceSets)) {
128
                $resourceType = $entityTypes[$thisClass];
129
                $targResourceSet = $resourceSets[$r];
130
                $metadata->addResourceReferenceProperty($resourceType, $n, $targResourceSet);
131
            }
132
        }
133 View Code Duplication
        foreach ($rel["HasMany"] as $n => $r) {
134
            if ($r[0] == "\\") {
135
                $r = substr($r, 1);
136
            }
137
            if (array_key_exists($r, $entityTypes)
138
                 && array_key_exists($r, $resourceSets)
139
                 && array_key_exists($thisClass, $entityTypes)
140
                 && array_key_exists($thisClass, $resourceSets)) {
141
                $resourceType = $entityTypes[$thisClass];
142
                $targResourceSet = $resourceSets[$r];
143
                $metadata->addResourceSetReferenceProperty($resourceType, $n, $targResourceSet);
144
            }
145
        }
146
147
        return $rel;
148
    }
149
150
    protected function getAllAttributes()
151
    {
152
        // Adapted from http://stackoverflow.com/a/33514981
153
        // $columns = $this->getFillable();
154
        // Another option is to get all columns for the table like so:
155
        $builder = $this->getConnection()->getSchemaBuilder();
156
        $columns = $builder->getColumnListing($this->getTable());
157
        // but it's safer to just get the fillable fields
158
159
        $attributes = $this->getAttributes();
160
161
        foreach ($columns as $column) {
162
            if (!array_key_exists($column, $attributes)) {
163
                $attributes[$column] = null;
164
            }
165
        }
166
        return $attributes;
167
    }
168
169 3
    protected function getRelationshipsFromMethods()
170
    {
171 3
        $model = $this;
172
        $relationships = array(
173 3
            "HasOne" => array(),
174 3
            "UnknownPolyMorphSide"=>array(),
175 3
            "HasMany"=>array(),
176 3
            "KnownPolyMorphSide"=>array()
177 3
        );
178 3
        $methods = get_class_methods($model);
179 3
        if (!empty($methods)) {
180 3
            foreach ($methods as $method) {
181 3
                if (!method_exists('Illuminate\Database\Eloquent\Model', $method)
182 3
                ) {
183
                    //Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
184 3
                    $reflection = new \ReflectionMethod($model, $method);
185
186 3
                    $file = new \SplFileObject($reflection->getFileName());
187 3
                    $file->seek($reflection->getStartLine()-1);
188 3
                    $code = '';
189 3
                    while ($file->key() < $reflection->getEndLine()) {
190 3
                        $code .= $file->current();
191 3
                        $file->next();
192 3
                    }
193 3
                    $code = trim(preg_replace('/\s\s+/', '', $code));
194 3
                    $begin = strpos($code, 'function(');
195 3
                    $code = substr($code, $begin, strrpos($code, '}')-$begin+1);
196
                    foreach (array(
197 3
                                'hasMany',
198 3
                                'hasManyThrough',
199 3
                                'belongsToMany',
200 3
                                'hasOne',
201 3
                                'belongsTo',
202 3
                                'morphOne',
203 3
                                'morphTo',
204 3
                                'morphMany',
205
                                'morphToMany'
206 3
                                ) as $relation) {
207 3
                        $search = '$this->'.$relation.'(';
208 3
                        if ($pos = stripos($code, $search)) {
209
                            //Resolve the relation's model to a Relation object.
210 3
                            $relationObj = $model->$method();
211 3
                            if ($relationObj instanceof Relation) {
212 3
                                $relatedModel = '\\'.get_class($relationObj->getRelated());
213 3
                                $relations = ['hasManyThrough', 'belongsToMany', 'hasMany', 'morphMany', 'morphToMany'];
214 3
                                if (in_array($relation, $relations)) {
215
                                    //Collection or array of models (because Collection is Arrayable)
216 1
                                    $relationships["HasMany"][$method] = $relatedModel;
217 3
                                } elseif ($relation === "morphTo") {
218
                                    // Model isn't specified because relation is polymorphic
219 1
                                    $relationships["UnknownPolyMorphSide"][$method] =
220
                                        '\Illuminate\Database\Eloquent\Model|\Eloquent';
221 1
                                } else {
222
                                    //Single model is returned
223 1
                                    $relationships["HasOne"][$method] = $relatedModel;
224
                                }
225 3
                                if (in_array($relation, ["morphMany", "morphOne"])) {
226 2
                                    $relationships["KnownPolyMorphSide"][$method] = $relatedModel;
227 2
                                }
228 3
                            }
229 3
                        }
230 3
                    }
231 3
                }
232 3
            }
233 3
        }
234 3
        return $relationships;
235
    }
236
237
    /**
238
     * Get the visible attributes for the model.
239
     *
240
     * @return array
241
     */
242
    public abstract function getVisible();
243
244
    /**
245
     * Get the hidden attributes for the model.
246
     *
247
     * @return array
248
     */
249
    public abstract function getHidden();
250
251
    /**
252
     * Get the primary key for the model.
253
     *
254
     * @return string
255
     */
256
    public abstract function getKeyName();
257
258
    /**
259
     * Get the current connection name for the model.
260
     *
261
     * @return string
262
     */
263
    public abstract function getConnectionName();
264
265
    /**
266
     * Get the database connection for the model.
267
     *
268
     * @return \Illuminate\Database\Connection
269
     */
270
    public abstract function getConnection();
271
272
    /**
273
     * Get all of the current attributes on the model.
274
     *
275
     * @return array
276
     */
277
    public abstract function getAttributes();
278
279
    /**
280
     * Get the table associated with the model.
281
     *
282
     * @return string
283
     */
284
    public abstract function getTable();
285
286
    /**
287
     * Get the fillable attributes for the model.
288
     *
289
     * @return array
290
     */
291
    public abstract function getFillable();
292
}
293