Laravel Virgin: Inject a Model In Controller as Dependency - php

In my inherited code in the Models there's some serious logic and I want to use the Laravel's Dependency Injection in order to load the models as Dependencies into the controller instead of Using the Laravel's provided Facades.
So here's a sample Controller:
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*
* #param int $id
* #return View
*/
public function show($id)
{
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}
But Instead of using the Facade User I want to be able to load it as dependency into the controller:
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
user App\Models\User
class UserController extends Controller
{
/**
* #var User
*/
private $user=null;
public function __construct(User $user)
{
$this->user=$user;
}
/**
* Show the profile for the given user.
*
* #param int $id
* #return View
*/
public function show($id)
{
return view('user.profile', ['user' => $this->user->findOrFail($id)]);
}
}
The reason why I want to do that is because I come from Symfony Background where the Dependency Injection Pattern Is heavily Erdosed. Also Dependency Injection is the Unit Test's best buddy, so I want to be able to unitilize the Dependency Injection that I am familiar with.
So I wanted to know whether I can inject the models where the logic exists in the Controllers Instead of using the Facade Pattern provided by laravel.

When you register your route, you can use the model binding:
// routes/web.php
Route::get('users/{user}', 'UserController#show');
Then in your controller, you can change your method to:
public function show(User $user)
{
//
}
Where the $user will be the instance of App\User with the right id. For example, if the url is /users/1, the $user will contain the App\User with id 1.
For more information: https://laravel.com/docs/5.8/routing#route-model-binding

Related

Should I use the FQN in PHPDoc if I'm importing the class with use?

Let's say I have a UserController class and I'm importing the App\User class with use. Inside, there is a show() method that receieves an instance of User.
namespace App\Http\Controllers;
use App\User;
class UserController extends Controller
{
/**
* Show user info
*
* #param User $user
* #return Illuminate\Http\JsonResponse
*/
public function show(User $user)
{
// Do something...
}
}
Is it recommended to add the fully qualified name of User in PHPDoc even if I'm importing the class with use?
Use FQN if there are no use statement, as it would be recognized as a different class, in Your case as \JsonResponse which is not imported.
As for User class, use short name.
It is more convenient to import class with use statement and write short name in docblock.
Also class aliases can be used, for instance:
namespace App\Http\Controllers;
use App\User;
use Illuminate\Http\JsonResponse as Response;
class UserController extends Controller
{
/**
* Show user info
*
* #param User $user
* #return Response
*/
public function show(User $user)
{
// Do something...
}
}

Extending Laravel base controller

I am a newbie in Laravel framework and I want to extend a base controller which in turn extends controller. However, I discovered that when I do that, my controller no longer recognises my session variables.
Here is my code
namespace App\Http\Controllers\Settings;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Auth\PermissionController;
use App\Fee;
class FeeController extends PermissionController
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
dd(session('userdata')['user_urls']);
$data['title']="Fees";
$data['fees']=Fee::all();
return view('settings.fee.index',$data);
}
And this is my PermissionController code
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class PermissionController extends Controller {
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct(Request $request) {
if(!session('userdata')['user_urls']->contains($request->path())){
dd(session('userdata')['user_urls']);
}
}
}
But I realize that my session('userdata')['user_urls'] becomes null at the PermissionController. But if I make FeeController to extend Controller, my session variables are intact.
I need to use the session variables for some control at the permission controller.
I am running Laravel 5.3 on a MAC OSX and PHP 7
I have figured out the problem. Actually, PermissionController is not registered in the web middleware group so that session is not persisting in the PermissionController. So the solution to your question is just make a trait named as Permission instead of the controller and use it in FeesContorller.
trait Permission{
public function permission(Request $request) {
if($request->session()->get('name') != null){
echo "Hello World";
}
}
}
And FeesController like this:
class FeesController extends Controller
{
use Permission;
public function index(Request $request)
{
$this->permission($request); // the method of the trait.
echo "\n".$request->session()->get('name');
}
}
Output:
If the name attribute is set in session then :
Hello World
Passion Infinite
Otherwise
No Output // null
I have solved the same problem with middleware. I have created a middleware that takes care of the authorization of requests by checking the session to ensure that the controller action being accessed is available in session.
This is the middleware
namespace App\Http\Middleware;
use Closure;
class PermissionMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$currentAction = \Route::currentRouteAction();
list($nothing,$route_action) = explode('App\Http\Controllers\\', $currentAction);
$user_actions=session('userdata')['user_urls'];
if((empty($user_actions))||!$user_actions->contains($route_action)){
return redirect('denied');
}
return $next($request);
}
}
This is the controller
namespace App\Http\Controllers\Settings;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Auth\PermissionController;
use App\Fee;
class FeeController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$data['title']="Fees";
$data['fees']=Fee::all();
return view('settings.fee.index',$data);
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create(Request $request)
{
$data['title']='New Fee';
return view('settings.fee.create',$data);
}
So, instead of using the routes (cos of some other reasons), I used the controller actions.
So, once a user logs in, all the controller actions he can access are loaded into session. When he tries to perform any action, the middleware, does the check to ensure he is allowed to perform that action. Otherwise, he is routed away.
So, I either add 'permission' to the routes middleware or call
$this->middleware('permission')
on the controller's construct method.
That is working for me now.
Thank you everybody for your contributions.

