I am trying to display information submitted from a form to create a message board. I am using php in the laravel framework. I am using a form, record, and repository. Whenever I try to display the content I receive the following error Symfony \ Component \ HttpKernel \ Exception \ MethodNotAllowedHttpException.
Here is the controller:
/**
* Display a listing of the resource.
* GET /messages
*
* #return Response
*/
public function create()
{
return View::make('comments.create');
}
public function show($comment)
{
$message_id = $this->messageRepository->find($comment);
return View::make('comments.show')->with('comment', $message_id);
}
/**
* Store a newly created resource in storage.
* POST /messaages
*
* #return Response
*/
public function store()
{
$data = Input::all() ;
$this->messageForm->validate($data);
$messageRecord = new MessageRecord;
$messageRecord->comment = $data['comment'];
Return "Comment created";
}
}
Here is the view causing the trouble:
<p>$comment</p>
I have not been using a route but I threw this together:
Route::resource('/message', 'MessageController');
[Edit:]
If your store() is already used by another form and you still have another form to send POST data then, you should declare another routes for that. Like
Route::post('message/display', ['as' => 'message.display', 'uses' => 'MessageController#display');
Sorry for my previous answer, show() is for GET request and since you are using form you probably you don't need it unless you decide to send data through url. I'm extremely sorry.
[Edit ends]
You are getting that error because you are not allowed to use display() in resourceful routing. Instead, you must use store(). Since you are using laravel resourceful routing, you should follow the convention of laravel. In your command line, run
php artisan routes
You will get something like this,
GET|HEAD message | message.index | MessageController#index
GET|HEAD message/create | message.create | MessageController#create
POST message | message.store | MessageController#store
GET|HEAD message/{message} | message.show | MessageController#show
GET|HEAD message/{message}/edit | message.edit | MessageController#edit
PUT message/{message} | message.update | MessageController#update
PATCH message/{message} | | MessageController#update
DELETE message/{message} | message.destroy | MessageController#destroy
These are the methods, their route names and their corresponding HTTP request that you should use.
Check Laravel documentation for resourceful controller
For quick look,
index : Display a listing of the resource,
create : Show the form for creating a new resource,
store : Store a newly created resource in storage,
show : Display the specified resource,
edit : Show the form for editing the specified resource,
update : Update the specified resource in storage,
destroy : Remove the specified resource from storage
It seems you use incorrect verb for route displaying this route. For display you should probably use Route::get and you probably use Route::post. If it's not the issue you edit your question and put there routes.php as you were asked in comment.
Related
I have been using RESTful controllers in my Laravel project. By including:
Route::controller('things', 'ThingController')
in my routes.php, I can define functions in the ThingController like:
public function getDisplay($id) {
$thing = Thing::find($id)
...
}
so that GETting the URL "...things/display/1" would automatically be directed to the controller function. This seems pretty handy and has been working great for me so far.
I noticed many of my controller functions start with getting a model by id from the url, and I thought it would be nice to be able to use route model binding to do this for me instead. So I updated my routes.php to
Route::model('thing', 'Thing');
Route::controller('things', 'ThingController')
and changed the ThingController functions to
public function getDisplay($thing) {
...
}
I assumed this would magically work the way I wanted it to (like everything else I've tried so far in Laravel has) but unfortunately I get "Trying to get property of non-object" when I attempt to use $thing in the function. Is this something that should be able to work and I have just done it wrong, or can route model binding only work with routes explicitly named in routes.php?
If you don't mind with URI path, method name and just work only show, edit and update method, you can use Resource Controller to generate URI string which can define model binding.
In routes.php change to
Route::model('things', 'Thing');
Route::resource('things', 'ThingController');
You can use php artisan routes command to see all URIs
$ artisan routes | grep ThingController
GET|HEAD things | things.index | ThingController#index
GET|HEAD things/create | things.create | ThingController#create
POST things | things.store | ThingController#store
GET|HEAD things/{things} | things.show | ThingController#show
GET|HEAD things/{things}/edit | things.edit | ThingController#edit
PUT things/{things} | things.update | ThingController#update
PATCH things/{things} | | ThingController#update
After that you can threat parameter as Thing object without explicitly name route.
/**
* Display the specified thing.
*
* #param Thing $thing
* #return mixed
*/
public function show(Thing $thing)
{
return $thing->toJson();
}
If you want to access ThingController#show, pass your model ID and Laravel will retrieve it automatically.
http://example.com/things/1
{"id":1,"type":"Yo!"}
You can use Route:resource and still provide other methods. Place the route you need just before that particular Route::resource line.
Eg:
Route::model('things', 'Thing');
Route::get('things/{things}/owner', 'ThingController#getOwner');
Route::resource('things', 'ThingController');
Then create the corresponding method in your controller.
public function getOwner($things) {
return Response::json($things->owner()->get());
}
Here is the official documentation from the Laravel 4.2 docs:
Source: http://laravel.com/docs/controllers#resource-controllers
Adding Additional Routes To Resource Controllers
If it becomes necessary for you to add additional routes to a resource controller beyond the default resource routes, you should define those routes before your call to Route::resource:
Route::get('photos/popular');
Route::resource('photos', 'PhotoController');
I currently have routes like this
//Settings
Route::prefix('settings')->group(function(){
//Get all users settings
Route::resource('user', 'SettingsController');
});
Which will produce a list of routes like so
| POST | settings/user |user.store | App\Http\Controllers\SettingsController#store | web,auth,GlobalAdmin |
| GET|HEAD | settings/user |user.index | App\Http\Controllers\SettingsController#index | web,auth,GlobalAdmin |
| GET|HEAD | settings/user/create |user.create | App\Http\Controllers\SettingsController#create | web,auth,GlobalAdmin |
And so on.
My issue is that I want the settings controller to be able to control a list of different settings in 1 controller, not just 'users'.
How would I name the resource so that it names the functions at the end?
For example, the above code generates function names like SettingsController#store, how would i get it so that it auto builds the function name with a prefix like SettingsController#userstore?
You can't. Unless you go behind the scenes and actually hack into how Laravel handles the generation. What you can do however is use except on the resource route or use partial resource routes.
Once you have done one of the above, you can just add your routes manually such as
POST settings/user
POST settings/other
POST settings/general
And point them that way.
If you really want to have such behavior you can have it. You can extend the Illuminate\Routing\ResourceRegistrar class and bind your extended version to the container.
There is only one method that needs to be adjusted, which is the method that sets up the action for each of the routes of the resource, getResourceAction. This can be adjusted to check for a key passed in the options array the the ResourceRegistrar already uses. If a key is present you can enable the behavior you need, prefixing the method name with the resource name and uppercasing the first letter of the actual method.
class YourRegistrar extends \Illuminate\Routing\ResourceRegistrar
{
protected function getResourceAction($resource, $controller, $method, $options)
{
$name = $this->getResourceRouteName($resource, $method, $options);
// check if 'pre' option was set
$method = isset($options['pre']) ? $resource . ucfirst($method) : $method;
$action = ['as' => $name, 'uses' => $controller.'#'.$method];
if (isset($options['middleware'])) {
$action['middleware'] = $options['middleware'];
}
return $action;
}
}
In a Service Provider #register (binding your new class to the current ResourceRegistrar):
$this->app->bind(
\Illuminate\Routing\ResourceRegistrar::class,
\Where\Ever\YourResourceRegistrar::class
);
In a routes file:
Route::resource('user', 'SettingsController', ['pre' => true]);
// SettingsController#userIndex
// SettingsController#userShow
// ...
Route::resource('user', 'SettingsController');
// SettingsController#index
// SettingsController#show
// ... normal
The router checks to see if there is something bound to the name of the ResourceRegistrar on the container before newing up one. If there is a binding it asks the container to resolve one. This is how you can extend the ResourceRegistrar and the router uses your version.
In our version we are checking if the options key pre was set or not. If it was we adjust the method names for the routes accordingly. ($method = isset($options['pre']) ? $resource . ucfirst($method) : $method;)
You can read more on the ResourceRegistrar and more detail of what happened above in my blog article on the subject:
asklagbox blog - Resource Registrar - lets extend
I do have the current state that I have multiple projects in my Laravel application (which are stored in a database). I do have the following URL structure:
http://app.com/project/7-exampleproject/news
where
https://app.com/project/{id}-{seostring}/{module}
Is the idea behind it. What I do need is that the project variable should be available on any view (module) which comes after the {id} part. How can I achieve it?
==
Additional information
Here is the route list:
GET|HEAD | project/{project}{extra?} | project.dashboard.index | App\Http\Controllers\Project\DashboardController#index | web
GET|HEAD | project/{project}{extra?}/news | project.news.index | App\Http\Controllers\Project\NewsController#index | web
GET|HEAD | project/{project}{extra?}/tournaments | project.tournaments.index | App\Http\Controllers\Project\TournamentController#index | web
Route::group(['prefix' => 'project', 'namespace' => 'Project'], function(){
Route::get('/{project}{extra?}', 'DashboardController#index')->name('project.dashboard.index')->where(['id', '[0-9+]','extra' => '-[A-Za-z0-9]+']);
Route::get('/{project}{extra?}/news', 'NewsController#index')->name('project.news.index')->where(['id', '[0-9+]','extra' => '-[A-Za-z0-9]+']);
Route::get('/{project}{extra?}/tournaments', 'TournamentController#index')->name('project.tournaments.index')->where(['id', '[0-9+]','extra' => '-[A-Za-z0-9]+']);
});
In each of those views my app.layouts.project gets extended by the content of each, so we end up with:
#extends('layouts.project')
#section('content')
<h3>Welcome to <br>{{ $project->name }}!</h3>
#endsection
But what I want to achieve is, EACH time the /project/{project} gets called, the view layouts.project should get it's active ID and $project variable
==
How I want the data to be available:
In my case, each module currently gets the active project from the URL (as seen on the dashboard here), already from the URL. This is not what I am looking for.
public function index(Project $project)
{
return view('project.dashboard.index',[
'project' => $project
]);
}
What I am looking for is to include the $project object (including all information) to layouts.project, which includes meta and head title informations, as well as displaying the project name itself.
Edit 3: Another thing was that you wanted to set the data in your parent view. You can do this even when you extend the parent and pass the data to the child view from the controller. Hope i've clarified things in the chat for you.
Edit 2: You want to auto load the object in the view. You can do this by creating a view composer and reading the current route. If the routes match, extract the project id and load the project model. You can then access the project object in your view without passing it through your controller. Though this is too much of work and bad practise compared to passing the object via the controller.
Edit: After all the comments and chat i understand what you're trying to do.
This will give you the Project object using route model binding while ignoring the unwanted SEO string. This will also work with both cases.
http://app.com/project/7-exampleproject/news
http://app.com/project/7/news
Replace the boot in your RouteServiceProvider
// app/Providers/RouteServiceProvider.php
public function boot()
{
parent::boot();
Route::bind('project', function ($value) {
$id = explode('-', $value)[0];
return \App\Project::findOrFail($id);
});
}
Change your routes to
// routes/web.php
Route::group(['prefix' => 'project', 'namespace' => 'Project'], function () {
Route::get('/{project}', 'DashboardController#index')->name('project.dashboard.index');
Route::get('/{project}/news', 'NewsController#index')->name('project.news.index');
Route::get('/{project}/tournaments', 'TournamentController#index')->name('project.tournaments.index');
});
The path has to be passed as a single parameter and you can split it in your controller to get the id and seotring separately. Something like this.
Route::get('/project/{project}/{module}', 'SomeController#show');
public function show($project, $module)
{
$data = explode('-', $project, 2);
$id = $data[0];
$name = $data[1];
return view('someview')->with(compact('id', 'name'));
}
I think you are looking for ViewComposers, it is not based on the url but on the view name.
My personal opinion is that this is a better approach than something based on URL since URL are changing more often than view names.
I am having a problem with the laravel5 resource controller. The POST method is working fine however the delete method is not. as you can see from postman i am passing the DELETE _method to the correct route
In the mean time i am using direct routes which are also working fine.
Route::delete('customisemymeal', ['as'=>'customisemymeal', 'uses'=>'UserMealCustomController#destroy']);
Route::post('customisemymeal', ['as'=>'customisemymeal', 'uses'=>'UserMealCustomController#store']);
I have disabled the CSRF token check until this is sorted out.
Can you please help explain why the same method is different for a resource controller compared to a route::delete?
routes:list
| DELETE | customisemymeal/{customisemymeal} | customisemymeal.destroy | App\Http\Controllers\UserMealCustomController#destroy |
| DELETE | customisemymeal | customisemymeal | App\Http\Controllers\UserMealCustomController#destroy |
To use the route:
Route::resource('customisemymeal', ['as'=>'customisemymeal', 'uses'=>'UserMealCustomController']);
You must abide to a few rules. To delete something you need to use:
domain.com/customisemymeal/resource_id
From your screenshots you are trying to delete a resource, using a different URI.
domain.com/customisemymeal
That won't work.
Rules are:
Index:
GET -> domain.com/resource
Show:
GET -> domain.com/resource/resource_id
create:
GET -> domain.com/resource/create
edit:
GET -> domain.com/resource/resource_id/edit
update:
PATCH / UPDATE -> domain.com/resource/resource_id
store:
POST -> domain.com/resource
delete:
DELETE -> domain.com/resource/resource_id
I have been using RESTful controllers in my Laravel project. By including:
Route::controller('things', 'ThingController')
in my routes.php, I can define functions in the ThingController like:
public function getDisplay($id) {
$thing = Thing::find($id)
...
}
so that GETting the URL "...things/display/1" would automatically be directed to the controller function. This seems pretty handy and has been working great for me so far.
I noticed many of my controller functions start with getting a model by id from the url, and I thought it would be nice to be able to use route model binding to do this for me instead. So I updated my routes.php to
Route::model('thing', 'Thing');
Route::controller('things', 'ThingController')
and changed the ThingController functions to
public function getDisplay($thing) {
...
}
I assumed this would magically work the way I wanted it to (like everything else I've tried so far in Laravel has) but unfortunately I get "Trying to get property of non-object" when I attempt to use $thing in the function. Is this something that should be able to work and I have just done it wrong, or can route model binding only work with routes explicitly named in routes.php?
If you don't mind with URI path, method name and just work only show, edit and update method, you can use Resource Controller to generate URI string which can define model binding.
In routes.php change to
Route::model('things', 'Thing');
Route::resource('things', 'ThingController');
You can use php artisan routes command to see all URIs
$ artisan routes | grep ThingController
GET|HEAD things | things.index | ThingController#index
GET|HEAD things/create | things.create | ThingController#create
POST things | things.store | ThingController#store
GET|HEAD things/{things} | things.show | ThingController#show
GET|HEAD things/{things}/edit | things.edit | ThingController#edit
PUT things/{things} | things.update | ThingController#update
PATCH things/{things} | | ThingController#update
After that you can threat parameter as Thing object without explicitly name route.
/**
* Display the specified thing.
*
* #param Thing $thing
* #return mixed
*/
public function show(Thing $thing)
{
return $thing->toJson();
}
If you want to access ThingController#show, pass your model ID and Laravel will retrieve it automatically.
http://example.com/things/1
{"id":1,"type":"Yo!"}
You can use Route:resource and still provide other methods. Place the route you need just before that particular Route::resource line.
Eg:
Route::model('things', 'Thing');
Route::get('things/{things}/owner', 'ThingController#getOwner');
Route::resource('things', 'ThingController');
Then create the corresponding method in your controller.
public function getOwner($things) {
return Response::json($things->owner()->get());
}
Here is the official documentation from the Laravel 4.2 docs:
Source: http://laravel.com/docs/controllers#resource-controllers
Adding Additional Routes To Resource Controllers
If it becomes necessary for you to add additional routes to a resource controller beyond the default resource routes, you should define those routes before your call to Route::resource:
Route::get('photos/popular');
Route::resource('photos', 'PhotoController');