在Laravel Eloquent对象上获取关系方法名称的最有效方法是什么?

时间:2018-10-25 08:06:46

标签: php laravel laravel-5 eloquent

很多时候,我都有理由获得在Eloquent对象上定义的关系的名称。由于Laravel目前不提供任何方法/帮助程序,因此我提出了以下建议:

public static function getRelationshipsForEloquentlModel($eloquentObject) {
        $methods   = self::getDirectClassMethods($eloquentObject);
        $relations = [];
        foreach ($methods as $method) {
            try {
                $reflection = new \ReflectionMethod($eloquentObject, $method);
                //filter out non-eloquent relationship methods that expect parameters in
                //their signature (else they blow up when they get called below without pars)
                $pars = $reflection->getNumberOfParameters();
                if ($pars == 0) {
                    $possibleRelationship = $eloquentObject->$method();
                    //one of the things we can use to distinctively identify an eloquent
                    //relationship method (BelongsTo, HasMany...etc) is to check for
                    //one of the public methods defined in Illuminate/Database/Eloquent/Relations/Relation.php
                    //(and hope that it is not discontinued/removed in future versions of Laravel :))
                    if (method_exists($possibleRelationship, "getEager")) {
                        $relationshipType = get_class($possibleRelationship);
                        //remove namespace
                        if ($pos = strrpos($relationshipType, '\\')) {
                            $relationshipType = substr($relationshipType, $pos + 1);
                        }
                        $relations[$method] = $relationshipType;
                    }
                }
            } catch (\Exception $ex) {
                //Eloquent's save() method will throw some
                //sql error because $eloquentObject may be
                //an empty object like new App\User (so some NOT NULL db fields may blow up)
            }
        }

        return $relations;
    }

辅助类getDirectClassMethods在下面(由onesimus on official PHP docs comment提供):

public static function getDirectClassMethods($class) {
        $array1 = get_class_methods($class);
        if ($parent_class = get_parent_class($class)) {
            $array2 = get_class_methods($parent_class);
            $array3 = array_diff($array1, $array2);
        } else {
            $array3 = $array1;
        }
        return ($array3);
    }

现在,整个代码清单对我来说是如此繁琐和冗长,至少在所需任务如此简单的情况下如此。没有所有这些冗长的词,是否有更好/更快/更有效的方法来实现这一目标?

1 个答案:

答案 0 :(得分:1)

此特征应有帮助         

    namespace App;

    use ErrorException;
    use Illuminate\Database\Eloquent\Relations\Relation;
    use ReflectionClass;
    use ReflectionMethod;

    trait RelationshipsTrait
    {
        public function relationships() {

            $model = new static;

            $relationships = [];

            foreach((new ReflectionClass($model))->getMethods(ReflectionMethod::IS_PUBLIC) as $method)
            {
                if ($method->class != get_class($model) ||
                    !empty($method->getParameters()) ||
                    $method->getName() == __FUNCTION__) {
                    continue;
                }

                try {
                    $return = $method->invoke($model);

                    if ($return instanceof Relation) {
                        $relationships[$method->getName()] = [
                            'type' => (new ReflectionClass($return))->getShortName(),
                            'model' => (new ReflectionClass($return->getRelated()))->getName()
                        ];
                    }
                } catch(ErrorException $e) {}
            }

            return $relationships;
        }
    }

您应该获得一个数组数组,只需将特征添加到任何模型中即可。

    class Article extends Model
    {
        use RelationshipsTrait;

        ...
    }

    $article = new Article;
    dd($article->relationships());

应该输出

    "example" => array:2 [▼
        "type" => "BelongsTo"
        "model" => "App\Example"
      ],
      "gallery" => array:2 [▼
        "type" => "MorphMany"
        "model" => "App\Gallery"
      ]