Laravel API Route controller function parameter - laravel

Using the following route declaration, I am trying to pass a parameter to the controller method however I am unsure of how to do this. I don't want a route parameter but instead a hard coded parameter so that I can use the same function for both updating the current user and other users using a parameter to distinguish between the two operations.
Route::patch('/v1/user', [UserController::class, 'update']);
I need to pass a boolean value to the update method from this Route statement, any ideas?

There are two simple ways to do this
A general method to update the user
public function updateUser($user){...}
Then you can just call this function from the other controllers. This way, your code becomes flexible and easier to test/mock
Make $id as nullable
public function update($id = null)
{
if($id) {
$user = User::find($id);
} else {
$user = auth()->user();
}
// Update user
}

Do not you mean this?
Route::patch('/v1/user/{id}', [UserController::class, 'update']);

Related

Custom route for edit method without ID in URL

I want to make a settings screen for my users, and I want the URL to be easy an memorable: domain.com/user/settings
I want this route to point use the UserController#edit method, however, the edit() method requires ID parameter.
Is there someway I can use user/settings URL without specifying the ID in the URL, but still use the same edit() method in the UserController?
The edit URL could be use without any parameter. In that case you can mention the user in controller.
Route
Route::get('user/settings');
Controller
public function edit()
{
$user = Auth::user();
}
Seems like I managed to solve it?
web.php:
Route::get('user/settings', 'UserController#edit')->name('user.settings');
Route::resource('user', 'UserController');
UserController.php:
public function edit(User $user = null)
{
$user = Auth::user();
}

How to pass multiple string variables to a Laravel Gate

I have a “security” service which I want to gradually move over to a Laravel Gate, so I can benefit from the helper methods that Laravel provides within the rest of the APP.
I defined the gate as follows now:
Gate::define('denja', function($user, $module, $permission) {
// validation of access to $module and $permission goes here
});
This works fine when I do
$user->can('denja', ['accounting', 'invoice.create']);```
for instance, but I don’t see how in my routes, I can define the middleware to properly function...
Route::post( '/accounting/invoices', 'InvoiceController#create')
->middleware("can:denja,accounting,invoice.create");```
Passing these parameters seems to be impossible from the middleware - the page now always returns a 403...
Any thoughts on how I can pass these parameters correctly to the gate from the Middleware? I think it's in fact a problem with the parameters; even with a dd() in the defined gate, I'm getting the 403.
I know I’m a bit “abusing” the system, but since we have an existing service that basically expects a user, module and permission under that module, I just want to delegate to that service for now...
When you are using can middleware :
The first is the name of the action we wish to authorise and the later is the route parameter we wish to pass to the policy method or a Model class path. documentation
For example :
Route::put('/post/{postId}', function (Post $post) {
// The current user may update the post...
})->middleware('can:update,postId');
OR
Route::post('/post', function () {
// The current user may create posts...
})->middleware('can:create,App\Post');
In your case :
Route::post( '/accounting/invoices', 'InvoiceController#create')
->middleware("can:denja,accounting,invoice.create");
which is missing the basic parameter signatures as there is no route param with name accounting or invoice.create nor a class.
Solution :
Remove middleware from route declaration :
Route::post( '/accounting/invoices', 'InvoiceController#create');
You can use can() method in your controller :
public function create(Request $request){
// Initialize $model and $permissions
// as per your business logic
if(!$request->user()->can('denja', $module, $permission){
abort(403);
}
// continue your logic for authorised user
}
Even if above solution works, if you have more authorisation rules, its better to make a policy class.
I to had this same problem so I did some digging into the 'can' middleware (Which maps to Illuminate\Auth\Middleware\Authorize)
Once in the class we see the following code
/**
* Get the model to authorize.
*
* #param \Illuminate\Http\Request $request
* #param string $model
* #return \Illuminate\Database\Eloquent\Model|string
*/
protected function getModel($request, $model)
{
if ($this->isClassName($model)) {
return trim($model);
} else {
return $request->route($model, null) ?:
((preg_match("/^['\"](.*)['\"]$/", trim($model), $matches)) ? $matches[1] : null);
}
}
What this means is...
If our string passed in is a class name then return that class name
If it is not a class name then...
1) Try to get it from the route, then return the route param
2) Try to get the model from the string via the regex "/^['\"](.*)['\"]$/"
So now lets say we have the middleware call of
$this->middleware(sprintf("can:create,%s,%s", User::class, Role::SUPPORT));
This will not work because the Role::SUPPORT does not match the regex
To match it we simply need to place the Role::SUPPORT into quotes.
TAKE NOTE OF THE "'" around the second %s
$this->middleware(sprintf("can:create,%s,'%s'", User::class, Role::SUPPORT));
To answer your question specifically, quote your string
Route::post('/accounting/invoices', 'InvoiceController#create')
->middleware("can:'denja','accounting','invoice.create'");

Laravel nova resource extending/overriding the create method

