I am writing a general policy which which will apply on multiple Models. How can I retrieve the class name of the class which needs to be authorized?
Policies:
protected $policies = [
'App\User' => 'App\Policies\ModelPolicy',
'App\Customer' => 'App\Policies\ModelPolicy',
];
The ModelPolicy:
class ModelPolicy
{
use HandlesAuthorization;
/**
* Create a new policy instance.
*
* #return void
*/
public function __construct()
{
}
public function index(User $user){
// how can I retrieve the class name, like User or Customer?
return true;
}
}
This is for example my customer controller. So in the policy I want to retrieve something like: App\Customer.
class CustomerController extends Controller
{
public function index(){
$this->authorize('index', Customer::class);
echo "test";
}
}
You'll need custom gates.
In your controller:
$this->authorize('model-index', Appointment::first());
In AuthServiceProvider:
Gate::define('model-index', function ($user, $model) {
var_dump(get_class($model));
die();
});
This way you can take the parameters you need for your authorization methods.
Please see
https://laravel.com/docs/5.5/authorization#gates
Laravel Policies - How to Pass Multiple Arguments to function
Related
I have a StripeClient service provider which needs a key to instantiate:-
namespace App\Providers;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
use Stripe\StripeClient;
class StripeServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->singleton(StripeClient::class, function ($app) {
return new StripeClient(config('services.stripe.secret'));
});
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides()
{
return [StripeClient::class];
}
Then a trait with a bunch of api call functions like this:-
trait StripeClientTrait
{
protected $stripe;
function __construct(StripeClient $stripeClient)
{
$this->stripe = $stripeClient;
}
/**
* #param User $user
*
* #return \Stripe\Customer
* #throws \Stripe\Exception\ApiErrorException
*/
function createCustomer(User $user)
{
return $this->stripe->customers->create([ 'name' => $user->fullname,
'email' => $user->email
]);
}
...
The trait works in a controller perfectly as expected:-
class SubscriptionContoller extends Controller
{
use StripeClientTrait;
public function checkout()
{
try {
$customer = $this->createCustomer(Auth::user());
if($checkoutSession = $this->createCheckoutSession($customer)) {
return redirect($checkoutSession->url);
}
} catch (ApiErrorException $ex){
Log::error($ex->getMessage());
return back()->with(['error'=>$ex->getMessage()]);
}
return back();
}
...
But I now need to use the trait in a model to provide access to some api functions.
class Company extends Tenant
{
use HasFactory, StripeClientTrait;
but adding the trait causes:-
Too few arguments to function App\Models\Company::__construct(), 0 passed in /home/vagrant/code/profiler/vendor/spatie/laravel-multitenancy/src/Models/Concerns/UsesTenantModel.php on line 13 and exactly 1 expected
Can anyone tell me how to implement the trait without using the constructor? I just need some static function helpers to lookup stuff on the API.
Thanks for any guidance :-)
having persevered I've found this way to use the service container in a model:-
public function getPrices()
{
$stripe = app(StripeClient::class);
return $stripe->prices->all(['active'=>true]);
}
But would still like to understand how to use the trait in the model, if anyone could explain I'd be grateful
Normally we can simplify finding User by id logic in controller by injecting the User Class in parameter. Like this:
class UserController extends Controller
{
public function show(User $id)
{
return $user;
}
}
But now I must treat the Id to find like this:
<?php
namespace App\Http\Controllers;
class UserController extends Controller
{
public function show(User $id)
{
$preformattedId = '98'.$id;
$user = User::find($preformattedId );
return $user;
}
}
My basic question is: how I can achieved that same trick to my preformatted id in below code like the above code?
Note: I have to use the Id this way because i work with legacy database that actually adding that '98' prefix in every Id, despite that we only use characters after that prefix.
You can use Inversion of Control by using explicit binding on your router.
In your RouteServiceProvider
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
parent::boot();
Route::bind('user', function ($value) {
return User::find('98'.$value);
});
}
Or in your User model
/**
* Retrieve the model for a bound value.
*
* #param mixed $value
* #param string|null $field
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveRouteBinding($value, $field = null)
{
return $this->find('98'.$value);
}
https://laravel.com/docs/7.x/routing#explicit-binding
You can share your route file ?
But if your file is
Route::get('user/{id}', 'UserController#show');
When you use
class UserController extends Controller
{
public function show(User $id)
{
// you don't need use find function, it is make automatic by laravel
$user = $id;
return $user;
}
}
if you want to get id, just remove User type inside show parameter
class UserController extends Controller
{
public function show($id)
{
$preformattedId = '98'.$id;
$user = User::find($preformattedId );
return $user;
}
}
I know that there is a resource functionality in laravel and as far as I know resource is something like json to model and its reverse..
So when I process form data, currently, use following custom helper method..
public function assignFormdata(Request $request, $model, $map = [])
{
foreach($map as $input=>$field) {
// $field is model's param. $input is form data key.
$model->$field = $request->input($input) ?? $field;
}
return $model;
}
.. Is this method already exist in laravel? or something similar..?
There is no "standard" way in Laravel, that I am aware of, that will accomplish what you have above, where you assign a default value to an input if it is missing, and control what attributes are being set using the map.
The closest thing to what you are looking for is Mass Assignment, I believe.
There are many different methods and patterns to handle these types of requests, and your approach seems fine to me. I personally use Form Requests + DTO because the code documents itself quite well. As an example:
Controller:
class UsersController extends Controller
{
...
public function store(CreateUserRequest $request)
{
$user = User::create($request->toCommand());
// Return response however you like
}
...
}
FormRequest
class CreateUserRequest extends FormRequest
{
...
public function rules()
{
// Validate all the data here
}
...
public function toCommand() : CreateUserCommand
{
return new CreateUserCommand([
'name' => $this->input('name'),
'birthdate' => Carbon::parse($this->input('birthdate')),
'role' => $this->input('role'),
'gender' => $this->input('gender'),
...
]);
}
}
Command DTO
class CreateUserCommand extends DataTransferObject
{
/** #var string */
public $name;
/** #var \Carbon\Carbon */
public $birthdate;
/** #var string */
public $role = 'employee'; // Sets default to employee
/** #var null|string */
public $gender; // Not required
}
class User extends Model
{
...
protected $fillable = [
'name',
'birthdate',
'role',
'gender',
];
...
public static function create(CreateUserCommand $command)
{
// Whatever logic you need to create a user
return parent::create($command->toArray());
}
}
That is a fairly "Laravel way" of doing things, and the code itself conveys a lot of information to anyone else (and you later :D) who needs to use it.
I have a models with custom rules of validation, In every model I have variable $rules:
public static $rules = [...];
https://medium.com/#konafets/a-better-place-for-your-validation-rules-in-laravel-f5e3f5b7cc
I want use these rules in custom request:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
class ModelRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return Auth::check() ? true : false;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return static::$rules;
}
}
I get error:
Access to undeclared static property: App\Http\Requests\ModelRequest::$rules
In controller:
public function store(ModelRequest $request)
{
...
}
This is globally. I need get instance of model, but how?
If you've put the $rules variable in your model you can't access it like this, because static refers to the class you are currently in.
Try this out:
Notice: I assume that your models are under the App name space
// In your model class
class YourModel extends Model{
const RULES=[];
}
//Then in your request class
class ModelRequest extends FormRequest
{
public function rules()
{
return App\YourModel::RULES;
}
}
How do you inject a Sentry 2 User model into a laravel 4 controller using IoC?
for example i would like the following
class myController extends BaseController {
/**
* creates a list of MyModel Models
*
* #return View
*/
public function getIndex( User $user )
{
// fetch models
$models = MyModel::all();
// Show the page
return View::make('my-views.the-view', compact('models', 'user'));
}
}
This is how I like to do it:
class myController extends BaseController {
$protected $user
/**
* creates a list of MyModel Models
*
* #return View
*/
function __construct(User $user){
parent::__construct();
$this->user = $user;
}
public function getIndex()
{
// fetch models
$models = MyModel::all();
// Show the page
return View::make('my-views.the-view', compact('models', 'user'));
}
}
You can also do it in the method, but... well, give this a good read, too: http://fabien.potencier.org/article/11/what-is-dependency-injection