/posts/{id} route is blocking /posts/create in Laravel - php

In my ChallengesController I have these routes:
public function show($id) {
$challenge = Challenge::find($id);
if (!$challenge) {
return back()->with('error', 'Challenge does not exist');
}
$projects = $challenge->projects;
return view('challenges.show')->with(['challenge' => $challenge, 'projects' => $projects]);
}
public function create() {
if (auth()->user()->role === 'user') {
return back()->with('error', 'You are unauthorized to do that');
}
return view('challenges.create');
}
In my web.php routes I have these routes:
Route::get('/challenges/{id}', 'ChallengesController#show');
Route::get('/challenges/create', 'ChallengesController#create');
Whenever I want to go to /challenges/create it thinks I have to go to /challenges/{id} and is thinking the {id} is "create". But in my other controller where I just specified
Route::resource('projects', 'ProjectsController');
it has the same route structure when I do php artisan route:list, but it's working and my custom /challenge routes are not.
Is there a way to override the /challenges/create or am I doing something wrong. I am using Laravel version 5.7.20.

or even more simpler, change the order of declaration:
Route::get('/challenges/create', 'ChallengesController#create');
Route::get('/challenges/{id}', 'ChallengesController#show');

From Laravel documentation
You may constrain the format of your route parameters using the where
method on a route instance. The where method accepts the name of the
parameter and a regular expression defining how the parameter should
be constrained:
Route::get('challenges/{id}', function ($id) {
//
})->where('id', '[0-9]+');
Now only numeric values will be accepted as the parameter id.

Related

Laravel Resource controller

How do I tell my API to display a particular result based on another column?
e.g. localhost:8000/api/gadgets/{{id}}
Normally it returns the particular information of the specific gadget with that ID and localhost:8000/api/gadgets/{{imei_code}} does not return any value or an error whereas imei_code is a column that I needed to pass as a GET request...
I'm using the normal resource controller
public function show(Gadgets $gadget)
{
$response = ['data' => new GadgetResource($gadget), 'message' => 'specific gadget'];
return response($response, 200);
}
Also I need help on how I can create like a search function in the controller.
You can`t do two similar URLs. I think your route for URL
localhost:8000/api/gadgets/{{imei_code}}
isn`t work. Also the order of the routes is important and route that defined firstly will be have higer priority then route that defined secondly.
Because your routes /api/gadgets/{{id}} and /api/gadgets/{{imei_code}} is similar in this case only the one described earlier will be processed.
You can define another router and handler, for example:
localhost:8000/api/gadgets
That will return a list of gadgets by default and you can add filters for imei_code. For example:
localhost:8000/api/gadgets?imei_code=123
And your handler for the new route may be writed something like that:
public function showList(Request $request): GadgetResource
{
if ($imeiCode = $request->query('imei_code')) {
$list = Gadget::query()->where('imei_code', $imeiCode)->get();
} else {
$list = Gadget::query()->take(10)->get();
}
return GadgetResource::collection($list);
}
Or like alternative solution you can create diferent route for searching of gadgets exactly by imei_code to get rid of any route conflicts
localhost:8000/api/gadgets/by_imei/123
public function findByImei(Request $request): GadgetResource
{
$imeiCode = $request->route('imei_code');
$item = Gadget::query()->where('imei_code', $imeiCode)->first();
return new GadgetResource($item);
}
You can specify the model key by scoping - check docs
Route::resource('gadgets', GadgetController::class)->scoped([
'gadget' => 'imei_code'
]);
Than, when Laravel try to bind Gadget model in Controller - model will will be searched by key imei_code.
This code equvalent of
Route::get('/gadget/{gadget:imei_code}');
Try to change response
public function show(Gadgets $gadget)
{
$response = ['data' => new GadgetResource($gadget), 'message' => 'specific gadget'];
return response()->json($response);
}

Laravel Validation - Rule to disallow request parameters

