Laravel routes prefix and ressources resulting in 404 not found - laravel

I've this Laravel routing issue. I don't really understand why my route:list tells me that an {} empty is appended to the URL? I believe this is the reason why my calls returns 404 not found.
I'd like DepartmentController to be inside my grouped body as I need the ID for other purposes. If I move ressource outside the prefix/group this scenarie works, but the other dosen't. This is my preferred way of structuring my routes, but it troubles me that / dosen't just use the prefixed URL, but it for some reason append it with {}
What am I doing wrong?
Calling URL: /department/1/edit
Result: 404 Not Found
Routes:
Route::prefix( 'department/{department_id?}' )->group( function () {
Route::resource( '/', 'DepartmentController' );
}
php artisan route:list:
| | GET|HEAD | department/{department_id?}/{} | show | App\Http\Controllers\DepartmentController#show | web |
| | PUT|PATCH | department/{department_id?}/{} | update | App\Http\Controllers\DepartmentController#update | web |
| | DELETE | department/{department_id?}/{} | destroy | App\Http\Controllers\DepartmentController#destroy | web |
| | GET|HEAD | department/{department_id?}/{}/edit | edit | App\Http\Controllers\DepartmentController#edit | web |
| | GET|HEAD | department/{department_id?}/create | create | App\Http\Controllers\DepartmentController#create | web |
| | POST | department/{department_id?} | store | App\Http\Controllers\DepartmentController#store | web |
| | GET|HEAD | department/{department_id?} | index | App\Http\Controllers\DepartmentController#index | web |
Update:
If I make a custom route like this:
Route::get( 'customedit', 'DepartmentController#editasddas' );
and request the url: /department/1/editasddas. It works as it's suppose to, but there is actually a reason why I'm using the ressource: to keep the routes as clean as possible. The ressource-routes have been implemented for that reason aswell, and I just need to implement the basic CRUD operations. Is this a bug in Laravel, or is this basically not possible? - really strange I think. It's not that complex.

I think you have this issue because how Route::resource creates subroutes itself (automatically adding a resource parameter within URLs, the parameter being {} at the end).
Also, note you are currently generating a index route with a department parameter, and that's not really useful.
Best solution for me is to move out your parameter:
Route::prefix( 'department' )->group( function () {
Route::resource( '/', 'DepartmentController' );
});
In the other hand, the department_id parameter will not be facultative. And you will need to add the parameter within each other custom routes (but that's what Route::resource does with its own routes after all).
Second one is to keep your prefix and declare each route individually. But you will need to change the default route names because department.index and department.show will have the exact same methods (GET and HEAD) and URLs (department/{department_id}).
Route::prefix('department/{department_id}')->group(function() {
Route::match(['get', 'head'], '/', 'DepartmentController#index')->name('department.index');
Route::match(['get', 'head'], '/show', 'DepartmentController#show')->name('department.show');
/* Declare all the others. */
});

The Route::resource method alone will achieve what you're looking for:
Route::resource( 'department', 'DepartmentController' );
Check the docs on this here, https://laravel.com/docs/5.8/controllers#resource-controllers

Related

API RESTful Laravel 6.x Best Practice for Many to Many Relatioship

I'm developing an API with Laravel 6.
I've got 2 models:
card -> table cards with card_id ecc.
user -> table users with user_id ecc.
I've defined into models many to many relationships
User.php
public function cards()
{
return $this->belongsToMany('App\Models\v1\Card');
}
Card.php
public function users() {
return $this->belongsToMany('App\Models\v1\User');
}
The pivot table is called card_user .
Now I've created routes for single entities:
Route::resource('v1/users', 'v1\UsersController');
Route::resource('v1/cards', 'v1\CardsController');
and I need to develop routes and controller for insert and delete rows from pivot table.
What is the best practice for this issue?
I try to solve this with a special controller that respond to a specific endpoint:
Route::resource('v1/cards/{id}/users', 'v1\CardsUsersController')->only([
'index', 'store', 'destroy'
]);
But when I need to store information I need to pass the ids of card and user into the URL and as object in post body like so:
[
'user_id' => $userId,
'card_id' => $cardId
]
Exists a better way to do this?
Thanks a lot!
You can use Nested Resources as described here:
https://laravel.com/docs/6.x/controllers#restful-nested-resources
"Sometimes you may need to define routes to a "nested" resource. For example, a photo resource may have multiple "comments" that may be attached to the photo. To "nest" resource controllers, use "dot" notation in your route declaration:
Route::resource('photos.comments', 'PhotoCommentController');
This route will register a "nested" resource that may be accessed with URLs like the following: photos/{photos}/comments/{comments}."
If you must have separate routes and controller for them, then it would be better to do
Route::resource('v1/card_user', 'v1\CardsUsersController')->only(['index', 'store','destroy']);
Keep the route clean, and don't overcomplicate it. Either You or someone else in the future who views code should be able to understand what it is for.
I would combine both answers. As a relationship, it is technically a nested resource. Also, you really have 2 RESTful actions: store and destroy (which correspond to attach and detach in Laravel). You may also want an index to view all of the relationship. I believe the "create" action is optional, depending on your UI.
// Ability_Role pivot routes
Route::resource('v1/user.cards', 'UserCardController')
->only(['index', 'create', 'store','destroy']);
This will give the following routes:
+--------+----------+------------------------------+--------------------+-------------------------------------------------+------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+------------------------------+--------------------+-------------------------------------------------+------------+
| | GET|HEAD | v1/user/{user}/cards | user.cards.index | App\Http\Controllers\UserCardController#index | web |
| | POST | v1/user/{user}/cards | user.cards.store | App\Http\Controllers\UserCardController#store | web |
| | GET|HEAD | v1/user/{user}/cards/create | user.cards.create | App\Http\Controllers\UserCardController#create | web |
| | DELETE | v1/user/{user}/cards/{card} | user.cards.destroy | App\Http\Controllers\UserCardController#destroy | web |
+--------+----------+------------------------------+--------------------+-------------------------------------------------+------------+
I chose to label the routes as user.cards because I would think you would more often want to start with the user model and attached the cards.
For the store method, you can post an array of cards to attached to the user.
If you also want to start with cards, and store an array of users, you can also define the inverse relationships (though it would require a 2nd controller with just the create and store routes:
// Inverse create and store routes
Route::get('v1/cards/{card}/users/create', 'CardUserController#create')
->name('cards.users.create');
Route::post('v1/cards/{card}/users', 'CardUserController#store')
->name('cards.users.store');
now you will get 2 more routes added:
+--------+----------+------------------------------+--------------------+-------------------------------------------------+------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+------------------------------+--------------------+-------------------------------------------------+------------+
| | GET|HEAD | api/user | api. | Closure | api |
| | | | | | auth:api |
| | POST | v1/cards/{card}/users | cards.users.store | App\Http\Controllers\CardUserController#store | web |
| | GET|HEAD | v1/cards/{card}/users/create | cards.users.create | App\Http\Controllers\CardUserController#create | web |
| | GET|HEAD | v1/user/{user}/cards | user.cards.index | App\Http\Controllers\UserCardController#index | web |
| | POST | v1/user/{user}/cards | user.cards.store | App\Http\Controllers\UserCardController#store | web |
| | GET|HEAD | v1/user/{user}/cards/create | user.cards.create | App\Http\Controllers\UserCardController#create | web |
| | DELETE | v1/user/{user}/cards/{card} | user.cards.destroy | App\Http\Controllers\UserCardController#destroy | web |
+--------+----------+------------------------------+--------------------+-------------------------------------------------+------------+

Laravel best naming convention for controller method and route

I'm creating an ajax request to get item details
Here's what my controller method looks like.
class SystemItemsController extends Controller
{
function getDetails(Request $request){
$response = SystemItems::where('item_name', 'like', '%' .$name . '%')->get();
return response()->json($response,200);
}
}
and my
routing name
Route::get("/system-items/item-details","SystemItemsController#getStockDetails");
question : what would be the best naming convention for my route(item-details) and method(getStockDetails)?
follow up Q : can i do this using laravel resource?
You can use kebab-case and plural in the URI pattern, but camelCase and singular for Controller name, as that is what Laravel will look for if trying to do route–model binding.
You can use it for resouce routes, but note, for this route
Route::resource('item-details', 'ItemDetailController');
the route parameter will result in snake_case and singular
/item-details/{item_detail}
For the controller methods the conventional names are index, show, create, store, edit, update and delete. And snakeCase for custom methods.
Also you can add a route group to prefix with some uri like /system-items
Route::group(['prefix' => 'system-items'], function () {
Route::resource('item-details', 'ItemDetailController');
});
run php artisan route:list to see the result
+--------+-----------+-----------------------------------------------------+-------------------------+---------------------------------------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+-----------+-----------------------------------------------------+-------------------------
| | GET|HEAD | api/v1/system-items/item-details | item-details.index | App\Http\Controllers\Api\v1\ItemDetailController#index | api |
| | POST | api/v1/system-items/item-details | item-details.store | App\Http\Controllers\Api\v1\ItemDetailController#store | api |
| | GET|HEAD | api/v1/system-items/item-details/create | item-details.create | App\Http\Controllers\Api\v1\ItemDetailController#create | api |
| | GET|HEAD | api/v1/system-items/item-details/{item_detail} | item-details.show | App\Http\Controllers\Api\v1\ItemDetailController#show | api |
| | PUT|PATCH | api/v1/system-items/item-details/{item_detail} | item-details.update | App\Http\Controllers\Api\v1\ItemDetailController#update | api |
| | DELETE | api/v1/system-items/item-details/{item_detail} | item-details.destroy | App\Http\Controllers\Api\v1\ItemDetailController#destroy | api |
| | GET|HEAD | api/v1/system-items/item-details/{item_detail}/edit | item-details.edit | App\Http\Controllers\Api\v1\ItemDetailController#edit
Of course, all of these are conventions and you can customize everything by doing it by hand and using your own conventions.

Route isn't passing variables to controller when used in blade

I'm using a named route in a view, passing two variables with it, but it throws the error that no arguments are passed.
I know that I could just construct the url for the href, but from what I've read so far is that what I'm trying should work just fine too (and I'm not seeing what I'm doing different from the documentation examples really).
link in show.blade.php
Change Name
in the browser the link shows up as
http://localhost/tasks/create?colony_slug=labr-oclh&action_name=change_name
route in web.php
Route::get('tasks/create/{colony_slug}/{action_name}', 'TaskController#create')->name('tasks.create');
TaskController#create
// CREATE ACTION
// =========================================================================
/**
* Show the form for creating a new resource.
*
* #param string $colony_slug
* #param string $action_name
*
* #return \Illuminate\Http\Response
*/
public function create($colony_slug, $action_name)
{
dd($colony_slug . " " . $action_name);
}
the error
Too few arguments to function App\Http\Controllers\TaskController::create(), 0 passed and exactly 2 expected
the routes list
| | GET|HEAD | tasks | tasks.index | App\Http\Controllers\TaskController#index | web,auth
|
| | POST | tasks | tasks.store | App\Http\Controllers\TaskController#store | web,auth
|
| | GET|HEAD | tasks/create/{colony_slug}/{action_name} | tasks.create | App\Http\Controllers\TaskController#create | web,auth
|
| | PUT|PATCH | tasks/{task} | tasks.update | App\Http\Controllers\TaskController#update | web,auth
|
| | DELETE | tasks/{task} | tasks.destroy | App\Http\Controllers\TaskController#destroy | web,auth
|
| | GET|HEAD | tasks/{task} | tasks.show | App\Http\Controllers\TaskController#show | web,auth
|
| | GET|HEAD | tasks/{task}/edit | tasks.edit | App\Http\Controllers\TaskController#edit | web,auth
|
|
As discovered through comment questions, the issue is your custom route conflicting with a Resource Controller route. Specifically in the route name (via named()), which should always be unique even if they use the same URI.
There are a couple solutions you can use, depending on what your functionality goal is:
Solution for: I only want one "Create Task" route
Disable the task.create route created by the resource controller by using the except() modifier:
Route::resource('tasks', 'TasksController')->except(['create']);
Leave your other route definition as-is. This will remove the URI /tasks/create from your app, and leave the one with the additional parameters.
Solution for: I want to use both
The route paths themselves are okay, and are only conflicting via the name. Name your custom route something different, and use it when you want to use the extra parameters.
Route::get('tasks/create/{colony_slug}/{action_name}', 'TaskController#create')->name('tasks.create-custom');
Solution for: I want to specify defaults on my Create Task form
I'm guessing that you're using these parameters in order to set some default values in your Create Task form. If that's what your end goal is, using the default Resource Controller route + query string parameters would work equally well, and won't involve extra routes.
Remove your extra custom route, and stick with the defaults from the Resource Controller
Declare a Illuminate\Http\Request dependency in your create() controller method:
public function create(Request $request)
{
// ...
}
Check for query string values in the request, and add them as defaults to your form.
As I look to all the code you provide it looks correct to me.
Maybe you should check your routes file. Remember routes have presedence top to botton, if you had another route with similar name it could be that this one is having precedence over the one you are tryint to use.

Laravel 5.2 some routes doesn't work

I have this route:
Route::get('/sites', 'SitesController#index');
and when i run http://localhost:8880/sites it responds:
The Browser response (404 error): The requested resource /sites was not found on this server.
The Terminal Response : Invalid request (Unexpected EOF)
When i change the route to:
Route::get('/premium-sites', 'SitesController#index');
And run it in my browser
http://localhost:8880/premium-sites.
Everything works like a charm.
I don't use anywhere else this route (/sites) and
all other 20 routes i have, work fine except for this.
I can't understand what kind of bug is this and i can't find a way to fix it.
Update
This is my route list
+--------+----------+-------------------+-----------------+-------------------------------------------------+------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+-------------------+-----------------+-------------------------------------------------+------------+
| | GET|HEAD | categories/{type} | categories.show | App\Http\Controllers\CategoriesController#index | web |
| | GET|HEAD | sites | sites.show | App\Http\Controllers\SitesController#index | web |
+--------+----------+-------------------+-----------------+-------------------------------------------------+------------+
I also changed the route to this:
Route::get('/sites', [
'as' => 'sites.show',
'uses' => 'SitesController#index'
]);
and my controller to:
public function index()
{
return 'test route';
}
The response is :
And the terminal response:
[Tue Aug 30 01:56:15 2016] ::1:61591 [404]: /sites - No such file or directory
[Tue Aug 30 01:56:15 2016] ::1:61593 [200]: /favicon.ico
[Tue Aug 30 01:56:38 2016] ::1:61594 Invalid request (Unexpected EOF)
I am probably late for this answer. Anyways could be useful for someone.
Had this issue the other day, the reason for me was due to the existence of a the folder name "X" in the public directory which resembles the route name "X".
Most probably you are having a folder called "sites" in public directory, Either rename that or the one in the routes file
It appears that there may be some conflict somewhere in one of your routes file. Here is the setup I have and it is working just fine.
routes.php
Route::group(['middleware' => ['web']], function () {
Route::get('/sites', 'SitesController#index');
});
route:list
php artisan route:list
+--------+----------+-------+------+--------------------------------------------+------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+-------+------+--------------------------------------------+------------+
| | GET|HEAD | sites | | App\Http\Controllers\SitesController#index | web |
+--------+----------+-------+------+--------------------------------------------+------------+
Here is my controller
<?php
namespace App\Http\Controllers;
class SitesController extends Controller
{
public function index()
{
return 'Test Route';
}
}
To debug this, I would start with xdebug to see if you can break at the route to see what is going on and follow the trace. if you do not have xdebug set up, comment out all routes except for the /sites route and see if it works then. If it does, one of your other routes is in the way. Begin by uncommenting routes one by one or group by group and when the error re-surfaces, you are closer.
Maybe Route::get('/sites', 'SitesController#index'); is involved
better use this route:
// Sites
Route::resource('/sites', 'SitesController');
for example the route :
// Home
Route::resource('/home', 'HomeController');
have :
GET|HEAD | home | home.index | App\Http\Controllers\HomeController#index
POST | home | home.store | App\Http\Controllers\HomeController#store
GET|HEAD | home/create | home.create | App\Http\Controllers\HomeController#create
DELETE | home/{home} | home.destroy | App\Http\Controllers\HomeController#destroy
PUT|PATCH | home/{home} | home.update | App\Http\Controllers\HomeController#update
GET|HEAD | home/{home} | home.show | App\Http\Controllers\HomeController#show
GET|HEAD | home/{home}/edit | home.edit | App\Http\Controllers\HomeController#edit

Laravel 5.2 : MethodNotAllowedException

I've defined the following route in Laravel:
Route::group(['prefix' => 'api'], function() {
Route::post('login', [
'uses' => 'Auth\AuthController#login',
'as' => 'auth.login',
]);
});
And I'm using Postman to send a request like this (you can also see the results):
Why am I getting a MethodNotAllowed exception????
I also tried creating a form in an empty html file, with the method set to post. but got the same results.
EDIT
If i add a route::get that shows a login form, after the post request in Postman it shows that login form.
EDIT 2:
output of php artisan route:list for our route entries:
+--------+----------+--------------+---------------------+----------------------------------------------------+------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+--------------+---------------------+----------------------------------------------------+------------+
| | GET|HEAD | / | guest.home | App\Http\Controllers\GuestController#index | |
| | GET|HEAD | a/dashboard | admin.dashboard | Closure | |
| | POST | api/login | auth.login | App\Http\Controllers\Auth\AuthController#login | |
| | GET|HEAD | api/login | auth.login | Closure | |
| | GET|HEAD | api/logout | auth.logout | App\Http\Controllers\Auth\AuthController#getLogout | jwt.auth |
| | POST | api/register | auth.register | App\Http\Controllers\Auth\AuthController#register | jwt.auth |
| | GET|HEAD | m/dashboard | moderator.dashboard | Closure | |
| | GET|HEAD | pu/dashboard | premium.dashboard | Closure | |
| | GET|HEAD | u/dashboard | user.dashboard | Closure | |
+--------+----------+--------------+---------------------+----------------------------------------------------+------------+
EDIT3
One more curious thing. If i set the method to Route::any, I get rid of the exception, but then I can't access the post parameters. i.e. I don't have any post parameters.
EDIT 4:
If I add a route::get and show the login view there and send the login credential, it works. But not in Postman.
Use x-www-form-urlencoded instead of form-data in postman, See the difference below.
form-data
multipart/form-data is the default encoding a web form uses to transfer data. This simulates filling a form on a website, and submitting it. The form-data editor lets you set key/value pairs (using the key-value editor) for your data. You can attach files to a key as well. Do note that due to restrictions of the HTML5 spec, files are not stored in history or collections. You would have to select the file again at the time of sending a request.
urlencoded
This encoding is the same as the one used in URL parameters. You just need to enter key/value pairs and Postman will encode the keys and values properly. Note that you can not upload files through this encoding mode. There might be some confusion between form-data and urlencoded so make sure to check with your API first.
Unfortunately the problem was with Postman3. I'm using Advanced REST Client now, and it works alright. Postman would send GET requests no matter what method I chose.

Resources