Route model binding issue - php

I have a set of code, it is similar to the other codes that I'm using and they are working fine. Just in this case there is some mysterious issue which I'm not able to find the cause of. Please see the code below
BlogPostController.php
public function category(Category $category){
return view('blog/cat')->with('categories',$category);
}
categories.blade.php
#extends('layouts.blog')
{β€Œ{$categories->name}}
The category.blade does not output {β€Œ{$categories->name}} . No errors are shown. If I change the {β€Œ{$categories->name}} and type normal text e.g data , then data is printed on the webpage . I even tried restarting my system. There is no way out.
The I removed the Model Route Binding, and tried the usual way ,
public function category($id){
$category = Category::where('id',$id)->first();
return view('blog/cat')->with('categories',$category);
}
EDIT
ROUTE - web.php
Route::get('blog/category/{cat}','BlogPostController#category')->name('blog.category');
In this case the category.blade.php prints the data properly.
What could be the issue with the Model Route Binding in this case. All my controllers use Model Route Binding rather than the usual way, but this is the first time I'm stumbling upon this issue.

From: laravel.com/docs/5.8/routing#route-model-binding
Implicit Binding
Laravel automatically resolves Eloquent models defined in routes or controller actions whose type-hinted variable names match a route segment name.
So try to do:
Route::get('blog/category/{category}','BlogPostController#category')->name('blog.category');
Explicit Binding
To register an explicit binding, use the router's model method to specify the class for a given parameter. You should define your explicit model bindings in the boot method of the RouteServiceProvider class
Or use Explicit Binding
RouteServiceProvider.php
public function boot()
{
parent::boot();
Route::model('cat', App\Category::class);
}
And you can still use:
Route::get('blog/category/{cat}','BlogPostController#category')->name('blog.category');

https://laravel.com/docs/5.5/routing#implicit-binding
Route::get('blog/category/{category}','BlogPostController#category')->name('blog.category');

Related

Routes inside controllers with laravel?