In my Laravel 5.8 app I have many API routes which return paginated results. If I make a request to my API appending the following query string I can disable pagination.
http://api.test/users/?no_paginate=1
My question is... how can I disable no_paginate from being used on certain routes? I'd preferbly want some validation to go in the request class but I can't find anything in the docs for that.
You can do this using a Global Middleware.
Create a DisableNoPaginate Middleware:
php artisan make:middleware DisableNoPaginate
Then define what the middleware should do (DisableNoPaginate.php):
<?php
namespace App\Http\Middleware;
use Closure;
class DisableNoPaginate
{
public function handle($request, Closure $next)
{
//remove no_paginate param from request object
unset($request['no_paginate']);
return $next($request);
}
}
Arrange for the middleware to run on all routes (routes.php):
$app->middleware([
App\Http\Middleware\DisableNoPaginate::class
]);
Now the no_paginate query param should be stripped from all your incoming requests.
For the best approach to get users either paginate or get all listing by below code in UsersController
public function index($type = null, Request $request)
{
$builder = User::where(/*query*/);
if($type == "paginate") {
$items = $builder->paginate(10);
} else {
$items = $builder->get();
}
return view("users.index", ['users' => $items]);
}
Here is the route in web.php/api.php file
Route::get('/{type?}', ['as' => 'users.index', 'uses' => 'UsersController#index']);
Here url will be
http://api.test/users/paginate // get pagination response.
http://api.test/users // get response without pagination
I think this will help you.

Call to a member function name() on null in laravel 5.4

When pressing my send button it's giving error like this-
Here is my routes web.php bellow-
Route::group(['prefix'=>'ajax', 'as'=>'ajax::'], function() {
Route::resource('message/send', 'MessageController#ajaxSendMessage')->name('message.new');
Route::delete('message/delete/{id}', 'MessageController#ajaxDeleteMessage')->name('message.delete');
});
Here is my controller MessageController.php bellow:
public function ajaxSendMessage(Request $request)
{
if ($request->ajax()) {
$rules = [
'message-data'=>'required',
'_id'=>'required'
];
$this->validate($request, $rules);
$body = $request->input('message-data');
$userId = $request->input('_id');
if ($message = Talk::sendMessageByUserId($userId, $body)) {
$html = view('ajax.newMessageHtml', compact('message'))->render();
return response()->json(['status'=>'success', 'html'=>$html], 200);
}
}
}
Resource routes should be named differently:
Route::prefix('ajax')->group(function () {
Route::resource('messages', 'MessageController', ['names' => [
'create' => 'message.new',
'destroy' => 'message.destroy',
]]);
});
Resource routes also point to a controller, instead of a specific method. In MessageController, you should add create and destroy methods.
More info at https://laravel.com/docs/5.4/controllers#restful-naming-resource-routes
You can't name a resource. Laravel by default name it, if you want to name all routes you must specify each one explicitly. It should be like this:
Route::group(['prefix'=>'ajax', 'as'=>'ajax::'], function() {
Route::get('message/send', 'MessageController#ajaxSendMessage')->name('message.new');
Route::delete('message/delete/{id}', 'MessageController#ajaxDeleteMessage')->name('message.delete');
});
Update
Another mistake of yours was trying to resource a single method. A Route::resource() is used to map all basic CRUD routes in Laravel by default. Therefore, you have to pass the base route and the class i.e:
<?php
Route::resource('message', 'MessageController');
Look at web.php line 28.
Whatever object you think has a name() method, hasn't been set, therefore you try and call a method on null.
Look before that line and see where it is (supposed to be) defined, and make sure it is set to what it should be!

Very Confusing MethodNotAllowedHttpException on a put request laravel

