-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathRelatingModelTrait.php
303 lines (265 loc) · 9.13 KB
/
RelatingModelTrait.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
<?php
namespace Esensi\Model\Traits;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* Trait that implements the Relating Model Interface.
*
*/
trait RelatingModelTrait
{
/**
* Dynamically call methods.
*
* @param string $method
* @param array $parameters
*
* @return mixed
*/
public function __call($method, $parameters)
{
// Resolve relationship dynamically
if ($relationship = $this->callDynamicRelationship($method)) {
return $relationship;
}
// Default Eloquent dynamic caller
return parent::__call($method, $parameters);
}
/**
* Dynamically retrieve attributes.
*
* @param string $key
* @return mixed
*/
public function __get($key)
{
// Resolve relationship dynamically
if ($relationship = $this->getDynamicRelationship($key)) {
return $relationship;
}
// Default Eloquent dynamic getter
return parent::__get($key);
}
/**
* Call a dynamically resolved relationship.
*
* @param string $name
* @return mixed
*/
protected function callDynamicRelationship($name)
{
// Dynamically call the relationship
if ($this->isRelationship($name)) {
return $this->callRelationship($name);
}
}
/**
* Get a dynamically resolved relationship.
*
* @param string $name
* @return mixed
*/
protected function getDynamicRelationship($name)
{
// Dynamically get the relationship
if ($this->isRelationship($name)) {
// Use the relationship already loaded
if (array_key_exists($name, $this->getRelations())) {
return $this->getRelation($name);
}
// Load the relationship
return $this->getRelationshipFromMethod($name, camel_case($name));
}
}
/**
* Get the relationships.
*
* @return array
*/
public function getRelationships()
{
return $this->relationships ?: [];
}
/**
* Return the relationship configurations.
*
* @param string $name of related model
*
* @throws Illuminate\Database\Eloquent\ModelNotFoundException
*
* @return array
*/
public function getRelationship($name)
{
// If relationship does not exist throw an exception
if (! $this->isRelationship($name)) {
$exception = new ModelNotFoundException();
$exception->setModel($name);
throw $exception;
}
return $this->relationships[$name];
}
/**
* Return the relationship configurations.
*
* @param string $name of related model
* @return array
*/
public function getPivotAttributes($name)
{
return $this->relationshipPivots[$name] ?: [];
}
/**
* Return whether the name is a relationship or not.
*
* @param string $name of related model
* @return bool
*/
public function isRelationship($name)
{
return array_key_exists($name, $this->relationships);
}
/**
* Return whether the relationshpi has pivot attributes or not.
*
* @param string $name of related model
* @return bool
*/
public function hasPivotAttributes($name)
{
return array_key_exists($name, $this->relationshipPivots ?: []);
}
/**
* Proxy call a relationship method using the
* configuration arguments of the relationship.
*
* @param string $name of related model
* @return mixed
*/
protected function callRelationship($name)
{
// Get the relationship arguments
$args = $this->getRelationship($name);
// Build the relationship
$method = array_shift($args);
$relationship = call_user_func_array([$this, $method], $args);
// Check to see if this relationship has extended pivot attributes
if ($this->hasPivotAttributes($name)) {
// Add timestamps to relationship
$attributes = $this->getPivotAttributes($name);
if (in_array('timestamps', $attributes)) {
unset($attributes[array_search('timestamps', $attributes)]);
$relationship->withTimestamps();
}
// Add the pivot attributes to the relationship
$relationship->withPivot($attributes);
}
return $relationship;
}
/**
* Define an inverse one-to-one or many relationship.
*
* @param string $related
* @param string $foreignKey
* @param string $otherKey
* @param string $relation
*
* @return Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function belongsTo($related, $foreignKey = null, $otherKey = null, $relation = null)
{
// If no relation name was given, we will use this debug backtrace to extract
// the calling method's name and use that as the relationship name as most
// of the time this will be what we desire to use for the relatinoships.
if (is_null($relation)) {
list(, $caller, $backtrace) = debug_backtrace(false);
// Use custom relationship bindings
if ($backtrace['function'] == 'callRelationship') {
$relation = $backtrace['args'][0];
}
// or default to the Eloquent bindings
else {
$relation = $caller['function'];
}
}
// If no foreign key was supplied, we can use a backtrace to guess the proper
// foreign key name by using the name of the relationship function, which
// when combined with an "_id" should conventionally match the columns.
if (is_null($foreignKey)) {
$foreignKey = snake_case($relation).'_id';
}
$instance = new $related();
// Once we have the foreign key names, we'll just create a new Eloquent query
// for the related models and returns the relationship instance which will
// actually be responsible for retrieving and hydrating every relations.
$query = $instance->newQuery();
$otherKey = $otherKey ?: $instance->getKeyName();
return new BelongsTo($query, $this, $foreignKey, $otherKey, $relation);
}
/**
* Define an polymorphic, inverse one-to-one or many relationship.
*
* @param string $name
* @param string $type
* @param string $id
* @param string $ownerKey
*
* @return Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function morphTo($name = null, $type = null, $id = null, $ownerKey = null)
{
// If no name is provided, we will use the backtrace to get the function name
// since that is most likely the name of the polymorphic interface. We can
// use that to get both the class and foreign key that will be utilized.
if (is_null($name)) {
list(, $caller, $backtrace) = debug_backtrace(false);
// Use custom relationship bindings
if ($backtrace['function'] == 'callRelationship') {
$relation = $backtrace['args'][0];
}
// or default to the Eloquent bindings
else {
$relation = $caller['function'];
}
$name = snake_case($relation);
}
list($type, $id) = $this->getMorphs($name, $type, $id);
// If the type value is null it is probably safe to assume we're eager loading
// the relationship. When that is the case we will pass in a dummy query as
// there are multiple types in the morph and we can't use single queries.
if (is_null($class = $this->$type)) {
return new MorphTo(
$this->newQuery(), $this, $id, null, $type, $name, $ownerKey
);
}
// If we are not eager loading the relationship we will essentially treat this
// as a belongs-to style relationship since morph-to extends that class and
// we will pass in the appropriate values so that it behaves as expected.
else {
if (method_exists($this, 'getActualClassNameForMorph')) {
$class = $this->getActualClassNameForMorph($class);
}
$instance = new $class();
return new MorphTo(
with($instance)->newQuery(), $this, $id, $instance->getKeyName(), $type, $name, $ownerKey
);
}
}
/**
* Set the relationships that should not be eager loaded.
*
* @param Illuminate\Database\Query\Builder $query
* @param mixed $relations
* @return $this
*/
public function scopeWithout($query, $relations)
{
$relations = is_array($relations) ? $relations : array_slice(func_get_args(), 1);
$relationships = array_dot($query->getEagerLoads());
foreach ($relations as $relation) {
unset($relationships[$relation]);
}
return $query->setEagerLoads([])->with(array_keys($relationships));
}
}