Laravel controller parameter's purpose?

namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*
* #param int $id
* #return Response
*/
public function showProfile($id)
{
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}
Why do we put parameters on laravel controllers when we can use "Input::get(id)"?
Can anyone give a sample on a situation where I need to use parameters on controller?
In your condition when you are using Input::get('id'), the request to show the profile will be something like
yourdomain.com/profile?id=2
But if you want to show the profile of a user based on something like this one,
yourdomain.com/profile/2
or
yourdomain.com/profile/john
you need a controller parameter.
In the seconds cases you can simply then, bind your parameter to the User model.
public function showProfile(User $user)
{
return view('user.profile', ['user' => $user);
}

How to validate in model in Laravel 5?

I am new to Laravel. Now I am starting to develop a project using Laravel 5. I have been using CodeIgniter before. I am having problem with validation class that is not compatible with what I want to do.
Normally we validate in Controller like this.
public function postCreate(Request $request)
{
$this->validate($request, [
'name' => 'required|max:30',
'mm_name' => 'required|max:50',
]);
echo "passed";
}
That is working find. But what I want to do is I want to move that validation logic to model. So I created a new folder called Models under app folder. Under the Models folder I created a class called ValidationHelper that extends the Model class.
This is my Validation helper class
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use DB;
class ValidationHelper extends Model{
function CreateCategory($request)
{
$this->validate($request, [
'name' => 'required|max:30',
'mm_name' => 'required|max:50',
]);
}
}
So now I am trying to import that class to my controller using constructor dependency injection. So now my controller is like this.
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Models\ValidationHelper;
class CategoryController extends Controller
{
protected $validationHelper;
function __construct(ValidationHelper $v_helper)
{
$this->validationHelper = $v_helper;
}
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function categoryList()
{
//
return View('admin.Category.list');
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
//
return View('admin.Category.create');
}
}
So when I run my app and do validation, it is giving me this error. How can I do this in Laravel? How to separate my validation logic to model?
You can move the code in your model by registering model event(s) as given below:
// For example, User.php model
public static function boot()
{
parent::boot();
static::creating(function($user)
{
if (!$user->isValid()) return false;
}
}
If you put this boot method in your User.php model then whenever you'll create a new user, the validation will take place at first. For this, you have to create the isValid method in your model where you'll check the validation by yourself and return true/false depending on the validation result. If you return false, creation will be inturupted. This is just an idea but you may read more here about model events.

How to Add an Object to Laravel's IOC Container from Middleware

I want to create an object in my middleware (in this case, a collection from an Eloquent query), and then add it to the IOC container so I can type-hint method signatures in my controllers to access it.
Is this possible? I can't find any examples online.
You can do that very easy, in several steps.
Create new middleware (name it like you want)
php artisan make:middleware UserCollectionMiddleware
Create new collection class that will extend Eloquent database collection. This step is not required, but will let you in future to create different bindings, using different collection types. Otherwise you can do only one binding to Illuminate\Database\Eloquent\Collection.
app/Collection/UserCollection.php
<?php namespace App\Collection;
use Illuminate\Database\Eloquent\Collection;
class UserCollection extends Collection {
}
Add your bindings in app/Http/Middleware/UserCollectionMiddleware.php
<?php namespace App\Http\Middleware;
use Closure;
use App\User;
use App\Collection\UserCollection;
class UserCollectionMiddleware {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
app()->bind('App\Collection\UserCollection', function() {
// Our controllers will expect instance of UserCollection
// so just retrieve the records from database and pass them
// to new UserCollection object, which simply extends the Collection
return new UserCollection(User::all()->toArray());
});
return $next($request);
}
}
Don't forget to put the middleware on the desired routes, otherwise you will get an error
Route::get('home', [
'middleware' => 'App\Http\Middleware\UserCollectionMiddleware',
'uses' => 'HomeController#index'
]);
Now you can type hint this dependency in your controller like this
<?php namespace App\Http\Controllers;
use App\Collection\UserCollection;
class HomeController extends Controller {
/**
* Show the application dashboard to the user.
*
* #return Response
*/
public function index(UserCollection $users)
{
return view('home', compact('users'));
}
}

Categories