So far all attempts to modify the routing methods have failed.
Been following some documentation on laravel restful controllers and have one set up to do basic editing and adding of items to a database. It was going well till I hit the snag on... well I'm not sure what precisely is triggering the problem, but basically, everything works till I hit submit on the form and then it's Game Over.
Normally I'd be able to diagnose this by checking to see if I'm using the right call, or made a spelling mistake or something. But this is a new request for me, so I can't quite debug where the problem is coming from.
This is the error those who know what to look for. In full here.
MethodNotAllowedHttpException in RouteCollection.php line 218:
My routes are pasted here.
A printout of the routes is here:
Controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests\ContactFormRequest;
use App\UserEdit;
use DB;
use App\Http\Requests;
class EditUserController extends Controller
{
public function index()
{
$array = UserEdit::all()->toArray();
return view('UserEntry', compact('array'));
}
public function create()
{
$id = UserEdit::find(715)->toArray();
return view('NewUser', compact('id'));
}
public function store(UserFormRequest $request)
{
//$user = new UserEdit([
// 'name'=>$request->get('First_Name'),
// 'email'=>$request->get('email'),
// 'username'=>$request->get('name')
//]);
//
//$user->save();
//return \Redirect::route('users')->with('message', 'Nice Work.');
}
public function show($id)
{
try {
$array = UserEdit::findorFail($id)->toArray();
return view('UserEdit')->with('array', $array);
} catch(\Exception $e) {
return \Redirect::route('users.index')
->withMessage('This user does not exist');
}
}
public function edit($id)
{
$user = UserEdit::findorFail($id);
return view('EditUser')->with('user',$user);
}
public function update($id, UserFormRequest $request)
{
$user = UserEdit::findorFail($id);
$user->update([
'name' => $request->get('name'),
'email' => $request->get('email')
]);
return \Redirect::route('users.edit', [$user->id])->with('message', 'Details Updated!');
}
public function destroy($id)
{
//
}
}
The Blade is here.
if you have a hard time finding the solution the easiest solution is using
Route::any('users/{user}', 'UserEntryController#update');
this allow you to access this action with any method type
OR
Route::match(array('get', 'put'), 'users/{user}', 'UserEntryController#update');
so you need 2 method which are
get -> view
put -> submit update
you can just indicate which method type you want to be accessible with in this action
i think you are using model in form.try this
{{ Form::open(['method' => 'put', 'route'=>['users.update', $user->id], 'class'=>'form']) }}
As per your route list and route put doesnt taking id so you get method not found exception
PUT users/{user} App\Http\Controllers\EditUserController#update
instead of using resouce just type each route for each method
Route::put('users/{user}', 'EditUserController #update');
It seems like after sorting out the routes, the issue fell to a bad capitalisation. $user->id should have been $user->ID.

Unable to generate a URL

I am currently trying to create a link on the index page that'll allow users to create an item. My routes.php looks like
Route::controller('items', 'ItemController');
and my ItemController looks like
class ItemController extends BaseController
{
// create variable
protected $item;
// create constructor
public function __construct(Item $item)
{
$this->item = $item;
}
public function getIndex()
{
// return all the items
$items = $this->item->all();
return View::make('items.index', compact('items'));
}
public function getCreate()
{
return View::make('items.create');
}
public function postStore()
{
$input = Input::all();
// checks the input with the validator rules from the Item model
$v = Validator::make($input, Item::$rules);
if ($v->passes())
{
$this->items->create($input);
return Redirect::route('items.index');
}
return Redirect::route('items.create');
}
}
I have tried changing the getIndex() to just index() but then I get a controller method not found. So, that is why I am using getIndex().
I think I have set up my create controllers correctly but when I go to the items/create url I get a
Unable to generate a URL for the named route "items.store" as such route does not exist.
error. I have tried using just store() and getStore() instead of postStore() but I keep getting the same error.
Anybody know what the problem might be? I don't understand why the URL isn't being generated.
You are using Route::controller() which does generate route names as far as I know.
i.e. you are referring to "items.store" - that is a route name.
You should either;
Define all routes specifically (probably best - see this blog here)
Use Route::resource('items', 'ItemController'); see docs here
If you use Route::resource - then you'll need to change your controller names
The error tells you, that the route name is not defined:
Unable to generate a URL for the named route "items.store" as such route does not exist.
Have a look in the Laravel 4 Docs in the Named Routes section. There are several examples that'll make you clear how to use these kind of routes.
Also have a look at the RESTful Controllers section.
Here's an example for your question:
Route::get('items', array(
'as' => 'items.store',
'uses' => 'ItemController#getIndex',
));
As The Shift Exchange said, Route::controller() doesn't generate names, but you can do it using a third parameter:
Route::controller( 'items',
'ItemController',
[
'getIndex' => 'items.index',
'getCreate' => 'items.create',
'postStore' => 'items.store',
...
]
);

Categories