protected function parseWith($withParam)
{
$fields = $this->query->columns;
$fieldsCount = count($fields);
$baseModel = $this->builder->getModel();
$withHistory = [];
foreach (explode(',', $withParam) as $with) {
//Use ArrayObject to be able to copy the array (for array_splice)
$parts = new ArrayObject(explode('.', $with));
$lastKey = count($parts) - 1;
for ($i = 0; $i <= $lastKey; $i++) {
$part = $parts[$i];
$partsCopy = $parts->getArrayCopy();
//Get the previous history path (e.g. if current is a.b.c the previous is a.b)
$previousHistoryPath = implode('.', array_splice($partsCopy, 0, $i));
//Get the current history part based on the previous one
$currentHistoryPath = $previousHistoryPath ? $previousHistoryPath . '.' . $part : $part;
//Create new history element
if (!isset($withHistory[$currentHistoryPath])) {
$withHistory[$currentHistoryPath] = ['fields' => []];
}
//Get all given fields related to the current part
$withHistory[$currentHistoryPath]['fields'] = array_filter($this->additionalFields, function ($field) use($part) {
return preg_match('/' . $part . '\\..+$/', $field);
});
//Get all given sorts related to the current part
$withHistory[$currentHistoryPath]['sorts'] = array_filter($this->additionalSorts, function ($pair) use($part) {
return preg_match('/' . $part . '\\..+$/', $pair[0]);
});
if (!isset($previousModel)) {
$previousModel = $baseModel;
}
//Throw a new ApiHandlerException if the relation doesn't exist
//or is not properly marked as a relation
if (!$this->isRelation($previousModel, $part)) {
throw new ApiHandlerException('UnknownResourceRelation', ['relation' => $part]);
}
$relation = call_user_func([$previousModel, $part]);
$relationType = $this->getRelationType($relation);
if ($relationType === 'BelongsTo') {
$firstKey = $relation->getQualifiedForeignKey();
$secondKey = $relation->getQualifiedParentKeyName();
} else {
if ($relationType === 'HasMany' || $relationType === 'HasOne') {
$firstKey = $relation->getQualifiedParentKeyName();
$secondKey = $relation->getForeignKey();
} else {
if ($relationType === 'BelongsToMany') {
$firstKey = $relation->getQualifiedParentKeyName();
$secondKey = $relation->getRelated()->getQualifiedKeyName();
} else {
if ($relationType === 'HasManyThrough') {
$firstKey = $relation->getHasCompareKey();
$secondKey = null;
} else {
die('Relation type not supported!');
}
}
}
}
//Check if we're on level 1 (e.g. a and not a.b)
if ($firstKey !== null && $previousHistoryPath == '') {
if ($fieldsCount > 0 && !in_array($firstKey, $fields)) {
$fields[] = $firstKey;
}
} else {
if ($firstKey !== null) {
if (count($withHistory[$previousHistoryPath]['fields']) > 0 && !in_array($firstKey, $withHistory[$previousHistoryPath]['fields'])) {
$withHistory[$previousHistoryPath]['fields'][] = $firstKey;
}
}
}
if ($secondKey !== null && count($withHistory[$currentHistoryPath]['fields']) > 0 && !in_array($secondKey, $withHistory[$currentHistoryPath]['fields'])) {
$withHistory[$currentHistoryPath]['fields'][] = $secondKey;
}
$previousModel = $relation->getModel();
}
unset($previousModel);
}
//Apply the withHistory to using the laravel "with" function
$withsArr = [];
foreach ($withHistory as $withHistoryKey => $withHistoryValue) {
$withsArr[$withHistoryKey] = function ($query) use($withHistory, $withHistoryKey) {
//Reduce field values to fieldname
$fields = array_map(function ($field) {
$pos = strpos($field, '.');
return $pos !== false ? substr($field, $pos + 1) : $field;
}, $withHistory[$withHistoryKey]['fields']);
if (count($fields) > 0 && is_array($fields)) {
$query->select($fields);
}
//Attach sorts
foreach ($withHistory[$withHistoryKey]['sorts'] as $pair) {
$pos = strpos($pair[0], '.');
$pair = $pos !== false ? [substr($pair[0], $pos + 1), $pair[1]] : $pair;
call_user_func_array([$query, 'orderBy'], $pair);
}
};
}
$this->builder->with($withsArr);
//Merge the base fields
if (count($fields) > 0) {
if (!is_array($this->query->columns)) {
$this->query->columns = [];
}
$this->query->columns = array_merge($this->query->columns, $fields);
}
}