basic authorization with livewire and laravel - laravel

I have a method in my postpolicy that only current authenticated user can delete his own posts. I try to put this method in my delete method, but it returns that the method does not exist. I have included authroizerequest in the controller, so I'm confused why I get this error.
Method App\Http\Livewire\Posts::authorize does not exist.
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
public $postId = null;
public function deletePost($id)
{
$this->authorize('delete', $this->postId);
$post = Post::find($id);
Storage::delete('public/photos/', $post->image);
$post->delete();
session()->flash('flash.banner', 'Post Deleted Successfully');
}
the policy:
namespace App\Policies;
use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
public function delete(User $user, Post $post){
return $user->id === $post->user_id;
}

You are just importing use AuthorizesRequests trait not including this trait inside your class you need to use this trait inside your controller
class ControllerName extends Controller {
use AuthorizesRequests;
}
Using this way you can use AuthorizesRequest traits method inside your class.

Related

Laravel route model binding without global scope

I have following route group in my laravel 8.0 app:
Route::prefix('offline_transaction')->name('offline_transaction.')->group(function () {
Route::post('/approve/{transaction:uuid}', [OfflineTransactionController::class, 'approve'])
->name('approve');
Route::post('/reject/{transaction:uuid}', [OfflineTransactionController::class, 'reject'])
->name('reject');
});
And Transaction model is:
class Transaction extends Model implements CreditBlocker
{
//....
protected static function boot()
{
parent::boot();
static::addGlobalScope(new AuthUserScope());
}
//....
}
And this is my AuthUserScope:
class AuthUserScope implements Scope
{
private string $fieldName;
public function __construct($fieldName = 'user_id')
{
$this->fieldName = $fieldName;
}
public function apply(Builder $builder, Model $model)
{
$user = Auth::user();
if ($user) {
$builder->where($this->fieldName, $user->id);
}
}
}
Now the problem is when an admin wants to approve or reject a transaction, 404 Not found error will throws. How can I pass this?
Customizing The Resolution Logic
If you wish to define your own model binding resolution logic, you may
use the Route::bind method. The closure you pass to the bind
method will receive the value of the URI segment and should return the
instance of the class that should be injected into the route. Again,
this customization should take place in the boot method of your
application's RouteServiceProvider:
Solution
What you can do is change the parameter name(s) in your routes/web.php file for the specific route(s).
Route::prefix('offline_transaction')->name('offline_transaction.')->group(function () {
Route::post('/approve/{any_transaction}', [OfflineTransactionController::class, 'approve'])
->name('approve');
Route::post('/reject/{any_transaction}', [OfflineTransactionController::class, 'reject'])
->name('reject');
Note the any_transaction. Change that to whatever naming convention you find most convenient.
Then, in your app/Providers/RouteServiceProvider.php file, change your boot(...) method to something like this:
use App\Models\Transaction;
use Illuminate\Support\Facades\Route;
// ...
public function boot()
{
// ...
Route::bind('any_transaction', function($uuid) {
return Transaction::withoutGlobalScopes()->where('uuid', $uuid)->firstOrFail();
});
// ...
}
// ...
Then in your controller app/Http/Controllers/OfflineTransactionController.php file, access the injected model:
use App\Models\Transaction;
// ...
public function approve(Transaction $any_transaction) {
// ...
}
// ...
Credits: Using Route Model Binding without Global Scope #thomaskim
Addendum
If you would like to remove a specific global scope from the route model bound query, you may use
withoutGlobalScope(AuthUserScope::class) in the boot(...) method of the app/Providers/RouteServiceProvider.php file.
Another approach is that I can use Route::currentRouteNamed in AuthUserScope class as following, which I prefer to use instead of Route::bind:
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
class AuthUserScope implements Scope
{
private string $fieldName;
public function __construct($fieldName = 'user_id')
{
$this->fieldName = $fieldName;
}
public function apply(Builder $builder, Model $model)
{
$user = Auth::user();
if ($user && !Route::currentRouteNamed('admin.*')) {
$builder->where($this->fieldName, $user->id);
}
}
}

Laravel 5.8 add data to a layout variable via controller constructor

I am trying to add data to a layout variable via a controller constructor. The reason I want to do this is because I always need to add categories to the topmenu when this controller is called.
No success so far. I add data to a layout via a view composer like this.
namespace App\Http\ViewComposers;
use Illuminate\View\View;
use App\Menu;
class MenuComposer
{
public function compose(View $view)
{
if (in_array($view->getName(), ['layouts.master', 'layouts.master-post', 'layouts.error']))
{
$menu = Menu::menu('topmenu');
view()->with('topmenu', $menu);
// view()->share('topmenu', $menu); not working either
}
}
}
I want to extend the data in a Controller constructor.
namespace App\Http\Controllers\Post;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\View\View;
class PostController extends Controller {
public function __construct(View $view)
{
$view->offsetGet('topmenu');
// $view->gatherData() not working either
}
Whatever I try, Laravel throws an exception:
Target [Illuminate\Contracts\View\Engine] is not instantiable while building [App\Http\Controllers\Post\PostController, Illuminate\View\View].
What I did in the serviceprovider boot function:
view()->share('topmenu', [
'items' => $newItemsToAdd
]);
In the viewComposer I did:
$extraItems = view()->shared('topmenu');
if (!empty($extraItems)) {
$items = aray_merge($items, $extraItems);
}
}

Laravel: Class not found if it is called from a Trait

After creating several Apps with Laravel and using softDelete properties I realized that methods like destroy(), restore() and kill() are exactly the same among several controllers. Therefore I am trying to put themn in a trait and use it from diferent Controllers.
My code is as follows:
ProfilesController.php
<?php
namespace App\Http\Controllers;
use App\Profile;
class ProfilesController extends Controller
{
public function destroy(Profile $profile)
{
Profile::del($profile, 'profiles');
return redirect()->route('profiles.index');
}
public function trashed()
{
Profile::trash('Profile');
}
}
Profile.php (model)
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Profile extends Model
{
protected $fillable = ['user_id', 'role_id', 'title', 'subtitle', 'slug', 'birthday', 'about'];
use SoftDeletes, Helpers, commonMethods;
public function getRouteKeyName()
{
return 'slug';
}
// ... more code here
}
trait file: commonMethods.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
use App\Profile;
use Session;
trait commonMethods
{
public static function del($element, $page_name)
{
$element->delete();
Session::flash('success', $element . ' successfully deleted!');
}
public static function trash($model)
{
$total = $model::onlyTrashed()->get();
$total_tr = count($total);
$all_tr = $model::all();
return view('partials.templates.trashed', compact('total', 'total_tr', 'all_tr'));
}
// ...more code here
}
The problem:
I try to visit the view "Trashed" that will list all elements "softdeleted" but not "killed", the method.
I pass the $model variable with the method trash($model)
I get the following error:
Class App/Profile does not found. Try to call App/Profile
I have debugged and the $model variable contains exactly what I need, the string 'Profile' which is what I need to build the Query:
$total = Profile::onlyTrashed()->get();
This query works while in the ProfilesController, but does not work while in a trait, since the model class is not found.
Any idea how could I make it work?
I am using Laravel 6.
If you need to use a class as a string you will want to use its full name. 'App\Profile' instead of 'Profile'.
$model = 'Profile';
new $model; // will use `\Profile`
$model = 'App\Profile';
new $model; // will use '\App\Profile';
In your controller( ProfilesController ) write :
use App\Profile;
In your model write :
use App\commonMethods;

Laravel - Custome Auth Throttlelogins

I have created a custom authentication and everything is working fine.
Now I am trying to add the Throttlelogins to prevent multiple incorrect login attempts. But The ThrottleLogins doesn't seem to load.
Q: What am I missing here? or am I doing something wrong?
The exception:
Method
App\Http\Controllers\Auth\CustomersLoginController::hasTooManyLoginAttempts
does not exist.
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Validation\ValidationException;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Auth;
class CustomersLoginController extends Controller
{
public function __construct()
{
$this->middleware('guest:customers');
}
public function ShowLoginForm()
{
return view('auth.customer-login');
}
public function login(Request $request)
{
$v = $request->validate([
'email' => 'required|email',
'password' => 'required',
]);
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if(Auth::guard('customers')->attempt(['email'=>$request->email,'password'=>$request->password],$request->remember)){
return redirect()->intended(route('customerdashboard'));
};
return $this->sendFailedLoginResponse($request);
}
protected function sendFailedLoginResponse(Request $request)
{
throw ValidationException::withMessages([
$this->username() => [trans('auth.failed')],
]);
}
public function username()
{
return 'email';
}
}
Error Message
Can someone please explain what am I mssing?
The error says you are missing a function: hasTooManyLoginAttempts
In the function login you can see it's trying to call the function but it does not exist in your class. This is where it goes wrong.
update
In the AuthenticateUsers class, which you tried to copy, it's using ThrottlesLogins trait, which you are missing in your controller.
Update your controller like so:
class CustomersLoginController extends Controller
{
use ThrottlesLogins;
Another update
You tried to import the Trait which Laravel uses in their own Login. However this will not work here's why:
When you define a class, it can only have access to other classes within its namespaces. Your controller for instance is defined within the following namespace.
namespace App\Http\Controllers\Auth;
So to use other classes, you need to import them from their own namespaces so you can access them. e.g.:
use Illuminate\Foundation\Auth\ThrottlesLogins;
Now that you have imported the ThrottlesLogins, which is actually a trait, now inside the class you use it to expose all of the methods inside.

Argument 1 passed to Illuminate\Database\Eloquent\Relations\HasOneOrMany::save() must be an instance of Illuminate\Database\Eloquent\Model

I am pretty new to Laravel and I am trying to add the post, create by a user into the database. But when I do so, following error comes:
Argument 1 passed to Illuminate\Database\Eloquent\Relations\HasOneOrMany::save()
must be an
instance of Illuminate\Database\Eloquent\Model, string given,
called in C:\xampp\htdocs\lar\app\Http\Controllers\PostController.php on line
25 and defined
User model:
<?php
namespace App;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
class User extends Model implements Authenticatable
{
use \Illuminate\Auth\Authenticatable;
public function posts()
{
return $this->hasMany('App\Post');
}
}
Post Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function user()
{
return $this->belongsTo('App\User') ;
}
}
PostController:
<?php
namespace App\Http\Controllers;
use App\Post;
use Illuminate\Http\Request;
class postController extends Controller
{
public function postCreatePost(Request $request){
// Validation
$post = new Post();
$post->$request['body'];
$request->user()->posts()->save('$post');
return redirect()->route('dashboard');
}
}
Post Route:
Route::post('/createpost',[
'uses' => 'PostController#postCreatePost',
'as'=>'post.create'
]);
Form action:
<form action="{{route('post.create')}}" method="post">
Please tell me how to fix this.. How to fix this?
Thank you in advance.. :)
I think what you want is this:
<?php
namespace App\Http\Controllers;
use App\Post;
use Illuminate\Http\Request;
class postController extends Controller
{
public function postCreatePost(Request $request){
// Validation
$post = new Post();
// here you set the body of the post like that
$post->body = $request->body;
// here you pass the $post object not as string
$request->user()->posts()->save($post);
return redirect()->route('dashboard');
}
}
You need to pass the $post object as an object to the save method. You was doing this: $user->posts()->save('$post') when you need to do this: $user->posts()->save($post).
Hope it helps.

Resources