Inconsistency with 'show' function - laravel

I've been working on a project for a few months now and I feel like I am seeing some inconsistency with how the public function show is working
I have a model and controller for a Location that has
public function show(Location $Location)
{
$Loc = Location::with('company:id,name')->findOrFail($Location);
return response()->json($Loc,200);
}
and that works just fine. Note the parameters.
I just made a new model and controller for Asset and it has this:
public function show(Asset $asset)
{
$AssetReturn = Asset::with('location:id,name,address')->findOrFail($asset);
return response()->json($AssetReturn,200);
}
but that does not work. it just returns empty. If i remove the class name from the parameters so its just
public function show($asset)
then it works as intended.
relation from asset model to location:
public function location()
{
return $this->belongsTo(Location::class);
}

According to the documentation, Laravel automatically resolves Eloquent models defined in routes or controller actions whose type-hinted variable names match a route segment name. For example:
Route::get('/assets/{asset}', function (App\Asset $asset) {
$asset->load('location:id,name,address');
return response()->json($asset);
});
Since the $asset variable is type-hinted as the App\Asset Eloquent model and the variable name matches the {asset} URI segment, Laravel will automatically inject the model instance that has an ID matching the corresponding value from the request URI. If a matching model instance is not found in the database, a 404 HTTP response will automatically be generated.
If you don't want this behavior and want to use findOrFail manually:
Route::get('/assets/{asset}', function ($assetId) {
$asset = App\Asset::with('location:id,name,address')->findOrFail($assetId);
return response()->json($asset);
});

Related

Laravel authorization policy not working on Show page

I have a laravel app using Policies to assign roles and permissions, i cant seem to access the show page and im not sure what im doing wrong?
If i set return true it still shows a 403 error as well, so im unsure where im going wrong here. The index page is accessable but the show page is not?
UserPolicy
public function viewAny(User $user)
{
if ($user->isSuperAdmin() || $user->hasPermissionTo(44, 'web')) {
return true;
}
return false;
}
public function view(User $user, User $model)
{
if ($user->isSuperAdmin() || $user->hasPermissionTo(44, 'web')) {
return true;
}
return false;
}
UserController
public function __construct()
{
$this->authorizeResource(User::class, 'user');
}
public function index()
{
$page_title = 'Users';
$page_description = 'User Profiles';
$users = User::all();
return view('pages.users.users.index', compact('page_title', 'page_description', 'users'));
}
public function create()
{
//
}
public function store(Request $request)
{
//
}
public function show($id)
{
$user = User::findOrFail($id);
$user_roles = $user->getRoleNames()->toArray();
return view('pages.users.users.show', compact('user', 'user_roles'));
}
Base on Authorize Resource and Resource Controller documentation.
You should run php artisan make:policy UserPolicy --model=User. This allows the policy to navigate within the model.
When you use the authorizeResource() function you should implement your condition in the middleware like:
// For Index
Route::get('/users', [UserController::class, 'index'])->middleware('can:viewAny,user');
// For View
Route::get('/users/{user}', [UserController::class, 'view'])->middleware('can:view,user');
or you can also use one policy for both view and index on your controller.
I had an issue with authorizeResource function.
I stuck on failed auth policy error:
This action is unauthorized.
The problem was that I named controller resource/request param with different name than its model class name.
F. ex. my model class name is Acknowledge , but I named param as timelineAcknowledge
Laravel writes in its documentation that
The authorizeResource method accepts the model's class name as its first argument, and the name of the route / request parameter that will contain the model's ID as its second argument
So the second argument had to be request parameter name.
// Here request param name is timelineAcknowledge
public function show(Acknowledge $timelineAcknowledge)
{
return $timelineAcknowledge->toArray();
}
// So I used this naming here also
public function __construct()
{
$this->authorizeResource(Acknowledge::class, 'timelineAcknowledge');
}
Solution was to name request param to the same name as its model class name.
Fixed code example
// I changed param name to the same as its model name
public function show(Acknowledge $acknowledge)
{
return $acknowledge->toArray();
}
// Changed here also
public function __construct()
{
$this->authorizeResource(Acknowledge::class, 'acknowledge');
}
I looked over Laravel policy auth code and I saw that the code actually expects the name to be as the model class name, but I couldn't find it anywhere mentioned in Laravel docs.
Of course in most of the cases request param name is the same as model class name, but I had a different case.
Hope it might help for someone.

