Dynamically Map Routes in Laravel - laravel

Are there any solutions to make Laravel routes dynamically call the controller and action? I couldn't find anything in the documentation.
<?php
Route::get('/{controller}/{action}',
function ($controller, $action) {
})
->where('controller', '.*')
->where('action', '.*');

Laravel does not have an out of the box implementation that automatically maps routes to controller/actions. But if you really want this, it is not that hard to make a simple implementation.
For example:
Route::get('/{controller}/{action}', function ($controller,$action) {
return resolve("\\App\\Http\Controllers\\{$controller}Controller")->$action();
})->where('controller', '.*')->where('action', '.*');
Keep in mind, this example will not automatically inject objects in your action and url parameters are also not injected. You will have to write a bit more code to do this.

Related

Custom Routes not working - 404 not found

I'm trying to create a custom route. The must be in this format: http://localhost:8000/home-back-to-school but instead I get a 404 not found error. http://localhost:8000/posts/home-back-to-school works, but that's not what I'm trying to get working.
My routes on web.php are defined as: Route::resource('posts',PostsController::class);
I modified the Route Service Provider by adding the code below:
parent::boot();
Route::bind('post',function($slug){
return Post::published()->where('slug',$slug)->first();
});
The published scope is defined in the Post Model file(Post.php) as:
public function scopePublished()
{
return $this->where('published_at','<=',today())->orderBy('published_at', 'desc');
}
I've done previously with laravel 5.x, now struggling with laravel 8.x
Link to the Documentation: Laravel 8 Documentation
You should define a custom route since you don't want to use the resourceful route for this method.
In your web.php
// Keep all your resource routes except the 'show' route since you want to customize it
Route::resource('posts', PostsController::class)->except(['show']);
// Define a custom route for the show controller method
Route::get('{slug}', PostsController::class)->name('posts.show');
In your PostController:
public function show(Post $post)
{
return view('posts.show', compact('post'));
}
In your Post model:
// Tell Laravel your model key isn't the default ID anymore since you want to use the slug
public function getRouteKeyName()
{
return 'slug';
}
You may have to fix your other Post routes to make them work with this change since you are now using $post->slug instead of $post->id as the model key.
Read more about customizing the model key:
https://laravel.com/docs/8.x/routing#customizing-the-default-key-name
You should also remove the code you have in the boot method and use the controller instead.
Finally, make sure your post slug is always unique for obvious reason.
Note:
You may run into problems if your other routes are not related to the Post model.
Imagine if you have a route called example.com/contact-us. Laravel has no way to "guess" if this route should be sent to the PostController or the ContactController. contact-us could be a Post slug or it could be a static route to your contact page. That's why it's generally a good idea to start your urls with the model name. In your case, it would be a good idea for your Post route to start with "/posts/" like this: http://example.com/posts/your-post-slug. Otherwise you may run into all sorts unexpected routing issues.
Don't fight the framework: Always follow best practices and naming conventions when you can.

Creating a route to custom function in controller in Laravel

