In this section I will describe how to integrate Viewi with Symfony.
You can find the source code here github.com/ivanvoitovych/viewi-symfony-demo.
First, we will need a new Response class for handling direct url invocations and getting a raw data without modifications:
<?php namespace App\Adapters; use Symfony\Component\HttpFoundation\JsonResponse; class RawJsonResponse extends JsonResponse { private $rawData = null; /** * @param mixed $data The response data * @param int $status The response status code * @param array $headers An array of response headers * @param bool $json If the data is already a JSON string */ public function __construct($data = null, int $status = 200, array $headers = [], bool $json = false) { $this->rawData = $data; parent::__construct($data, $status, $headers, $json); } public function setData($data = []) { $this->rawData = $data; parent::setData($data); } public function getRawData() { return $this->rawData; } }
Then we need to adapt Viewi components to be served from Symfony. Let us create a ViewiSymfonyComponent which will be an entry point to Viewi components.
<?php namespace App\Adapters; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Viewi\App; use Viewi\WebComponents\Response as ViewiResponse; class ViewiSymfonyComponent { public function __invoke(Request $request): Response { $params = $request->attributes->all(); $component = $params['__viewi_component']; $response = App::run($component, $params); if (is_string($response)) { // html return new Response( $response ); } else if ($response instanceof ViewiResponse) { /** @var ViewiResponse $response */ if ($response->Stringify) { // ViewiResponse with the object (should never happen, Symfony handles the API) return new JsonResponse( $response->Content, $response->StatusCode, $response->Headers ); } return new Response( $response->Content, $response->StatusCode, $response->Headers ); } else { // json (should never happen, Symfony handles the API) return new JsonResponse($response); } } }
Then we need an adapter.
Please, do not use Kernel dev in production.
<?php namespace App\Adapters; use App\Kernel; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; use Viewi\Routing\RouteAdapterBase; use Symfony\Component\HttpKernel\HttpKernelInterface; use Viewi\Routing\Route; use Viewi\Routing\RouteItem; class ViewiSymfonyAdapter extends RouteAdapterBase { private int $index = 0; // unique names public function register($method, $url, $component, $defaults) { // skip } public function handle($method, $url, $params = null) { // !!do not use Kernel dev in production!! $kernel = new Kernel('dev', false); $request = Request::create($url, $method, $params ?? []); $response = $kernel->handle($request, HttpKernelInterface::SUB_REQUEST); if($response instanceof RawJsonResponse) { return $response->getRawData(); } return json_decode($response->getContent()); } public function registerRoutes(RoutingConfigurator $routes) { $viewiRoutes = Route::getRoutes(); /** @var RouteItem $route */ foreach ($viewiRoutes as $viewiRoute) { $route = $routes->add($viewiRoute->component . (++$this->index), $viewiRoute->url) ->controller(ViewiSymfonyComponent::class) ->methods([$viewiRoute->method]); $defaults = ['__viewi_component' => $viewiRoute->component] + ($viewiRoute->defaults ?? []); $route->defaults($defaults); } } }
Now let us use it in our Symfony application. For that we need to modify Kernel. Register an adapter inside of boot method. And register routes inside of configureRoutes method.
<?php namespace App; use App\Adapters\ViewiSymfonyAdapter; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\HttpKernel\Kernel as BaseKernel; use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; use Viewi\Routing\Route; class Kernel extends BaseKernel { use MicroKernelTrait; private ViewiSymfonyAdapter $viewiAdapter; public function boot() { $this->viewiAdapter = new ViewiSymfonyAdapter(); Route::setAdapter($this->viewiAdapter); include __DIR__ . '/ViewiApp/viewi.php'; parent::boot(); } protected function configureRoutes(RoutingConfigurator $routes): void { $extensions = '{php,yaml}'; $routes->import('../config/{routes}/' . $this->environment . "/*.$extensions"); $routes->import("../config/{routes}/*.$extensions"); $routes->import("../config/{routes}.$extensions"); $this->viewiAdapter->registerRoutes($routes); } }