Completed
Pull Request — master (#5)
by Alex
22:17
created

MetadataTrait::getFillable()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 1
ccs 0
cts 0
cp 0
nc 1
1
<?php
2
namespace AlgoWeb\PODataLaravel\Models;
3
4
use Illuminate\Support\Facades\Schema as Schema;
5
use Illuminate\Support\Facades\App as App;
6
use Illuminate\Database\Eloquent\Relations\Relation;
7
use POData\Providers\Metadata\ResourceStreamInfo;
8
use POData\Providers\Metadata\Type\EdmPrimitiveType;
9
use Illuminate\Database\Eloquent\Model;
10
11
trait MetadataTrait
12
{
13
    /*
14
     * Array to record mapping between doctrine types and OData types
15
     */
16
    protected $mapping = [
17
        'integer' => EdmPrimitiveType::INT32,
18
        'string' => EdmPrimitiveType::STRING,
19
        'datetime' => EdmPrimitiveType::DATETIME,
20
        'decimal' => EdmPrimitiveType::DECIMAL,
21
        'text' => EdmPrimitiveType::STRING,
22
        'boolean' => EdmPrimitiveType::BOOLEAN,
23
        'blob' => "stream"
24
    ];
25
26
    /*
27
     * Retrieve and assemble this model's metadata for OData packaging
28
     */
29 3
    public function metadata()
30
    {
31 3
        assert($this instanceof Model, get_class($this));
32
33
        // Break these out separately to enable separate reuse
34 2
        $connect = $this->getConnection();
35 2
        $builder = $connect->getSchemaBuilder();
36
37 2
        $table = $this->getTable();
38
39 2
        assert(
40 2
            $builder->hasTable($table),
41 2
            $table.' table not present in current db, '.$this->getConnectionName()
42 2
        );
43
44 1
        $columns = $builder->getColumnListing($table);
45 1
        $mask = $this->metadataMask();
46 1
        $columns = array_intersect($columns, $mask);
47
48 1
        $tableData = [];
49
50 1
        $foo = $connect->getDoctrineSchemaManager()->listTableColumns($table);
51
52 1
        foreach ($columns as $column) {
53
            // Doctrine schema manager returns columns with lowercased names
54 1
            $rawColumn = $foo[strtolower($column)];
55 1
            $nullable = !($rawColumn->getNotNull());
56 1
            $fillable = in_array($column, $this->getFillable());
57 1
            $rawType = $rawColumn->getType();
58 1
            $type = $rawType->getName();
59 1
            $tableData[$column] = ['type' => $type, 'nullable' => $nullable, 'fillable' => $fillable];
60 1
        }
61
62 1
        return $tableData;
63
    }
64
65
    /*
66
     * Return the set of fields that are permitted to be in metadata
67
     * - following same visible-trumps-hidden guideline as Laravel
68
     */
69 4
    public function metadataMask()
70
    {
71 4
        $attribs = array_keys($this->getAllAttributes());
72
73 4
        $visible = $this->getVisible();
74 4
        $hidden = $this->getHidden();
75 4
        if (0 != count($visible)) {
76 2
            $attribs = array_intersect($visible, $attribs);
77 4
        } elseif (0 != count($hidden)) {
78 1
            $attribs = array_diff($attribs, $hidden);
79 1
        }
80
81 4
        return $attribs;
82
    }
83
84
    /*
85
     * Assemble this model's OData metadata as xml schema
86
     */
87 1
    public function getXmlSchema($MetaNamespace = "Data")
88
    {
89 1
        $raw = $this->metadata();
90
91 1
        $metadata = App::make('metadata');
92
93 1
        $table = $this->getTable();
94
95 1
        $complex = $metadata->addEntityType(new \ReflectionClass(get_class($this)), $table, $MetaNamespace);
96 1
        $keyName = $this->getKeyName();
97 1
        $metadata->addKeyProperty($complex, $keyName, $this->mapping[$raw[$keyName]['type']]);
98 1
        foreach ($raw as $key => $secret) {
99 1
            if ($key == $keyName) {
100 1
                continue;
101
            }
102 1
            if ($secret['type'] == "blob") {
103 1
                $complex->setMediaLinkEntry(true);
104 1
                $streamInfo = new ResourceStreamInfo($key);
105 1
                $complex->addNamedStream($streamInfo);
106 1
                continue;
107
            }
108 1
            $metadata->addPrimitiveProperty($complex, $key, $this->mapping[$secret['type']]); // tag as isBag?
109 1
        }
110
111 1
        return $complex;
112
    }
113
114
    public function hookUpRelationships($entityTypes, $resourceSets)
115
    {
116
        $metadata = \App::make('metadata');
117
        $rel = $this->getRelationshipsFromMethods();
118
        $thisClass = get_class($this);
119 View Code Duplication
        foreach ($rel["HasOne"] as $n => $r) {
120
            if ($r[0] == "\\") {
121
                $r = substr($r, 1);
122
            }
123
            if (array_key_exists($r, $entityTypes)
124
                && array_key_exists($r, $resourceSets)
125
                && array_key_exists($thisClass, $entityTypes)
126
                && array_key_exists($thisClass, $resourceSets)) {
127
                $resourceType = $entityTypes[$thisClass];
128
                $targResourceSet = $resourceSets[$r];
129
                $metadata->addResourceReferenceProperty($resourceType, $n, $targResourceSet);
130
            }
131
        }
132 View Code Duplication
        foreach ($rel["HasMany"] as $n => $r) {
133
            if ($r[0] == "\\") {
134
                $r = substr($r, 1);
135
            }
136
            if (array_key_exists($r, $entityTypes)
137
                 && array_key_exists($r, $resourceSets)
138
                 && array_key_exists($thisClass, $entityTypes)
139
                 && array_key_exists($thisClass, $resourceSets)) {
140
                $resourceType = $entityTypes[$thisClass];
141
                $targResourceSet = $resourceSets[$r];
142
                $metadata->addResourceSetReferenceProperty($resourceType, $n, $targResourceSet);
143
            }
144
        }
145
146
        return $rel;
147
    }
148
149
    protected function getAllAttributes()
150
    {
151
        // Adapted from http://stackoverflow.com/a/33514981
152
        // $columns = $this->getFillable();
153
        // Another option is to get all columns for the table like so:
154
        $connName = $this->getConnectionName();
155
        $columns = Schema::connection($connName)->getColumnListing($this->table);
0 ignored issues
show
Bug introduced by
The property table does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
156
        // but it's safer to just get the fillable fields
157
158
        $attributes = $this->getAttributes();
159
160
        foreach ($columns as $column) {
161
            if (!array_key_exists($column, $attributes)) {
162
                $attributes[$column] = null;
163
            }
164
        }
165
        return $attributes;
166
    }
167
168
    protected function getRelationshipsFromMethods()
169
    {
170
        $model = $this;
171
        $relationships = array(
172
            "HasOne" => array(),
173
            "UnknownPolymorphSide"=>array(),
174
            "HasMany"=>array(),
175
            "KnownPolyMorphSide"=>array()
176
        );
177
        $methods = get_class_methods($model);
178
        if (!empty($methods)) {
179
            foreach ($methods as $method) {
180
                if (!method_exists('Illuminate\Database\Eloquent\Model', $method)
181
                ) {
182
                    //Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
183
                    $reflection = new \ReflectionMethod($model, $method);
184
185
                    $file = new \SplFileObject($reflection->getFileName());
186
                    $file->seek($reflection->getStartLine()-1);
187
                    $code = '';
188
                    while ($file->key() < $reflection->getEndLine()) {
189
                        $code .= $file->current();
190
                        $file->next();
191
                    }
192
                    $code = trim(preg_replace('/\s\s+/', '', $code));
193
                    $begin = strpos($code, 'function(');
194
                    $code = substr($code, $begin, strrpos($code, '}')-$begin+1);
195
                    foreach (array(
196
                                'hasMany',
197
                                'hasManyThrough',
198
                                'belongsToMany',
199
                                'hasOne',
200
                                'belongsTo',
201
                                'morphOne',
202
                                'morphTo',
203
                                'morphMany',
204
                                'morphToMany'
205
                                ) as $relation) {
206
                        $search = '$this->'.$relation.'(';
207
                        if ($pos = stripos($code, $search)) {
208
                            //Resolve the relation's model to a Relation object.
209
                            $relationObj = $model->$method();
210
                            if ($relationObj instanceof Relation) {
211
                                $relatedModel = '\\'.get_class($relationObj->getRelated());
212
                                $relations = ['hasManyThrough', 'belongsToMany', 'hasMany', 'morphMany', 'morphToMany'];
213
                                if (in_array($relation, $relations)) {
214
                                    //Collection or array of models (because Collection is Arrayable)
215
                                    $relationships["HasMany"][$method] = $relatedModel;
216
                                } elseif ($relation === "morphTo") {
217
                                    // Model isn't specified because relation is polymorphic
218
                                    $relationships["UnknownPolymorphSide"][$method] =
219
                                        '\Illuminate\Database\Eloquent\Model|\Eloquent';
220
                                } else {
221
                                    //Single model is returned
222
                                    $relationships["HasOne"][$method] = $relatedModel;
223
                                }
224
                                if (in_array($relation, ["morphMany", "morphOne"])) {
225
                                    $relationships["KnownPolyMorphSide"][$method] = $relatedModel;
226
                                }
227
                            }
228
                        }
229
                    }
230
                }
231
            }
232
        }
233
        return $relationships;
234
    }
235
236
    /**
237
     * Get the visible attributes for the model.
238
     *
239
     * @return array
240
     */
241
    public abstract function getVisible();
242
243
    /**
244
     * Get the hidden attributes for the model.
245
     *
246
     * @return array
247
     */
248
    public abstract function getHidden();
249
250
    /**
251
     * Get the primary key for the model.
252
     *
253
     * @return string
254
     */
255
    public abstract function getKeyName();
256
257
    /**
258
     * Get the current connection name for the model.
259
     *
260
     * @return string
261
     */
262
    public abstract function getConnectionName();
263
264
    /**
265
     * Get the database connection for the model.
266
     *
267
     * @return \Illuminate\Database\Connection
268
     */
269
    public abstract function getConnection();
270
271
    /**
272
     * Get all of the current attributes on the model.
273
     *
274
     * @return array
275
     */
276
    public abstract function getAttributes();
277
278
    /**
279
     * Get the table associated with the model.
280
     *
281
     * @return string
282
     */
283
    public abstract function getTable();
284
285
    /**
286
     * Get the fillable attributes for the model.
287
     *
288
     * @return array
289
     */
290
    public abstract function getFillable();
291
}
292