I am building a web app + REST server with Laravel 5.5 so that the users can either access the services online with a web interface or indirectly use the APIs via the mobile app.
Now the objective would be to have the same controllers capable of handling both API and direct requests leveraging on Laravel built-in double routing and automatic JSON responses for FormRequests.
The main problems I am figuring are:
How to handle JSON (for API access) and HTML view (for web visitors) responses on the same controller function?
How to manage "Resource not found" errors in the controller and subsequently reply to the user in the desired way?
A possible approach to the second issue would be to use "findOrFail" and then catch the exception, looking whether the request has got an "Accpet" header and reply accordingly but it looks quite bulky.
Here is a brief overview of a controller I am working on; I haven't implemented any checks on the retrieved data yet.
class UsersController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$users = User::all();
return UserResource::collection($users);
}
/**
* Store a newly created resource in storage.
*
* #param \Washery\Http\Request\StoreUser $request
* #return \Illuminate\Http\Response
*/
public function store(StoreUser $request)
{
User::create($request->all());
return response()->json(['message' => 'success'], 200);
}
/**
* Display the specified resource.
*
* #return \Illuminate\Http\Response
*/
public function show($id)
{
$user = User::find($id);
return new UserResource($user);
}
/**
* Update the specified resource in storage.
*
* #param \Washery\Http\Request\UpdateUser $request
* #return \Illuminate\Http\Response
*/
public function update(UpdateUser $request)
{
User::update($request->all());
return response()->json(['message' => 'success'], 200);
}
/**
* Remove the specified resource from storage.
*
* #param \Washery\User $user
* #return \Illuminate\Http\Response
*/
public function destroy($id)
{
User::find($id)->delete();
return response()->json(['message' => 'success'], 200);
}
}
An approach would be to know where the request is coming from. If it comes from the mobile (API request), then return JSON, else, return a view.
if ($request->expectsJson()) {
return response()->json(['message' => 'success']); // No need to put 200 here.
} else {
return view('view.path');
}
You can learn more about the request api here: https://laravel.com/api/5.5/Illuminate/Http/Request.html
Related
Im having some trouble with Laravel policies and cant work out why this isnt working?
I have a policy attached to a model with all the regular methods (index, create, show etc)
The index page is working fine, but i keep getting a 403 page not found when going to the view page?
As you can see in the policy i have returned true whether the check is succesfull or not and it still returns a 403
ShippingController
/**
* Display the specified resource.
*
* #param int $id
* #param ShippingModel $shippingModel
* #return \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\View\View
* #throws \Illuminate\Auth\Access\AuthorizationException
*/
public function show($id, ShippingModel $shippingModel)
{
$this->authorize('view', ShippingModel::class);
return view('pages.warehouse.shipping.show');
}
ShippingModelPolicy
/**
* Determine whether the user can view the model.
*
* #param \App\Models\User $user
* #param \App\Models\ShippingModel $shippingModel
* #return \Illuminate\Auth\Access\Response|bool
*/
public function view(User $user, ShippingModel $shippingModel)
{
if ($user->isSuperAdmin() || $user->hasPermissionTo(205, 'web')) {
return true;
}
return true;
}
I have added the authorize in the method even though i have the authorizeController defined as well but it still does not work
public function __construct()
{
$this->authorizeResource(ShippingModel::class, 'shippingModel');
}
Your view policy method is dependent on a model, but the check on the controller doesn't account for a model. Delete the second argument and only leave User $user
I am using laravel 5.5 and I am trying to use a automatic route to the controller but it isn't working
In the web.php(the routing file for this version)
I have the follow line
Route::resource('panel', 'panel');
Route::resource('/', 'HomeController');
In the panel I have the follow actions
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
class panel extends Controller{
public function index(){
return \View::make('panel.index');
}
public function registrar(){
return \View::make('panel.registrar');
}
}
but it's only calling the index() view
the registrar() view is not being called when user acess the url
site.com/panel/registrar
the follow erro is printing in the screen
"Method [show] does not exist on [App\Http\Controllers\panel]."
I tried to use the base_controller but it don't work too
"Class 'App\Http\Controllers\Base_Controller' not found"
is there an way to identify these actions ?
Resource routing sets up 7 specific routes, that is 7 specific methods you need on the controller, 7. If you dont want all 7 of those routes you have to define it that way.
Resource routing is not implicit controllers. It does not look at the method on the controller then make routes .. Resource routing is a 'specific' thing. We do not have implicit controllers any more in Laravel as there is really no point.
Laravel 5.5 Docs - Controllers - Resource Controllers
You have routes that are created that point to methods that don't exist, that is what the error is.
Also, the first argument to Route::resource is a resource 'name', not a PATH. It is not technically a URI. It is a name of a resource.
Route::resource('/', ...) // not a name
This is a resource controller with basic CRUD operations, so in order to work you have to define the rest methods like in your case you should add a method show() and then render the view you want in that method.
A resource controller must have the following methods defined:
class TestController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}
And base controller obviusly it is not Base_Controller but its Controller
For more please reffer here Laravel 5.5 Resource Controllers
Change this resourse to simple get , if you don't need all resource methods
Route::get('/panel', 'panel#index');
Route::get('/panel/registrar', 'panel#registrar');
And use home instead just / to get unconflicted url
Route::resource('home', 'HomeController');
I have developed a Laravel 4 application a few years ago which hasn't broken since. All of a sudden users were seeing others users information. Basically what happens is I use Auth::attempt to check the user and, if valid, I use Auth::user() to get the user. What is happening is there is only one session being written to the storage/sessions folder and whenever a new user logs in, this session is overridden with the new values. Hence, it will show the last persons data. I have used EXACTLY the same solution on another server and it makes a new session whenever someone tries to login.
My question is why is it only creating one session? How can I fix this please?
It is on a shared hosting platform and I am assuming that with a PHP update or something, something has broken. As mentioned it worked for years and now has stopped which means that it probably is a configuration. Please can you help me?
Source Code:
class SessionsController extends BaseController {
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
if (Auth::check()) return Redirect::to('/dashboard');
return View::make('login');
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store()
{
if (Auth::attempt(array('email'=>Input::get('email'),'password'=>Input::get('password'),'usertype_id'=>'2'))){
return Redirect::to('/dashboard');
}
return Redirect::back()->withInput()->with('flash_error', 'Login credentials are incorrect.');
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
*/
public function update($id)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy()
{
Auth::logout();
return Redirect::to('/login');
}
}
Then I use it as follows in other Controllers:
$student = User::find(Auth::user()->id)->student;
I'm bulding API Laravel 5 application with RESTful controllers. I have method destroy defined this way in controller:
public function destroy($id)
{
App::abort(404);
}
because at the moment I don't want to handle it. The strange thing is when I use such code, I get 404 header but also get output from my show method:
public function show($id)
{
die('show method');
}
so when using DELETE method for my resource I get 404 code with output show method.
I'm 100% sure I'm launching destroy method, because if I put in my destroy method:
public function destroy($id)
{
die('destroy');
}
I will have displayed destroy with 200 status code
I'v tested it in PhpStorm but also with this Firefox addon and in both cases result is the same.
The question is - what is going here and how to return just 404 code without data or with empty data?
EDIT
I've investigated this issue further and what I discovered. If I run my app on localhost with:
DELETE http://lara404/test/1
I get pure 404 error as it should be.
I copied exact same code and run it in Vagrant. I run url:
DELETE http://lara404.app/test/1
and now I'm getting 404 code with abcdef message.
The only things I changed in default installation is:
1) adding at the beginning of routes.php
$router->resource('test','TestController');
2) Putting into TestController the following code:
<?php namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App;
class TestController extends Controller {
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
//
dd('xxx');
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store()
{
//
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
//
return "abcdef";
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
*/
public function update($id)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy($id)
{
//
App::abort(404);
}
}
3) Commenting in Kernel.php line:
'Illuminate\Foundation\Http\Middleware\VerifyCsrfToken'
I've just checked it also with fresh installation of Laravel 5 (not using any specific commit) and exactly same happens - the same code launched on localhost works fine and the same code running on Vagrant goes to show method also
You need this if using json -
public function destroy($id)
{
return Response::json(null, 404);
}
Or this if not using json -
public function destroy($id)
{
return Response::make("", 404);
}
I get the error when trying to make a post call to /api/subject/search
I assume it's a simple syntax error I'm missing
I have my api routes defined below
Route::group(array('prefix' => 'api'), function()
{
Route::post('resource/search', 'ResourceController');
Route::resource('resource', 'ResourceController');
Route::post('subject/search', 'SubjectController');
Route::resource('subject', 'SubjectController');
Route::resource('user', 'UserController');
Route::controller('/session', 'SessionController');
Route::post('/login', array('as' => 'session', 'uses' => 'SessionController#Store'));
});
And my controller is mostly empty
class SubjectController extends \BaseController
{
public function search()
{
$subjects = [];
if((int)Input::get('grade_id') < 13 && (int)Input::get('grade_id') > 8)
$subjects = Subject::where('name', 'like', '%HS%')->get();
else
$subjects = Subject::where('name', 'not like', '%HS%')->get();
return Response::json([
'success' => true,
'subjects' => $subjects->toArray()
]);
}
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store()
{
//
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
*/
public function update($id)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy($id)
{
//
}
}
You need to specify the method.
try
Route::post('subject/search', 'SubjectController#search');
See the named route example:
Laravel Docs
In your case I think search is not resolved by the controller to load the search() method. You are also sending a POST for search functionality and I guess it's better to do a GET request since POST and PUT are for storing data.
Conventions
When creating API's it's a good thing to stick to naming conventions and patterns.
http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api
Solution
Your route could be simpler like this: api.yourdomain.com/api/subject?search=term1,term2. Doing this with a GET query makes it going to the index() method. There you can check the GET params and do your search stuff and return.
Check this for the cleanest and truely RESTful way to make an API in Laravel:
How do I create a RESTful API in Laravel to use in my BackboneJS app
I got same error when accessing object at index of an empty array in view blade php file.