How to get result in Laravel with where clause while passing model in controller method

I have a route to get a single post item by slug.
Route
Route::get('post/{post}', 'PostController#details')->name('post.details');
While I want to pass the model in the controller method for the route.
Controller
public function details(Post $post)
{
// how to get the post by slug
}
My question is how can I get the post by slug passing in the route
instead of post ID?
I am aware that I can pass the slug and get the post using where clause.
//Route
Route::get('post/{slug}', 'PostController#details')->name('post.details');
//Controller method
public function details($slug)
{
$post = Post::with('slug', $slug)->first();
}
But I want to learn to do the same by passing the Model in the method.
set route key name to your model class
//Post.php
public function getRouteKeyName()
{
return 'slug';
}
This will inform Laravel injector/resolver to look the variable passed in slug column while fetching the object instance.
What you want to do is implicit route model binding
What you can do is in your Post model define getRouteKeyName like below
<?php
class Post extends Model
{
/**
* Get the route key for the model.
*
* #return string
*/
public function getRouteKeyName()
{
return 'slug';
}
}
and define your route like this:
Route::get('post/{post:slug}', 'PostController#details')->name('post.details');
and then in your controller
public function details(Post $post)
{
// it will return post with slug name
return $post;
}
Hope it helps.
Thanks

How to I specify this route in Laravel?

I want to have a URL like this:
/v1/vacations?country=US&year=2017&month=08
How do I set the route in Laravel 5.3 and where can I put the controller and logic to accept the query string?
your route should look like;
Route::get('v1/vacations', 'VacationsController#index');
then on VacationsController
public function index()
{
dd(request()->query());
$query=request()->query();
//search database using query
//return view with results
}
Query strings can not be defined in your route since the query string is not part of the URI.
To access the query string you should use the request object. $request->query() will return an array of all query parameters. You can also use it as such to return a single query param $request->query('key')
You just would check the Request object for the url parameters like so:
// The route declaration
Route::get('/v1/vacations', 'YourController#method');
// The controller
class YourController extends BaseController {
public function method(Illuminate\Http\Request $request)
{
$country = $request->country;
// do things with them...
}
}
Hope this helps you.

How to improve Laravel code using with operator?

In my request to DB I use Laravel's models and function with like:
$user = User::with("city")->get();
Problem is that if to do the following in template:
{{$user->city()->name}}
It will work only then if user specified city, so then value exists in database table. Otherwise it returns an error:
How to get rid of superfluous checks like:
#if(isset($user->city()->name))
{{$user->city()->name}}
#endif
This is awfully!
When defining the relationship on your model, use the withDefault method:
class User extends Model
{
public function city()
{
return $this->hasOne(City::class)->withDefault();
}
}
With this in place, $user->city will always return a City model.

Laravel 5 ModelNotFoundException in Builder.php for Routing

I have a Model Class with name Article.php
and use below rout:
Route::get('articles/create','ArticlesController#create');
when input in browser http://localhost:8000/articles/create
i see this error :
ModelNotFoundException in Builder.php line 125: No query results for model [App\Article].
but when i user below every think is ok:(article insted articles)
Route::get('article/create','ArticlesController#create');
this is my controller :
class ArticlesController extends Controller {
public function index()
{
$articles = Article::all();
return view('articles.index',compact('articles'));
}
public function show($id)
{
$article = Article::findOrFail($id);
return view('articles.show',compact('article'));
}
public function create()
{
return view('articles.create');
}
}
what happened really ?!!!
The problem with your code is that in your routes.php your route priority is like this :
Route::get('articles/{id}','ArticlesController#show');
Route::get('articles/create','ArticlesController#create');
and when you go to http://localhost:8000/articles/create in your browser laravel catches create as a variable with {id} request in articles/{id} before articles/create gets an opportunity to resolve the route. To solve your problem you must consider the route priority and make the following changes to your route.php file :
Route::get('articles/create','ArticlesController#create');
Route::get('articles/{id}/edit','ArticlesController#show');
Route::get('articles/{id}','ArticlesController#show');
But if you have a bunch of these in your routes.php file you should really consider using this instead:
Route::resource('articles', 'ArticlesController');
This single line will take care of all 4 get routes (index, create, edit, show) as well as all three post/put/delete routes of (store, update, delete).
But to each their own.
You should include your controller code.
Most likely there's some code there that tries a findOrFail() on the Eloquent model, triggering this error.

Resources