Skip to content

Instantly share code, notes, and snippets.

@dsazup
Created April 14, 2020 17:27
Show Gist options
  • Save dsazup/996cd6ca7f1ff50cc31c0823fe26b2a7 to your computer and use it in GitHub Desktop.
Save dsazup/996cd6ca7f1ff50cc31c0823fe26b2a7 to your computer and use it in GitHub Desktop.
Laravel response()->to()
<?php
namespace App\Providers;
use Illuminate\Routing\ResponseFactory;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Contracts\View\Factory as ViewFactoryContract;
use Illuminate\Contracts\Routing\ResponseFactory as ResponseFactoryContract;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
$this->app->singleton(ResponseFactoryContract::class, function ($app) {
return new class($app[ViewFactoryContract::class], $app['redirect']) extends ResponseFactory implements Responsable {
protected $callbacks = [];
public function to($type, $callback) {
$this->callbacks[$type] = $callback;
return $this;
}
public function toResponse($request)
{
throw_unless($callback = $this->callbacks[$request->format()] ?? null, NotFoundHttpException::class);
return app()->call($callback);
}
};
});
}
}
@glaszig
Copy link

glaszig commented May 14, 2023

nice. that's some rails-like magic 🪄 helped me out. thanks.

@SlimGee
Copy link

SlimGee commented Dec 30, 2023

Here's an adapted version using macros instead of extending the response factory, I had issues with return types when extending

<?php

namespace App\Providers;

use Closure;
use Illuminate\Routing\ResponseFactory;
use Illuminate\Support\ServiceProvider;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        ResponseFactory::macro('to', function (): mixed {
            ResponseFactory::macro('do', function (Closure $callback): mixed {
                throw_unless($callback, NotFoundHttpException::class);

                return app()->call($callback, ['format' => request()->hasHeader('Turbo-Frame') ? 'turbo_frame' : request()->format()]);
            });

            if (count(func_get_args()) > 0) {
                return $this->do(...func_get_args());
            }

            return $this;
        });
    }
}

I also added support to work with hotwired Turbo frames

In the controller I would have something like this

//controller
/**
 * Show the form for editing the specified resource.
 */
public function edit(Course $course): Renderable
{
    return respond_to()->do(fn ($format) => match ($format) {
        'turbo_frame' => view('admin.courses._form', [
            'course' => $course,
        ]),
        'html' => view('admin.courses.edit', [
            'course' => $course,
        ]),
    });
}

Then I have the helper respond_to

use Illuminate\Routing\ResponseFactory;

if (! function_exists('respond_to')) {

    function respond_to(): ResponseFactory
    {
        return response()->to();
    }
}

But you can just as easily call response()->to($callback) I just found adding an extra to()->do($callback) more "railsy" and descriptive

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment