Laravel-如何动态解析具有不同命名空间的模型的实例?

时间:2019-06-06 18:45:37

标签: laravel

tldr:

如何仅通过数据库表名称动态获取模型的实例?

您从请求中获得的信息:

  • 模型的ID
  • 模型的表名(它随时都在变化!)

您不知道的地方:

  • 模型的命名空间

更长的解释:

我有一个报告系统,用户可以用来报告某些东西。对于每个报告,都会发送ID和表名。

直到现在,每个模型都位于命名空间App\*下。但是,由于我的项目太大,我需要将一些代码分成Modules\*

这是一个示例,该报告如何保存在数据库中:

示例:

请求包含规则:

public function rules()
{
    return [
        'id' => 'required|string',
        'type' => 'required|in:users,comments,offer_reviews, ......',
        'reason' => 'required|string',
        'meta' => 'nullable|array',
        'meta.*' => 'string|max:300'
    ];
}

在数据库中,我们将数据保存到:

id  reportable_type            ...
1   App\User                   ...
4   Modules\Review\OfferReview ...

当您仅知道数据库表名称(例如offer_reviews)时,如何动态创建模型的实例?

我想到了一个解决方案,但是,我不确定它是否增加了更多的安全性问题。如果用户发送完整的名称空间+类名该怎么办?这样,我就直接知道在哪里解析实例。

看看我现在在做什么

(在我更改为模块之前)

//在我的控制器中

class ReportController extends Controller
{
    /**
     * Stores the report in DB.
     */
    public function store(StoreReportRequest $request)
    {
        $model = $request->getModel();

        $model->report([
            'reason' => $request->reason,
            'meta' => $request->meta
        ], auth()->user());

        return response()->json(['status' => 'Submitted'], 201);
    }
}

//在StoreReportRequest中

/**
 * Gets the Model dynamically.
 * If not found we throw an error
 * @return \App\Model
 */
public function getModel()
{
    return get_model($this->type)
                ->findOrFail(\Hashids::decode($this->id)[0]);
}

//在助手中

/**
 * Gets me the model of a table name.
 * @param  String $table Has to be the name of a table of Database
 * @return Eloquent       The model itself
 */
function get_model($table)
{
    if (Schema::hasTable(strtolower($table))) {
        return resolve('App\\' . Str::studly(Str::singular($table)));
    }
    throw new TableNotFound;
}

1 个答案:

答案 0 :(得分:0)

我不知道是否有更好的解决方案,但是您可以。当找不到我们使用App\作为命名空间的代码时,我的代码正在寻找带有命名空间的方法。

也许这段代码可以帮助某人:)

class StoreReportRequest extends FormRequest
{
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'id' => 'required|string',
            'type' => 'required|in:mc_messages,profile_tweets,food,users,comments,offer_reviews,user_reviews',
            'reason' => 'required|string',
            'meta' => 'nullable|array',
            'meta.*' => 'string|max:300'
        ];
    }

    /**
     * Gets the Model dynamically.
     * If not found we throw an error
     * @return \App\Model
     */
    public function getModel()
    {
        $namespace = $this->getNamespace();

        return $this->resolveModel($namespace);
    }

    protected function getNamespace(): string
    {
        $method = $this->typeToMethod();

        if (method_exists($this, $method)) {
            return $this->$method();
        }
        return 'App\\';
    }

    protected function typeToMethod(): string
    {
        return  'get' . \Str::studly(\Str::singular($this->type)) . 'Namespace';
    }

    protected function resolveModel(string $namespace)
    {
        return get_model($this->type, $namespace)
            ->findOrFail(\Hashids::decode($this->id)[0]);
    }

    protected function getOfferReviewNamespace(): string
    {
        return 'Modules\Review\Entities\\';
    }

    protected function getUserReviewNamespace(): string
    {
        return 'Modules\Review\Entities\\';
    }
}