0

I'm a bit confused how I am to add methods to Eloquent models. Here is the code in my controller:

public function show($id)
    {
        $limit = Input::get('limit', false);

        try {
            if ($this->isExpand('posts')) {
                $user = User::with(['posts' => function($query) {
                    $query->active()->ordered();
                }])->findByIdOrUsernameOrFail($id);
            } else {
                $user = User::findByIdOrUsernameOrFail($id);
            }

            $userTransformed = $this->userTransformer->transform($user);

        } catch (ModelNotFoundException $e) {
            return $this->respondNotFound('User does not exist');
        }

        return $this->respond([
            'item' => $userTransformed
        ]);
    }

And the code in the User model:

public static function findByIdOrUsernameOrFail($id, $columns = array('*')) {
        if (is_int($id)) return static::findOrFail($id, $columns);

        if ( ! is_null($user = static::whereUsername($id)->first($columns))) {
            return $user;
        }

        throw new ModelNotFoundException;
    }

So essentially I'm trying to allow the user to be retrieved by either user_id or username. I want to preserve the power of findOrFail() by creating my own method which checks the $id for an int or string.

When I am retrieving the User alone, it works with no problem. When I expand the posts then I get the error:

Call to undefined method Illuminate\Database\Query\Builder::findByIdOrUsernameOrFail()

I'm not sure how I would go about approaching this problem.

1
  • have you tried call the method like this $user -> findByIdOrUsernameOrFail($id) ? Commented Aug 27, 2015 at 20:33

2 Answers 2

2

You are trying to call your method in a static and a non-static context, which won't work. To accomplish what you want without duplicating code, you can make use of Query Scopes.

public function scopeFindByIdOrUsernameOrFail($query, $id, $columns = array('*')) {
    if (is_int($id)) return $query->findOrFail($id, $columns);

    if ( ! is_null($user = $query->whereUsername($id)->first($columns))) {
        return $user;
    }

    throw new ModelNotFoundException;
}

You can use it exactly in the way you are trying to now.


Also, you can use firstOrFail:

public function scopeFindByIdOrUsernameOrFail($query, $id, $columns = array('*')) {
    if (is_int($id)) return $query->findOrFail($id, $columns);

    return $query->whereUsername($id)->firstOrFail($columns);
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, that last solution is exactly what I needed, using a scope in this instance makes more sense, and uses less code (a laravel favorite)
1

Your method is fine, but you're trying to use it in two conflicting ways. The one that works as you intended is the one in the else clause, like you realised.

The reason the first mention doesn't work is because of two things:

  1. You wrote the method as a static method, meaning that you don't call it on an instantiated object. In other words: User::someStaticMethod() works, but $user->someStaticMethod() doesn't.
  2. The code User::with(...) returns an Eloquent query Builder object. This object can't call your static method.

Unfortunately, you'll either have to duplicate the functionality or circumvent it someway. Personally, I'd probably create a user repository with a non-static method to chain from. Another option is to create a static method on the User model that starts the chaining and calls the static method from there.

Edit: Lukas's suggestion of using a scope is of course by far the best option. I did not consider that it would work in this situation.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.