php - How to stop passing null to `__construct`

I have a Service which is called inside a Controller in a Laravel application. Usually these services are implemented using a composition pattern, where the service is injected in the __construct of the controller; then, whenever it's necessary to use the service, you just call the service and the method you want. If you want to create a new register on the database, you just need to call the service, the method, and pass the necessary parameters to that method.

But let's say I don't want to pass parameters. I want an immutable, zero-parameter object. I have tried to implement this in the code bellow (first there's the service and then the controller where the service is called). Notice I have all the parameters I'd need to pass to each method in the constructor of my service. This produces what I want: an immutable, zero-parameter object.

But there's a problem: it's not in every call of the service that I need all the parameters in the constructor (not every method used needs them), that's why they're typed as a [any type]|null. This seems wrong, because if I don't need all the parameters to construct my object, maybe it should be another object altogether. I don't have a problem with small classes (I like it, actually), but it seems just too much to have a CreateCmsUserService, UpdateCmsUserService, DeleteCmsUserService.

I'd like to know if there's an elegant solution for this constructor with null or if the only way to go is dividing the class.

<?php

namespace App\Services;

use App\Models\User;
use Exception;
use Illuminate\Support\Facades\Hash;
use App\Interfaces\CRUD;

class CmsUsersService implements CRUD
{
  private ?int $user_id;
  private ?array $data;
  private ?string $users_to_be_deleted;

  public function __construct(?array $data, ?int $user_id, ?string $users_to_be_deleted)
  {
    $this->user_id = $user_id;
    $this->data = $data;
    $this->users_to_be_deleted = $users_to_be_deleted;
  }

  public function create()
  {
    $this->data['token'] = Hash::make($this->data['email']);
    $this->data['password'] = Hash::make($this->data['password']);
    User::create($this->data);

    return cms_response(trans('cms.users.success_create'));
  }

  public function update()
  {
    try {
      if (array_key_exists('password', $this->data)) {
        $this->data['password'] = Hash::make($this->data['password']);
      }

      $user = $this->__findOrFail();
      $user->update($this->data);

      return cms_response(trans('cms.users.success_update'));
    } catch (\Throwable $th) {
      return cms_response($th->getMessage(), false, 400);
    }
  }

  public function delete()
  {
    User::whereIn('id', json_decode($this->users_to_be_deleted))->delete();
    return cms_response(trans('cms.users.success_delete'));
  }

  private function __findOrFail()
  {
    $user = User::find($this->user_id);
    if ($user instanceof User) {
      return $user;
    }
    throw new Exception(trans('cms.users.error_user_not_found'));
  }
}

<?php

namespace App\Http\Controllers\Cms;

use App\Http\Controllers\Controller;
use App\Http\Requests\UserRequest;
use App\Services\CmsUsersService;
use Illuminate\Http\Request;

class UsersController extends Controller
{
  /**
   * Display a listing of the resource.
   *
   * @return \Illuminate\Http\Response
   */
  public function index()
  {
  }

  /**
   * Show the form for creating a new resource.
   *
   * @return \Illuminate\Http\Response
   */
  public function create()
  {
    //
  }

  /**
   * Store a newly created resource in storage.
   *
   * @param  \Illuminate\Http\Request  $request
   * @return \Illuminate\Http\Response
   */
  public function store(UserRequest $request)
  {
    $users_service = new CmsUsersService($request->all(), null, null);
    $result = $users_service->create();
    return redirect()->back()->with('message', $result);
  }

  /**
   * Display the specified resource.
   *
   * @param  int  $id
   * @return \Illuminate\Http\Response
   */
  public function show($id)
  {
    //
  }

  /**
   * Show the form for editing the specified resource.
   *
   * @param  int  $id
   * @return \Illuminate\Http\Response
   */
  public function edit($id)
  {
    //
  }

  /**
   * Update the specified resource in storage.
   *
   * @param  \Illuminate\Http\Request  $request
   * @param  int  $id
   * @return \Illuminate\Http\Response
   */
  public function update(UserRequest $request, $id)
  {
    $users_service = new CmsUsersService($request->all(), $id, null);
    $result = $users_service->update();
    return redirect()->back()->with('message', $result);
  }

  /**
   * Remove the specified resource from storage.
   *
   * @param  string  $users_id
   * @return \Illuminate\Http\Response
   */
  public function destroy($users_id)
  {
    $users_service = new CmsUsersService(null, null, $users_id);
    $result = $users_service->delete();
    return redirect()->back()->with('message', $result);
  }
}

Answer

Solution:

You could give parameters a default value, like so:

  public function __construct(?array $data = [], ?int $user_id = null, ?string $users_to_be_deleted = null)
  {
    $this->user_id = $user_id;
    $this->data = $data;
    $this->users_to_be_deleted = $users_to_be_deleted;
  }

Now you can use this:

  $users_service = new CmsUsersService();

See: https://3v4l.org/InkBY

Source