Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

dancourse's avatar

json or webvew

Ola' laraviens,

I feel like this is painfully obvious but I'm clearly missing a coding principle, laravel cleverness or design pattern here.

All I want to do is have the same routing resource work from either,

Route::resource('auctions', 'AuctionsController')

  • auctions/1' sends back the blade webview for auction:id:1
  • auctions/1.json sends back the json object

However, it's proving a right faf and I can't seem to nail one solution properly. So it's very possible I'm trying to do something stupid and I should be using a seperate api/ route to split them?!?

Thanks,

DanC

0 likes
6 replies
bobbybouwmann's avatar

I don't think it possible, but I might be able to give you some kind of solution. When you don't return a view Laravel will return json to the browser. To make this work you can do something like this:

Route::resource('auctions', 'AuctionsController')
Route::get('auctionsjson', AuctionsController@indexjson)

Then your function can do something like this (You will get the idea):

public function index() 
{
    return view('auctions.index')->with('auctions' => Auction::all());
}

public function indexjson()
{
    return Auction::all();
}
JarekTkaczyk's avatar

@dancourse This is how you can define the routes:

Route::pattern('auctions', '^\d+(\.(json|xml)?$');
Route::resource('Auctions', 'AuctionsController');

Then you need to check the parameter passed to the controller methods, something between the lines of:

public function show($id)
{
   (strpos($id, '.') !== false)
   {
       list($id, $format) = explode('.', $id);
   }

   // do the job
}

Obviously you shouldn't do that in each method, but rather extract it. You will handle that, right?

pmall's avatar

How I do it :

# routes.php

$router->pattern('format', '.json|.csv|.whatever');

$router->get('auctions/{id}/{format?}', ['uses' => 'AuctionsController@show']);

# AuctionsController.php

public function show($id, $format = null)
{
  $auction = Auction::findOrFail($id);

  if($format == '.json') return $auction;

  return view('auctions.show', ['auction' => $auction]);
}

When I get many format and get fedup with the if statements everywhere I register a special response macro in a service provider :

# AppServiceProvider.php

public function register()
{
  $this->app->call([$this, 'registerResponseMacros']);
}

public function registerResponseMacros(Request $request, ResponseFactory $response)
{
  $response->macro('format', function($format, $resource, $default = null) use($request, $response)
  {
    if($format == '.json' or $request->wantsJson()) return $response->json($resource);
    if($format == '.csv')  return $response->file($resource);

    return $default;
  });

  $response->macro('file', function(DownloadableInterface $downloadable) use($response)
  {
    $file_name = $downloadable->getDownloadFileName();
    $file_path = $downloadable->getDownloadFilePath();

    return $response->download($file_path, $file_name);
  });
}

This way I can only use one line for the response :

public function show($id, $format = null)
{
  $auction = Auction::findOrFail($id);

  return response()->format($format, $auction, view('auctions.show', [
    'auction' => $auction
  ]);
}

First arg the desired format, second arg the data to format, third arg the default response.

But I think it would be cool if laravel request object had special features to handle the format. Something like a special token in the route pattern, which is not passed to the action's arguments and we can retrieve with something like $request->getFormat().

3 likes
robgeorgeuk's avatar

It looks like @pmall nailed it but worth mentioning that the below could be useful in some situations.

if (Request::ajax())
{
    //
}
pmall's avatar

@robgeorgeuk I think it is better to check if the request accepts json as a response or the url ends with .json. Request::ajax() wont work with api call from cURL for example.

1 like

Please or to participate in this conversation.