如何在这里避免PHP代码重复

I am creating a laravel todo application. In my controller there's have different methods but all codes in it are almost same. In notCompleted method and completed method there have 1 more different where clause. Except it all codes are same. How can I avoid code duplication here?

public function all() 
{
    $user_id = $this->user_id;

    $todos = $this->todos
            ->where('user_id', $user_id)
            ->orderBy('id', 'DESC')->paginate(15);

    return view('todos.index', compact('todos'));
}


public function notCompleted() 
{
    $user_id = $this->user_id;

    $todos = $this->todos
            ->where('user_id', $user_id)
            ->where('completed', false)
            ->orderBy('id', 'DESC')->paginate(15);

    return view('todos.index', compact('todos'));   
}


public function completed() 
{
    $user_id = $this->user_id;

    $todos = $this->todos
            ->where('user_id', $user_id)
            ->where('completed', true)
            ->orderBy('id', 'DESC')->paginate(15);
    return view('todos.index', compact('todos'));        
}

I was needed three different methods, So I kept those methods and extract the codes into one methods. And may that save's a code duplication. Isn't it?

and Thanks to all who respond me :)

public function all() 
{
    return $this->todoStatus('all');
}

public function index()
{   
    return $this->todoStatus('current', false);
}

public function completed() 
{
    return $this->todoStatus('completed', true);
}

protected function todoStatus($completed, $status = false) 
{
    $user_id = $this->user_id;

    if($completed === 'all') {
        $todos = $this->todos
            ->where('user_id', $user_id)
            ->orderBy('id', 'DESC')->paginate(15);
        return view('todos.index', compact('todos'));
    } else {
        $todos = $this->todos
            ->where('user_id', $user_id)
            ->where('completed', $status)
            ->orderBy('id', 'DESC')->paginate(15);
        return view('todos.index', compact('todos'));       
    }
}

In your model you can define a scope to eliminate the duplicate. example

In Todo Model

public function scopeUserTodo($query, $userId){
   return $query->where('user_id', $userId);
}

public function scopeCompleted($query, $flag){
   return $query->where('completed', $flag);
}

public function scopeLatest($query){
   return $query->orderBy('id','Desc');
}

Then in your controller you can change your queries to

public function all() 
{
    $user_id = $this->user_id;

    $todos = $this->todos->userTodo($user_id)->latest()->paginate(15)

   return view('todos.index', compact('todos'));
}


public function notCompleted() 
{
     $user_id = $this->user_id;

     $todos = $this->todos->userTodo($user_id)->completed(false)
             ->latest()->paginate(15)


     return view('todos.index', compact('todos'));   
 }


 public function completed() 
 {
     $user_id = $this->user_id;

      $todos = $this->todos->userTodo($user_id)->completed(true)
             ->latest()->paginate(15)

    return view('todos.index', compact('todos'));        
 }

You can further create a function in your controller and name it todoState and move the common code between the notCompleted and completed function there. Example

 public function todoState($userId, $completed){

     $todos = $this->todos->userTodo($userId)
                 ->completed($completed)
                 ->latest()
                 ->paginate(15);

     return $todos;

 }
public function all($check, $value) 
{
    $user_id = $this->user_id;

    if($check !== "completed"){
    $todos = $this->todos
            ->where('user_id', $user_id)
            ->where('completed', $value)
            ->orderBy('id', 'DESC')->paginate(15);

    return view('todos.index', compact('todos'));
    }else{
        $todos = $this->todos
            ->where('user_id', $user_id)
            ->orderBy('id', 'DESC')->paginate(15);

            return view('todos.index', compact('todos'));
    }
}

something like this? and just pass through the data on function call

You could create a simple function that takes in the few changeable variables

so something like:

private function helper($userid, $completed) 
{
    return $this->todos
        ->where('user_id', $userid)
        ->where('completed', $completed)
        ->orderBy('id', 'DESC')->paginate(15);
}

Then in your controller code:

public function notCompleted() 
{
    $user_id = $this->user_id;

    $todos = $this->helper($user_id, false);

    return view('todos.index', compact('todos'));   
}

Create a function to getByCompletedStatus...

public function getByCompletedStatus($status) 
{
  $user_id = $this->user_id;

  $todos = $this->todos
        ->where('user_id', $user_id)
        ->where('completed', $status)
        ->orderBy('id', 'DESC')->paginate(15);
  return view('todos.index', compact('todos')); 
} 

Then you'll pass a true or false as required, and avoid the code duplication.