Passed
Pull Request — master (#182)
by Alex
06:05
created

LaravelHookQuery::getMetadataProvider()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 3
rs 10
c 1
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
1
<?php
2
3
namespace AlgoWeb\PODataLaravel\Query;
4
5
use AlgoWeb\PODataLaravel\Auth\NullAuthProvider;
6
use AlgoWeb\PODataLaravel\Interfaces\AuthInterface;
7
use AlgoWeb\PODataLaravel\Providers\MetadataProvider;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Database\Eloquent\Relations\BelongsTo;
10
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
11
use Illuminate\Database\Eloquent\Relations\HasOneOrMany;
12
use Illuminate\Database\Eloquent\Relations\Relation;
13
use Illuminate\Support\Facades\App;
14
use POData\Common\InvalidOperationException;
15
use POData\Providers\Metadata\ResourceSet;
16
17
class LaravelHookQuery extends LaravelBaseQuery
18
{
19
    /**
20
     * Attaches child model to parent model.
21
     *
22
     * @param ResourceSet $sourceResourceSet
23
     * @param Model       $sourceEntityInstance
24
     * @param ResourceSet $targetResourceSet
25
     * @param Model       $targetEntityInstance
26
     * @param $navPropName
27
     *
28
     * @return bool
29
     *
30
     * @throws InvalidOperationException
31
     */
32
    public function hookSingleModel(
33
        ResourceSet $sourceResourceSet,
34
        $sourceEntityInstance,
35
        ResourceSet $targetResourceSet,
36
        $targetEntityInstance,
37
        $navPropName
38
    ) {
39
        $relation = $this->isModelHookInputsOk($sourceEntityInstance, $targetEntityInstance, $navPropName);
40
41
        // in case the fake 'PrimaryKey' attribute got set inbound for a polymorphic-affected model, flatten it now
42
        unset($targetEntityInstance->PrimaryKey);
43
44
        if ($relation instanceof BelongsTo) {
45
            $relation->associate($targetEntityInstance);
46
        } elseif ($relation instanceof BelongsToMany) {
47
            $relation->attach($targetEntityInstance);
48
        } elseif ($relation instanceof HasOneOrMany) {
49
            $relation->save($targetEntityInstance);
50
        }
51
        LaravelQuery::queueModel($sourceEntityInstance);
52
        LaravelQuery::queueModel($targetEntityInstance);
53
        return true;
54
    }
55
56
    /**
57
     * Removes child model from parent model.
58
     *
59
     * @param ResourceSet $sourceResourceSet
60
     * @param Model       $sourceEntityInstance
61
     * @param ResourceSet $targetResourceSet
62
     * @param Model       $targetEntityInstance
63
     * @param $navPropName
64
     *
65
     * @return bool
66
     * @throws InvalidOperationException
67
     */
68
    public function unhookSingleModel(
69
        ResourceSet $sourceResourceSet,
70
        $sourceEntityInstance,
71
        ResourceSet $targetResourceSet,
72
        $targetEntityInstance,
73
        $navPropName
74
    ) {
75
        $relation = $this->isModelHookInputsOk($sourceEntityInstance, $targetEntityInstance, $navPropName);
76
77
        // in case the fake 'PrimaryKey' attribute got set inbound for a polymorphic-affected model, flatten it now
78
        unset($targetEntityInstance->PrimaryKey);
79
80
        $changed = false;
81
82
        if ($relation instanceof BelongsTo) {
83
            $relation->dissociate();
84
            $changed = true;
85
        } elseif ($relation instanceof BelongsToMany) {
86
            $relation->detach($targetEntityInstance);
87
            $changed = true;
88
        } elseif ($relation instanceof HasOneOrMany) {
89
            // dig up inverse property name, so we can pass it to unhookSingleModel with source and target elements
90
            // swapped
91
            $otherPropName = $this->getMetadataProvider()
92
                ->resolveReverseProperty($sourceEntityInstance, $navPropName);
93
            if (null === $otherPropName) {
94
                $srcClass = get_class($sourceEntityInstance);
95
                $msg = 'Bad navigation property, ' . $navPropName . ', on source model ' . $srcClass;
96
                throw new \InvalidArgumentException($msg);
97
            }
98
            $this->unhookSingleModel(
99
                $targetResourceSet,
100
                $targetEntityInstance,
101
                $sourceResourceSet,
102
                $sourceEntityInstance,
103
                $otherPropName
104
            );
105
        }
106
        if ($changed) {
107
            LaravelQuery::queueModel($sourceEntityInstance);
108
            LaravelQuery::queueModel($targetEntityInstance);
109
        }
110
        return true;
111
    }
112
113
    /**
114
     * @param $sourceEntityInstance
115
     * @param $targetEntityInstance
116
     * @param $navPropName
117
     * @throws \InvalidArgumentException
118
     * @throws InvalidOperationException
119
     * @return Relation
120
     */
121
    protected function isModelHookInputsOk($sourceEntityInstance, $targetEntityInstance, $navPropName)
122
    {
123
        if (!$sourceEntityInstance instanceof Model || !$targetEntityInstance instanceof Model) {
124
            $msg = 'Both input entities must be Eloquent models';
125
            throw new InvalidOperationException($msg);
126
        }
127
        $relation = $sourceEntityInstance->$navPropName();
128
        if (!$relation instanceof Relation) {
129
            $msg = 'Navigation property must be an Eloquent relation';
130
            throw new \InvalidArgumentException($msg);
131
        }
132
        $targType = $relation->getRelated();
133
        if (!$targetEntityInstance instanceof $targType) {
134
            $msg = 'Target instance must be of type compatible with relation declared in method ' . $navPropName;
135
            throw new \InvalidArgumentException($msg);
136
        }
137
        return $relation;
138
    }
139
}
140