diff --git a/src/app/Library/CrudPanel/CrudPanel.php b/src/app/Library/CrudPanel/CrudPanel.php index 8d03dd7f31..b6fea84dc6 100644 --- a/src/app/Library/CrudPanel/CrudPanel.php +++ b/src/app/Library/CrudPanel/CrudPanel.php @@ -489,22 +489,4 @@ private function getRelatedEntries($model, $relationString) return $results; } - - /** - * Check if the method in the given model has any parameters. - * - * @param object $model - * @param string $method - * @return bool - */ - private function modelMethodHasParameters($model, $method) - { - $reflectClassMethod = new \ReflectionMethod(get_class($model), $method); - - if ($reflectClassMethod->getNumberOfParameters() > 0) { - return true; - } - - return false; - } } diff --git a/src/app/Library/CrudPanel/Traits/ColumnsProtectedMethods.php b/src/app/Library/CrudPanel/Traits/ColumnsProtectedMethods.php index 2fbdef763d..b9dd80a1f2 100644 --- a/src/app/Library/CrudPanel/Traits/ColumnsProtectedMethods.php +++ b/src/app/Library/CrudPanel/Traits/ColumnsProtectedMethods.php @@ -190,32 +190,33 @@ protected function makeSureColumnHasEntity($column) if (strpos($column['name'], '.') !== false) { $possibleMethodName = Str::before($column['name'], '.'); - // if the first part of the string exists as method, - // it is a relationship + // if the first part of the string exists as method in the model if (method_exists($this->model, $possibleMethodName)) { - // if it has parameters it's not a relation method. - $column['entity'] = $this->modelMethodHasParameters($this->model, $possibleMethodName) ? false : $column['name']; + // check model method for possibility of being a relationship + $column['entity'] = $this->modelMethodIsRelationship($this->model, $possibleMethodName) ? $column['name'] : false; - $parts = explode('.', $column['entity']); + if ($column['entity']) { + $parts = explode('.', $column['entity']); - $attribute_in_relation = false; + $attribute_in_relation = false; - $model = $this->model; + $model = $this->model; - // here we are going to iterate through all relation parts to check - // if the attribute is present in the relation string. - foreach ($parts as $i => $part) { - try { - $model = $model->$part()->getRelated(); - } catch (\Exception $e) { - $attribute_in_relation = true; + // here we are going to iterate through all relation parts to check + // if the attribute is present in the relation string. + foreach ($parts as $i => $part) { + try { + $model = $model->$part()->getRelated(); + } catch (\Exception $e) { + $attribute_in_relation = true; + } + } + // if the user setup the attribute in relation string, we are not going to infer that attribute from model + // instead we get the defined attribute by the user. + if ($attribute_in_relation) { + $column['attribute'] = $column['attribute'] ?? end($parts); } - } - // if the user setup the attribute in relation string, we are not going to infer that attribute from model - // instead we get the defined attribute by the user. - if ($attribute_in_relation) { - $column['attribute'] = $column['attribute'] ?? end($parts); } return $column; @@ -225,8 +226,8 @@ protected function makeSureColumnHasEntity($column) // if there's a method on the model with this name if (method_exists($this->model, $column['name'])) { - // if it has parameters it's not a relation method. - $column['entity'] = $this->modelMethodHasParameters($this->model, $column['name']) ? false : $column['name']; + // check model method for possibility of being a relationship + $column['entity'] = $this->modelMethodIsRelationship($this->model, $column['name']); return $column; } @@ -237,9 +238,8 @@ protected function makeSureColumnHasEntity($column) $possibleMethodName = Str::replaceLast('_id', '', $column['name']); if (method_exists($this->model, $possibleMethodName)) { - - // if it has parameters it's not a relation method. - $column['entity'] = $this->modelMethodHasParameters($this->model, $possibleMethodName) ? false : $possibleMethodName; + // check model method for possibility of being a relationship + $column['entity'] = $this->modelMethodIsRelationship($this->model, $possibleMethodName); return $column; } diff --git a/src/app/Library/CrudPanel/Traits/FieldsProtectedMethods.php b/src/app/Library/CrudPanel/Traits/FieldsProtectedMethods.php index 93c855f75c..7bc132d65f 100644 --- a/src/app/Library/CrudPanel/Traits/FieldsProtectedMethods.php +++ b/src/app/Library/CrudPanel/Traits/FieldsProtectedMethods.php @@ -142,16 +142,16 @@ protected function makeSureFieldHasEntity($field) //if the name is dot notation we are sure it's a relationship if (strpos($field['name'], '.') !== false) { $possibleMethodName = Str::of($field['name'])->before('.'); - // if it has parameters it's not a relation method. - $field['entity'] = $this->modelMethodHasParameters($model, $possibleMethodName) ? false : $field['name']; + // check model method for possibility of being a relationship + $field['entity'] = $this->modelMethodIsRelationship($model, $possibleMethodName) ? $field['name'] : false; return $field; } // if there's a method on the model with this name if (method_exists($model, $field['name'])) { - // if it has parameters it's not a relation method. - $field['entity'] = $this->modelMethodHasParameters($model, $field['name']) ? false : $field['name']; + // check model method for possibility of being a relationship + $field['entity'] = $this->modelMethodIsRelationship($model, $field['name']); return $field; } @@ -162,8 +162,8 @@ protected function makeSureFieldHasEntity($field) $possibleMethodName = Str::replaceLast('_id', '', $field['name']); if (method_exists($model, $possibleMethodName)) { - // if it has parameters it's not a relation method. - $field['entity'] = $this->modelMethodHasParameters($model, $possibleMethodName) ? false : $possibleMethodName; + // check model method for possibility of being a relationship + $field['entity'] = $this->modelMethodIsRelationship($model, $possibleMethodName); return $field; } diff --git a/src/app/Library/CrudPanel/Traits/Relationships.php b/src/app/Library/CrudPanel/Traits/Relationships.php index 3b9adc892f..977b99996e 100644 --- a/src/app/Library/CrudPanel/Traits/Relationships.php +++ b/src/app/Library/CrudPanel/Traits/Relationships.php @@ -309,4 +309,48 @@ private static function getPivotFieldStructure($field) return $pivotSelectorField; } + + /** + * Checks the properties of the provided method to better verify if it could be a relation. + * Case the method is not public, is not a relation. + * Case the return type is Attribute, or extends Attribute is not a relation method. + * If the return type extends the Relation class is for sure a relation + * Otherwise we just assume it's a relation. + * + * DEV NOTE: In future versions we will return `false` when no return type is set and make the return type mandatory for relationships. + * This function should be refactored to only check if $returnType is a subclass of Illuminate\Database\Eloquent\Relations\Relation. + * + * @param $model + * @param $method + * @return bool|string + */ + private function modelMethodIsRelationship($model, $method) + { + $methodReflection = new \ReflectionMethod($model, $method); + + // relationship methods function does not have parameters + if ($methodReflection->getNumberOfParameters() > 0) { + return false; + } + + // relationships are always public methods. + if (! $methodReflection->isPublic()) { + return false; + } + + $returnType = $methodReflection->getReturnType(); + + if ($returnType) { + $returnType = $returnType->getName(); + if (is_a((new $returnType), 'Illuminate\Database\Eloquent\Casts\Attribute')) { + return false; + } + + if (is_a((new $returnType), 'Illuminate\Database\Eloquent\Relations\Relation')) { + return $method; + } + } + + return $method; + } }