I still can't understand why I can't point my blade to the custom function I made in my controller. I create a route like this,
Route::get('/orders/storeInitialItems', 'OrdersController#storeInitialItems')->name('orders.storeInitialItems');
and in my controller I have this,
public function storeInitialItems()
{
return view('orders.storeInitialItems');
}
but when I run the page, storeInitialItems.blade.php, the error seems calling the show() function of my controller.
Why is that happening?
update
Complete routes for ORDERS
Route::get('/orders','OrdersController#index')->name('orders.index');
Route::get('/orders/create', 'OrdersController#create')->name('orders.create');
Route::post('/orders', 'OrdersController#store')->name('orders.store');
Route::get('/orders/{order}/edit', 'OrdersController#edit')->name('orders.edit');
Route::post('/orders/{order}', 'OrdersController#update')->name('orders.update');
Route::delete('/orders/{order}', 'OrdersController#destroy')->name('orders.delete');
Route::resource('orders', 'OrdersController');
Route::put('orders/{order}/pub', 'OrdersController#publish')->name('orders.publish');
Route::put('orders/{order}/cancel', 'OrdersController#cancel')->name('orders.cancel');
Route::put('orders/{order}/delivered', 'OrdersController#delivered')->name('orders.delivered');
Route::get('/orders/storeInitialItems', 'OrdersController#storeInitialItems')->name('orders.storeInitialItems');
Route::get('/orders/{order}/delivery', 'OrdersController#viewdeliveryItems')->name('orders.delivery');
Route::get('/orders/acceptDelivery', 'OrdersController#acceptDelivery')->name('orders.acceptDelivery');
Add your orders.storeInitialItems route
Route::get('/orders/storeInitialItems', 'OrdersController#storeInitialItems')->name('orders.storeInitialItems');
before,
Route::resource('orders', 'OrdersController');
or add some extra path with your storeInitialItems
Route::get('/orders/storeInitialItems/add-some-extra-path', 'OrdersController#storeInitialItems')->name('orders.storeInitialItems');

Route model binding route with hash and slug parameters

Say I have some routes:
Route::get('items', 'ItemController#index')->name('item.index');
Route::get('items/{hash}/{slug}', 'ItemController#show')->name('item.show');
While using route model binding I want to handle the following cases:
/items/<correct_hash>/<incorrect_slug> permanent redirect to correct hash and slug
/items/<correct_hash> permanent redirect to correct hash and slug
Anything else redirect to /items/
I currently understand the shortcut benefit of route model binding which reduces controller code but is typically used for a simple /items/{id} cases. Is it possible to extend what is shown in the documentation for my case? Or should I scrap the entire model binding approach and go back to caveman controller logic?
Currently it seems the documentation can only bind one parameter at a time not a combination:
Route::bind('user', function ($value) {
return App\User::where('name', $value)->first() ?? abort(404);
});

Laravel Routes and 'Class#Method' notation - how to pass parameters in URL to method

I am new to Laravel so am uncertain of the notation. The Laravel documentation shows you can pass a parameter this way:
Route::get('user/{id}', function ($id) {
return 'User '.$id;
});
Which is very intuitive. Here is some existing coding in a project I'm undertaking, found in routes.php:
Route::get('geolocate', 'Api\CountriesController#geolocate');
# which of course will call geolocate() in that class
And here is the code I want to pass a variable to:
Route::get('feed/{identifier}', 'Api\FeedController#feed');
The question being, how do I pass $identifier to the class as:
feed($identifier)
Thanks!
Also one further sequitir question from this, how would I notate {identifier} so that it is optional, i.e. simply /feed/ would match this route?
You should first create a link which looks like:
/feed/123
Then, in your controller the method would look like this:
feed($identifier)
{
// Do something with $identifier
}
Laravel is smart enough to map router parameters to controller method arguments, which is nice!
Alternatively, you could use the Request object to return the identifier value like this:
feed()
{
Request::get('identifier');
}
Both methods have their merits, I'd personally use the first example for grabbing one or two router parameters and the second example for when I need to do more complicated things.

How to share object in blade template across Zizaco/Confide routes?

I'm trying to share an object across a Laravel application. I need this because I want to create a blade template which will be included everywhere and will also perform some logic/data manipulation (a dynamic menu sort of speak).
To be able to accomplish this I've created a constructor in the Base controller and used View::share facade.
While this works across all routes in the application, it's not working for Zizaco/Confide generated routes, where I get Undefined variable error for $books.
This is the constructor in the base controller:
public function __construct()
{
$books = Book::all();
View::share('books', $books);
return View::make('adminMenu')->with('books', $books);
}
What you need are View Composers!!
You can hook a view composer to a certain view name or pattern (using * wildcard). Every time before that view gets rendered the view composer will run.
You can put this anywhere. Most elegant would be a custom app/composers.php which is then required at the bottom of app/start/global.php
View::composer('adminMenu', function($view){
$books = Book::all();
$view->with('books', $books);
}

Resources