I have been declaring all the routes for my application inside web.php , but it is now getting quite large. I find that I am losing a lot of time shifting between web.php and each controller and this is hurting productivity.
I feel like it would be better to define routes inside of the controller, perhaps ideally delegating some URL to a controller and then allowing the controller to handle the "sub routes" since this would allow me to use inheritance when I have two similar controllers with similar routes.
It is not possible given how laravel works. Every request is passed onto router to find its designated spot viz. the controller with the method. If it fails to find the route within the router, it just throws the exception. So the request never reaches any controller if the route is not found. It was possible in earlier versions on Symphony where you would configure the route in the comment of a particular controller method.
Sadly with laravel it works how it works.
But for me, I just like to have the routes in a separate file.
Alternate solution, easier way to sort all the routes.
You can move your route registration into controllers if you use static methods for this. The code below is checked in Laravel 7
In web.php
use App\Http\Controllers\MyController;
.....
MyController::registerRoutes('myprefix');
In MyController.php
(I use here additional static methods from the ancestor controller also posted below)
use Illuminate\Support\Facades\Route;
.....
class MyController extends Controller {
......
static public function registerRoutes($prefix)
{
Route::group(['prefix' => $prefix], function () {
Route::any("/foo/{$id}", self::selfRouteName("fooAction"));
Route::resource($prefix, self::selfQualifiedPath());
}
public function fooAction($id)
{
........
}
In Controller.php
class Controller extends BaseController {
....
protected static function selfAction($actionName, $parameters = [], $absolute = false)
{
return action([static::class, $actionName], $parameters, $absolute);
}
protected static function selfQualifiedPath()
{
return "\\".static::class;
}
protected static function selfRouteName($actionName)
{
//classic string syntax return "\\".static::class."#".$actionName;
// using tuple syntax for clarity
return [static::class, $actionName];
}
}
selfAction mentioned here is not related to your question, but mentioned just because it allows making correct urls for actions either by controller itself or any class using it. This approach helps making action-related activity closer to the controller and avoiding manual url-making. I even prefer making specific functions per action, so for example for fooAction
static public function fooActionUrl($id)
{
return self::selfAction('foo', ['id' => $id]);
}
Passing prefix into registerRoutes makes controller even portable in a sense, so allows inserting it into another site with a different prefix in case of conflict

Laravel , how to call a function from another controller

I have a controller with the "getUsers" function in a controller called "UserController" , and inside it I want to call a function of the "CarController" controller called "getCars", the two options I have are:
a) Make the second call as "static" , then I can call it without instantiating the class
b) Do not do that function of the static class and I call it in this way
$ car_id = 100;
$ userController = new UserController ();
$ userController-> getCars ($ car_id);
I do not know which is the best practice, or what pros or cons has one or another.
I'm using laravel.
Thanxs.
It is a bad practice to call a controller from another controller, this usually signals that you have badly designed your code and you should think of a different way to achieve what you want.
None the less, you can do it like this:
app()->call('App\Http\Controllers\CarController#getCars');
If your controller method has parameters you can pass them as the second argument:
app()->call('App\Http\Controllers\CarController#getCars', [$param1, $param2]);
To answer your question, you should not call one controller method from another. As #elfu mentioned, this is not the intended functionality of a controller anyway. His post is correct and in your case you should probably use the User model as the location of this method, but I thought I'd at to it a little.
If you do want to share methods between multiple controllers, a good place to do this is through a Trait. In some cases, you are not referencing a model that is shared between controllers, and a Trait would be your best option.
To include a trait, you can reference it by including it at the top of your controller and then with a 'use' statement after the class declaration for the controller. Here is an example:
use App\Traits\ExampleTrait;
class CarController extends Controller
{
use ExampleTrait;
...
You would do the same in the UserController. Then, any method that you place in the ExampleTrait will be directly accessible from the CarController and the UserController by referencing it as $this->methodName(), just like referencing any other method in the same controller.
In your particular case, I would say that your logic should probably be stored in the User model, since the cars for a user are really an ATTRIBUTE of the User model, but the above gives you another option to work with.
In my humble opinion you should not call another controller in a controller.
It looks like you have some business logic in that controller. So you should move your logic to the entity (User.php) and call it in both controllers methods.
A regular controller returns a view (at least that is what is expected), so if you want to call another controller you should just send that route to that method (in web.php file) instead of calling it in another controller.
Hope that helps you.
You can call one controller function from another but the best way is to create a trait and use it both the controllers like:
trait Common
{
public function method(){}
}
class FirstController extends Controller
{
use Common;
}
class SecondController extends Controller
{
use Common;
}
If you want to bind parameters to the call, you can use:
$videos = app()->call('App\Http\Controllers\StorageController#returnViewVideo',[
'course'=>$course,
'lesson'=>$lesson,
]);
The following code worked for me well. and also it also can be used in routes.php
public function mobileImageUpload(Request $request){
$this->validate($request,[
'data'=>'required',
'filetype'=>'required',
'userid'=>'required',
]);
$namespace = 'App\Http\Controllers';
$controller = app()->make($namespace.'\ImageController');
return $controller->callAction('mobileImageUpload',[$request]);
}

Laravel boilerplate send object to controller

I am newbie with Laravel. I have just fork laravel 5 boilerplate from https://github.com/rappasoft/laravel-5-boilerplate.
In route files, i see that there is a line like that :
Route::group(['prefix' => 'user/{deletedUser}'], function () {
Route::get('delete', 'UserStatusController#delete')->name('user.delete-permanently');
Route::get('restore', 'UserStatusController#restore')->name('user.restore');
});
I understand it means that, when url catch 'restore' it will use function restore in UserStatusController.
And here it is:
public function restore(User $deletedUser, ManageUserRequest $request)
Can anybody can help me to find out that, how can it send object $deletedUser to restore function. Tks you!
If your look at the route definition:
user/{deletedUser}
That {deletedUser} represents the id of the user to be deleted/restored. Variables are declared between {} in routes as the docs states.
Now in your controller:
public function restore(User $deletedUser, ManageUserRequest $request)
You can see that a User object is declared as an argument. This object is being injected by Laravel, that automatically will look for an User object that has that id. This is called Route Model Binding.
The documentation explains it better:
When injecting a model ID to a route or controller action, you will often query to retrieve the model that corresponds to that ID. Laravel route model binding provides a convenient way to automatically inject the model instances directly into your routes. For example, instead of injecting a user's ID, you can inject the entire User model instance that matches the given ID.
The same way, the Request class injected in this case is a ManageUserRequest that should be an instance of a FormRequest.
So, returning to your question, you will just have to specify the user id that you want to delete/restore, like this:
someurl.dev/users/5 // <-- for the user of id=5
Now your controller will interact with that specific object to do what you want:
public function restore(User $deletedUser, ManageUserRequest $request)
{
$deletedUser->delete(); // for example
}
There are two things happening here: parameters (docs) and model binding (docs)
First of all, in ['prefix' => 'user/{deletedUser}'] you can see that you are parsing a parameter from the url. This way, when someone navigates to api/user/3, laravel will pass the 3 to your route handler.
Second, it would be very nice to get the User model instance instead of just getting an id number. That's possible and it's called "model binding". Model binding can be
Explicit
You add your bindings to boot method in your RouteServiceProvider class, telling laravel what is the expected type of parameter.
public function boot()
{
parent::boot();
Route::model('deletedUser', App\User::class);
// in older docs I've seen 'App\User' passed as a string instead of as a class
}
Implicit
Laravel automatically figures out what model you need based on type hints.
public function restore(User $deletedUser, ManageUserRequest $request) {}
Here, $deletedUser has is type hinted as User. Laravel sees this, so it will go ahead and convert the id to the Eloquent model for you.
You seem to be using implicit binding, but feel free to check your RouteServiceProvider class.
Check the documentation links for more details, it's pretty well written. (If you are not using version 5.6, then just change the version number in the links).
You Just need to pass ID of the user as a parameter.
And this function
public function restore(User $deletedUser, ManageUserRequest $request)
you can see $deletedUser is of type User Laravel will search for that id ($deletedUser) in Users table and return an object of that user.
If you don't want User object and just need ID that you are passing in URL update restore() function to
public function restore($deletedUser, ManageUserRequest $request)

Same Laravel resource controller for multiple routes

I am trying to use a trait as a typehint for my Laravel resource controllers.
The controller method:
public function store(CreateCommentRequest $request, Commentable $commentable)
In which the Commentable is the trait typehint which my Eloquent models use.
The Commentable trait looks like this:
namespace App\Models\Morphs;
use App\Comment;
trait Commentable
{
/**
* Get the model's comments.
*
* #return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function Comments()
{
return $this->morphMany(Comment::class, 'commentable')->orderBy('created_at', 'DESC');
}
}
In my routing, I have:
Route::resource('order.comment', 'CommentController')
Route::resource('fulfillments.comment', 'CommentController')
Both orders and fulfillments can have comments and so they use the same controller since the code would be the same.
However, when I post to order/{order}/comment, I get the following error:
Illuminate\Contracts\Container\BindingResolutionException
Target [App\Models\Morphs\Commentable] is not instantiable.
Is this possible at all?
So you want to avoid duplicate code for both order and fulfillment resource controllers and be a bit DRY. Good.
Traits cannot be typehinted
As Matthew stated, you can't typehint traits and that's the reason you're getting the binding resolution error. Other than that, even if it was typehintable, the container would be confused which model it should instantiate as there are two Commentable models available. But, we'll get to it later.
Interfaces alongside traits
It's often a good practice to have an interface to accompany a trait. Besides the fact that interfaces can be typehinted, you're adhering to the Interface Segregation principle which, "if needed", is a good practice.
interface Commentable
{
public function comments();
}
class Order extends Model implements Commentable
{
use Commentable;
// ...
}
Now that it's typehintable. Let's get to the container confusion issue.
Contexual binding
Laravel's container supports contextual binding. That's the ability to explicitly tell it when and how to resolve an abstract to a concrete.
The only distinguishing factor you got for your controllers, is the route. We need to build upon that. Something along the lines of:
# AppServiceProvider::register()
$this->app
->when(CommentController::class)
->needs(Commentable::class)
->give(function ($container, $params) {
// Since you're probably utilizing Laravel's route model binding,
// we need to resolve the model associated with the passed ID using
// the `findOrFail`, instead of just newing up an empty instance.
// Assuming this route pattern: "order|fullfilment/{id}/comment/{id}"
$id = (int) $this->app->request->segment(2);
return $this->app->request->segment(1) === 'order'
? Order::findOrFail($id)
: Fulfillment::findOrFail($id);
});
You're basically telling the container when the CommentController requires a Commentable instance, first check out the route and then instantiate the correct commentable model.
Non-contextual binding will do as well:
# AppServiceProvider::register()
$this->app->bind(Commentable::class, function ($container, $params) {
$id = (int) $this->app->request->segment(2);
return $this->app->request->segment(1) === 'order'
? Order::findOrFail($id)
: Fulfillment::findOrFail($id);
});
Wrong tool
We've just eliminated duplicate controller code by introducing unnecessary complexity which is as worse as that. πŸ‘
Even though it works, it's complex, not maintainable, non-generic and worst of all, dependent to the URL. It's using the wrong tool for the job and is plain wrong.
Inheritance
The right tool to eliminate these kinda problems is simply inheritance. Introduce an abstract base comment controller class and extend two shallow ones from it.
# App\Http\Controllers\CommentController
abstract class CommentController extends Controller
{
public function store(CreateCommentRequest $request, Commentable $commentable) {
// ...
}
// All other common methods here...
}
# App\Http\Controllers\OrderCommentController
class OrderCommentController extends CommentController
{
public function store(CreateCommentRequest $request, Order $commentable) {
return parent::store($commentable);
}
}
# App\Http\Controllers\FulfillmentCommentController
class FulfillmentCommentController extends CommentController
{
public function store(CreateCommentRequest $request, Fulfillment $commentable) {
return parent::store($commentable);
}
}
# Routes
Route::resource('order.comment', 'OrderCommentController');
Route::resource('fulfillments.comment', 'FulfillCommentController');
Simple, flexible and maintainable.
Arrrgh, wrong language
Not so fast:
Declaration of OrderCommentController::store(CreateCommentRequest $request, Order $commentable) should be compatible with CommentController::store(CreateCommentRequest $request, Commentable $commentable).
Even though overriding method parameters works in the constructors just fine, it simply does not for other methods! Constructors are special cases.
We could just drop the typehints in both parent and child classes and go on with our lives with plain IDs. But in that case, as Laravel's implicit model binding only works with typehints, there won't be any automatic model loading for our controllers.
Ok, maybe in a better world.
πŸŽ‰Update: See PHP 7.4's support for type variance πŸŽ‰
Explicit route model binding
So what we gonna do?
If we explicitly tell the router how to load our Commentable models, we can just use the lone CommentController class. Laravel's explicit model binding works by mapping route placeholders (e.g. {order}) to model classes or custom resolution logics. So, while we're using our single CommentController we can utilize separate models or resolution logics for orders and fulfillments based on their route placeholders. So, we drop the typehint and rely on the placeholder.
For resource controllers, the placeholder name depends on the first parameter you pass to the Route::resource method. Just do a artisan route:list to find out.
Ok, let's do it:
# App\Providers\RouteServiceProvider::boot()
public function boot()
{
// Map `{order}` route placeholder to the \App\Order model
$this->app->router->model('order', \App\Order::class);
// Map `{fulfillment}` to the \App\Fulfilment model
$this->app->router->model('fulfillment', \App\Fulfilment::class);
parent::boot();
}
Your controller code would be:
# App\Http\Controllers\CommentController
class CommentController extends Controller
{
// Note that we have dropped the typehint here:
public function store(CreateCommentRequest $request, $commentable) {
// $commentable is either an \App\Order or a \App\Fulfillment
}
// Drop the typehint from other methods as well.
}
And the route definitions remain the same.
It's better than the first solution, as it does not rely on the URL segments which are prone to change contrary to the route placeholders which rarely change. It's also generic as all {order}s will be resolved to \App\Order model and all {fulfillment}s to the App\Fulfillment.
We could alter the first solution to utilize route parameters instead of URL segments. But there's no reason to do it manually when Laravel has provided it to us.
Yeah, I know, I don't feel good, too.
You can't typehint traits.
However, you can typehint interfaces. So you can create an interface that requires the methods from the trait and resolve that. Then have your classes implement that interface and you should be OK.
EDIT: As #Stefan has kindly pointed out, it's still likely to be difficult to resolve the interface to a concrete class because it will need to resolve to different classes under different circumstances. You could access the request in the service provider and use the path to determine how to resolve it, but I'm a bit dubious of that. I think putting them in separate controllers and using inheritance/traits to share common functionality may be a better bet, since the methods in each controller can type hint the required object, and then pass them to the equivalent parent method.
For my case I have following resources:
Route::resource('books/storybooks', 'BookController');
Route::resource('books/magazines', 'BookController');
After php artisan route:cache and it creates the route to tie up with 'magazine' model.
The solution is to add following line in app/Providers/RouteServiceProvider.php > boot() method, after parent::boot():
Route::model('magazine', \App\Book::class);
Pay attention to the singular and plural.

Laravel 5.1 - View variables(specifically current controller name) for a controller

What I wanna do is to know, inside a view, if I'm in a specific controller or not. From what I know, I've got two choices and I don't have the answer to either of them :-D
inject a view variable using the share method in my AppServiceProvider, which involves getting the current controller name(or at least the action name so that I can switch it) inside the service provider.
inject a variable to all the views returned in the controller. For example does controllers have a boot method? Or can I override the view() method in the following code snippet?
public function someAction(Request $request)
{
return view('someview', ['myvar' => $myvalue]);
}
well of course there's the easy (yet not easy :|) solution: add the variable in all methods of the controller. I don't like this one.
Thanks
You could use the controller's construct function.
Add this to the top of your controller:
public function __construct()
{
view()->share('key', 'value');
}

Categories