I am developing a web admin panel using Laravel Nova.
I am having an issue since Nova is quite a new technology.
What I would like to do now is I would like to add a hidden field or extend or override the create method.
This is my scenario. Let's say I have a vacancy nova resource with the following field.
public function fields(Request $request)
{
return [
ID::make()->sortable(),
Text::make('Title')->sortable(),
Text::make('Salary')->sortable()
// I will have another field, called created_by
];
}
Very simple. What I like to do is I want to add a new field called created_by into the database. Then that field will be auto filled with the current logged user id ($request->user()->id).
How can I override or extend the create function of Nova? How can I achieve it?
I can use resource event, but how can I retrieve the logged in user in
the event?
What you're looking for is Resource Events.
From the docs:
All Nova operations use the typical save, delete, forceDelete, restore Eloquent methods you are familiar with. Therefore, it is easy to listen for model events triggered by Nova and react to them.
The easiest approach is to simply attach a model observer to a model:
If you don't feel like creating a new observable you could also create a boot method in your eloquent model as so:
public static function boot()
{
parent::boot();
static::creating(function ($vacancy) {
$vacancy->created_by = auth()->user()->id;
});
}
But please do note that these are a bit harder to track than observables, and you or a next developer in the future might be scratching their head, wondering how's the "created_at" property set.
In my opinion you should go for Observers. Observers will make you code more readable and trackable.
Here is how you can achieve the same with Laravel Observers.
AppServiceProvider.php
public function boot()
{
Nova::serving(function () {
Post::observe(PostObserver::class);
});
}
PostObserver.php
public function creating(Post $post)
{
$post->created_by = Auth::user()->id;
}
OR
You can simply hack a Nova field using withMeta.
Text::make('created_by')->withMeta([
'type' => 'hidden',
'value' => Auth::user()->id
])
You could also do that directly within your Nova resource. Every Nova resource has newModel() method which is called when resource loads fresh instance of your model from db. You can override it and put there your logic for setting any default values (you should always check if values already exist, and only set if they are null, which will only be the case when the model is being created for the first time, which is what you actually need):
public static function newModel()
{
$model = static::$model;
$instance = new $model;
if ($instance->created_by == null) {
$instance->created_by = auth()->user()->id;
}
return $instance;
}
a) Create an Observer class with following command:
php artisan make:observer -m "Post" PostObserver
b) Add following code in the PostObserver:
$post->created_by = Auth::user()->id;
c) Register PostObserver in AppServiceProvider.php
For detailed explanation: https://medium.com/vineeth-vijayan/how-to-add-a-new-field-in-laravel-nova-resource-87f79427d38c
Since Nova v3.0, there is a native Hidden field.
Usage:
Hidden::make('Created By', 'created_by')
->default(
function ($request) {
return $request->user()->id;
}),
Docs: https://nova.laravel.com/docs/3.0/resources/fields.html#hidden-field

Laravel 5.4 Sessions and Auth::user() not available in controller's constructor

I would like to use a User class throught the application. So, I would like to create CustomUser and then inject it into controllers that need it (it would be most of them).
Now, I create an empty instance in serviceprovider. Next, I want to fill it with data that are already saved in Auth::user(). After long time I have not found where to do it.
Auth::user() is empty in middlewares, but is filled with the user data in controllers. I am missing the step where Laravel queries the database and fills Auth:user() with data. I want to avoid making the same query again.
Thanks for any help!
You can use base controller with __get() method. For example:
class Controller
{
public function __get(string $name)
{
if($name === 'user'){
return Auth::user();
}
return null;
}
}
And in the child controllers can call $this->user
Since Laravel 5.3, you do not have access to sessions in controller constructors. This is because the middleware has not been run yet. I know it's difficult to locate, but in the migration documentation from 5.2 > 5.3 (you're probably on 5.4), it shows that the proper way to resolve data from sessions (which auth() is just a wrapper around a session() call to get the user), is to use the following method:
class MyController extends Controller {
protected $user;
public function __construct() {
$this->middleware(function ($request, $next) {
$this->user= auth()->user();
return $next($request);
});
}
}
Then $this->user will reference the auth user to any methods inside of this controller.
Hopefully his helps.
In Laravel 5.6 i used this
$this->middleware(function ($request, $next) {
$id = Auth::user()->id;
$res = $this->validateAnyFunction($id);
if(!$res){
//to redirect to any other route
return $next(redirect()->route("any")->with("failed","Invalid")->send());
}
//this is used to proccess futher funcitons of controller
return $next($request);
});

Passing page URL parameter to controller in Laravel 5.2

In my application I have a page it called index.blade, with route /index. In its URL, it has some get parameter like ?order and ?type.
I want to pass these $_get parameter to my route controller action, query from DB and pass its result data to the index page. What should I do?
If you want to access the data sent from get or post request use
public function store(Request $request)
{
$order = $request->input('order');
$type = $request->input('type');
return view('whatever')->with('order', $order)->with('type', $type);
}
you can also use wildcards.
Exemple link
website.dev/user/potato
Route
Route::put('user/{name}', 'UserController#show');
Controller
public function update($name)
{
User::where('name', $name)->first();
return view('test')->with('user', $user);
}
Check the Laravel Docs Requests.
For those who need to pass part of a url as a parameter (tested in laravel 6.x, maybe it works on laravel 5.x):
Route
Route::get('foo/{bar}', 'FooController#getFoo')->where('bar', '(.*)');
Controller:
class FooController extends Controller
{
public function getFoo($url){
return $url;
}
}
Test 1:
localhost/api/foo/path1/path2/file.gif will send to controller and return:
path1/path2/file.gif
Test 2:
localhost/api/foo/path1/path2/path3/file.doc will send to controller and return:
path1/path2/path3/file.doc
and so on...

Resources