Separating methods into many controllers? - laravel

I noticed that many of the examples in the Laravel docs seem to have Controllers where the class has only one use/method.
For example, in this part of the doc, they have a UpdatePasswordController class with a single method, update():
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use App\Http\Controllers\Controller;
class UpdatePasswordController extends Controller
{
/**
* Update the password for the user.
*
* #param Request $request
* #return Response
*/
public function update(Request $request)
{
// Validate the new password length...
$request->user()->fill([
'password' => Hash::make($request->newPassword)
])->save();
}
}
Normally, I would put a method called updatePassword() in my UserController class (along with signIn(), signUp(), resetPassword(), etc.), but I'm wondering if it's better to create multiple classes, each with a single action?

Normally class is defined for a single purpose. In laravel , for authentication there is a Illuminate base bundle which is optimized for years.
As an example UpdatePasswordController only responsible for updating password,
AuthController only responsible for authentication.
I prefer you to reserch some MVC best practices

Related

Which namespace do i use for laravel view()?

My ide typhints a few different namespaces for rendering a view in my controller and i'm not sure which one i'm supposed to use:
class PostsController extends Controller
{
public function index() : View
{
return view('posts.index');
}
}
The "view" function returns multiple types:
#return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
So which one am i supposed to use? \Illuminate\Contracts\View\View or \Illuminate\Contracts\View\Factory
What is the difference?
Why is it returning two different types instead of one? I code php in a strict way because i prefer it for readable and less error prone code, in my opinion this is a bad way of doing it and as you can see is causing confusion; a method should only be allowed one return type, create multiple methods if need be.
EDIT
Thank you for your input everyone, i have come up with the following that allows me to use the facade and the contract without producing typhint errors in my ide:
<?php
namespace App\Http\Controllers;
use Illuminate\Contracts\View\View as ViewContract;
use Illuminate\Support\Facades\View as ViewFacade;
/**
* Class PostsController
* #package App\Http\Controllers
*/
class PostsController extends Controller
{
/**
* #return ViewContract
*/
public function index() : ViewContract
{
return ViewFacade::make('posts.index');
}
}
So i can call View:make() and return the contract that View:make() returns.
EDIT 2
Using the view() helper i can condense further, i am aliasing with ViewContract just for my benefit of knowing which namespace i'm using:
<?php
namespace App\Http\Controllers;
use Illuminate\Contracts\View\View as ViewContract;
/**
* Class PostsController
* #package App\Http\Controllers
*/
class PostsController extends Controller
{
/**
* #return ViewContract
*/
public function index() : ViewContract
{
return view('posts.index');
}
}
I’ll try and address each of your questions.
In your instance, type-hint Illuminate\Contracts\View\View or the concrete implementation (Illuminate\View\View).
I’ll cover this in 3.
You’re using the view global helper function. It can return different types because, well, it can. If you don’t pass a parameter to view() then it will return a view factory instance. If you do pass a parameter (like you have in your usage), then it will return an instance of the view named by the first parameter (if such a view exists). So that’s why the view() helper function is typed to return multiple different types. Because depending on how you use it, it can return a different type.
You mean: \Illuminate\View\View.
public function index(): \Illuminate\View\View
{
return view('posts.index');
}
view() is a helper of Illuminate\Support\Facades\View facade.
return view('posts.index');
Same as :
use Illuminate\Support\Facades\View;
return View::make('posts.index');
See the documentation of Creating & Rendering Views

Using Sanctum with Laravel Spark conflict

My setup
Laravel 8
Laravel Spark Mollie
I'm constantly hitting a brick wall when calling API requests with Spark & Sanctum. I've installed Sanctum with no problem and migrated.
I've added use Laravel\Sanctum\HasApiTokens; to app/Models/User.php and added use HasApiTokens; to the class.
My Api.php route
Route::group([
'middleware' => 'auth:sanctum'
], function () {
Route::get('categories', [\App\Http\Controllers\categories::class, 'fetchCategories']);
});
When I call the Api I get this error
ErrorException
Declaration of Laravel\Sanctum\HasApiTokens::tokenCan(string $ability) should be compatible with Laravel\Spark\User::tokenCan($ability)
I've tried changing use Laravel\Sanctum\HasApiTokens; to Laravel\Spark\HasApiTokens on User.php. The error goes away, but whenever I try calling the Api, it returns me back to the login homepage.
Any ideas? As the Spark documentation doesn't really explain how Sanctum or Api protection work.
The problem is that your main User class extends the User class from the vendor Spark library. This User model uses the trait named HasApiTokens which is not the same as Sanctum
Since you don't want to change the file from the vendor directory, one fix I found was to copy the original SparkUser model class from the vendor and create a new one like this and remove the trait HasApiTokens since you don't want to use it anymore.
<?php
namespace App\Models\Users;
use Illuminate\Support\Str;
use Laravel\Spark\Billable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class SparkUser extends Authenticatable
{
use Billable, Notifiable; // HasApiTokens was removed from the original SparkUser class
/**
* Get the profile photo URL attribute.
*
* #param string|null $value
* #return string|null
*/
public function getPhotoUrlAttribute($value)
{
return empty($value) ? 'https://www.gravatar.com/avatar/'.md5(Str::lower($this->email)).'.jpg?s=200&d=mm' : url($value);
}
/**
* Make the team user visible for the current user.
*
* #return $this
*/
public function shouldHaveSelfVisibility()
{
return $this->makeVisible([
'uses_two_factor_auth',
'country_code',
'phone',
'card_brand',
'card_last_four',
'card_country',
'billing_address',
'billing_address_line_2',
'billing_city',
'billing_state',
'billing_zip',
'billing_country',
'extra_billing_information'
]);
}
/**
* Convert the model instance to an array.
*
* #return array
*/
public function toArray()
{
$array = parent::toArray();
if (! in_array('tax_rate', $this->hidden)) {
$array['tax_rate'] = $this->taxPercentage();
}
return $array;
}
}
And now all I had to change was my original User class model to use this new model like this and add the trait HasApiTokens from Sanctum!
use App\Models\SparkUser; // Modified from the original in the vendor folder
use Laravel\Sanctum\HasApiTokens;
class User extends SparkUser
{
use HasApiTokens;
...
}

