Laravel query builder macro on loaded models

Viewed 1136

I have developed a package to enable search method on Eloquent models with JSON.

The engine currently works in a way that a query is appended for supported parameters depending on values provided.

Builder::macro('search', function (Request $request) {
    /**
     * @var $this Builder
     */
    $searcher = new Searcher($this, $request);
    $searcher->search();
    return $this;
});

Which in turn runs the following:

/**
 * Perform the search
 *
 * @throws Exceptions\SearchException
 */
public function search(): void
{
    $this->appendQueries();
    Log::info('[Search] SQL: ' . $this->builder->toSql());
}

/**
 * Append all queries from registered parameters
 *
 * @throws Exceptions\SearchException
 */
protected function appendQueries(): void
{
    foreach ($this->requestParametersConfig->registered as $parameter) {
        $requestParameter = $this->createRequestParameter($parameter);
        $requestParameter->appendQuery();
    }
}

protected function createRequestParameter($parameter): AbstractParameter
{
    return new $parameter($this->request, $this->builder, $this->modelConfig);
}

Request parameters are registered within configuration file and point to request parameter classes to have everything modular.

Now this works great if used on a something like index endpoint or a custom search endpoint, but I would like to extend the functionality to show as well. The issue being here that with route model binding, the model is already loaded when I fetch it.

Let's take relationships for example. I have a request parameter which when used loads relationships on a given model:

www.example.com/api/contacts?relations=(phones)

will load phones relations on Contact model by doing this:

/**
 * Append the query to Eloquent builder
 * @throws SearchException
 */
public function appendQuery(): void
{
    $arguments = $this->getArguments();

    $this->builder->with($arguments);
}

But when I do a ->with on already loaded model followed by ->get(), I just get list of all models with loaded relation.

Is there a way to "tap into" already loaded model with query builder, or is it at this point already done deal, and I should pick up the ID on show route instead of resolving it, and then assemble the query?

1 Answers

After a lot of research of Laravel inner workings, what I asked initially isn't possible.

At the point where you call the method to interact with DB in some way, a query builder instance is made and depending on the method you called on the original model, the query is formed. After execution there is nothing to tap into as the object no longer exists.

Also, from the logical point of view, I have concluded that search makes no sense on already loaded models, so I did a concept switch there and disabled the package for already loaded models.

Related