I just recently started learning Laravel and decided to dive in learning by doing.
As far as i understand from documentation [example:1] [example:2] i can typehint properties in constructors for automatic injection.
So to try this out i've decided to redo the breeze scaffolded user registration's controller to make it more cleaner and extract the validation and all the logic into a service.
class RegisteredUserController extends Controller
{
protected UserService $userService;
public function __construct(UserService $userService)
{
}
public function store(RegisterUserRequest $request): RedirectResponse
{
$this->userService->create($request->validated());
return redirect(route('dashboard', absolute: false));
}
}
And the service is very simple, i just copy pasted the default code from the controller's action:
class UserService
{
/**
* @param RegisterUserRequest $request
* @return void
*/
public function create(RegisterUserRequest $request): void
{
$user = User::create([
'name' => $request['name'],
'email' => $request['email'],
'password' => Hash::make($request['password']),
'age' => $request['age'],
]);
event(new Registered($user));
Auth::login($user);
}
This is all fun and games untill now, everything is working as intended. So I've decided to keep on using the same pattern, i hate bloated controllers, even though this first method might not be necessary calling for a service. But i planned on adding more.
class HousingService
{
/**
* @return Collection
*/
public function list(): Collection
{
return Housing::all();
}
}
I hope you are still with me, sorry for making it long. But we are arriving to the problem right now.
When i try to do the same method of injecting the dependent HousingService class into my HousingController i get an error that says i'm trying to access a typed property before initialization.
Typed property App\Http\Controllers\HousingController::$housingService must not be accessed before initialization
So here's the controller in question. I have absolutely no idea what's the difference between the two.
class HousingController extends Controller
{
protected HousingService $housingService;
public function __construct(HousingService $housingService)
{
//if i type the commented code below it fixes the error, but
//i'd like to know what's causing it, since it wasn't required
//before
//$this->housingService = $housingService
}
public function list(Request $request)
{
return view('housing.list', [
'user' => $request->user(),
'housings' => $this->housingService->list(),
]);
}
}
Thank you for bearing with me, hope I'm not an absolute idiot for missing something very simple.
It seems you are confusing dependency injection with OOP. DI serves only to build required objects automatically, based on the class you have type hinted, and inject them into the current method. What you do with the built object within the method/class is up to you.
protected HousingService $housingService;
public function __construct(HousingService $housingService)
{
// here $housingService is the injected object from DI
// this injected object has nothing to do with the properties of the class
// $this->housingService is a property of the class and since you have not given it a default value it is an undefined property
// so $this->housingService and $housingService are not the same
// it is up to you to assign the property to the injected object
$this->housingService = $housingService
}
if you don't want to write this out, the shorter syntax version is:
public function __construct(protected HousingService $housingService)
{}
Keep in mind this is only syntactic sugar and it's functionally the same as the first version.