Namespaces in Php/Laravel

Suppose some controller like this:
<?php
namespace App\Http\Controller
use Illuminate\Http\Request;
use Validator;
use Auth;
class MemberController extends Controller {
//some code
}
Where do Validator and Auth belong to (so I can see their defintion) and what exactly are they?
I've one more question: here (Laravel 5.6 documentaton) it says
we will use the validate method provided by the
Illuminate\Http\Request object.
and when I check here for more information, there's no validate method!
I'd really appreciate it if anyone can help me with these questions.
Validator and Auth are aliases for the Facades of the same name in Illuminate\Support\Facades. They are a static proxy for an instance of a class. You can read about them in the Laravel docs about Facades.
The aliases for these are configured in config/app.php 'aliases' array.
Laravel 5.6 Docs - Facades
For $request->validate(...):
It is a macro. Macros allow you to add functionality to classes that implement macro functionality at run time.
The Illuminate\Foundation\Providers\FoundationServiceProvider#registerRequestValidation sets this macro on Illuminate\Http\Request to allow for a validate method.
/**
* Register the "validate" macro on the request.
*
* #return void
*/
public function registerRequestValidation()
{
Request::macro('validate', function (array $rules, ...$params) {
return validator()->validate($this->all(), $rules, ...$params);
});
}
For a quick read about macros in Laravel check out my article:
asklagbox blog - Using Macros in Laravel

Can laravel blade service injection can be done from outside view

I want to inject service globally for all application views
can i inject it thorough application service provider boot method ?
What service you want to inject? How will you use it?
An easy way to share variables across all views is to call the share method:
view()->share([
'myService' => app()->make(My\Service::class),
]);
You can call this within your controller or maybe inside a middleware to work across many different controllers, too.
Then, in your views, something like this:
#foreach ($myService->getItems() as $item)
...
#endforeach
Follow this steps:
create service provider: php artisan make:provider UserServiceProvider
Go to
app\providers\UserServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\View;
use Auth;
class UserServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
// key can be anything
// value what you want
View::share('key', 'value');
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
}
}
Than register this service provider inside the config\app.php
App\Providers\UserServiceProvider::class,
Now you can access this key for every views.

Laravel ResetsPasswords Trait

Locally, I have updated this trait to do some different redirecting after the user submits the getEmail() method to request the reset password link. When pushed to production, my editions aren't there. I'm guessing this is because the ResetsPasswords trait is in the laravel framework which is installed separately from my repository on the server.
If this is the case, what's the best way to change how this ResetsPasswords trait functions. Do I make my own and include that in the repository and just change my controller? Below is the PasswordController.
Thanks!
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class PasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
protected $redirectPath = '/main';
/**
* Create a new password controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest');
}
}
Update: So, in the ResetsPasswords trait, I modify the redirect getSendResetLinkEmailSuccessResponse() method. So, do I instead just put that method in my controller (with the same name) and my edited code?
protected function getSendResetLinkEmailSuccessResponse($response)
{
...modified code...
}
You should not be making changes to the Laravel vendor files for the reason you stated.
Instead, you should override any of the trait functions you need to modify in your controller.
So just add the method to your controller with your modified code like so:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class PasswordController extends Controller {
use ResetsPasswords;
protected $redirectPath = '/main';
public function __construct() {
$this->middleware('guest');
}
protected function getSendResetLinkEmailSuccessResponse($response) {
// modified code that sends an awesome flash message
}
}
Also, if all you're trying to do is change where the user is redirected to then you don't have to override the functions at all. All you have to do is change the redirectPath property in your controller.

Resources