laravel-remote-template
is a package for fetching blade templates from a remote URL.
Imagine your customer wants you to build a fully flexible application but also would like to manage the content by themself. Laravel is great for building applications - but managing content is not the focus. Maybe you have expirences in Content Management System - but hey, those aren't as flexible as Laravel in building applications. Why not use both? A CMS for the content and Laravel for the application. This package helps you to use content that is remote available for rendering in Laravel applications.
- Installation
- Configuration
- Using a fallback route
- Modify remote URL before call is executed
- Push response handlers
Simply install the package with composer:
composer require schnoop/laravel-remote-template
If you are using an older version of Laravel (< 5.5), you will also need to add our service provider
to your app configuration (config/app.php
):
'providers' => [
...
Schnoop\RemoteTemplate\RemoteTemplateServiceProvider::class,
],
Next, publish the configuration files:
php artisan vendor:publish --provider="Schnoop\RemoteTemplate\RemoteTemplateServiceProvider"
The package extends Laravels FileViewFinder class used to resolve views by name. When encountering a special remote delimiter token, the configured remote host will be used to resolve the view instead of the local filesystem.
'remote-delimiter' => 'remote:',
Now, everytime you call or include a view by name and the name starts with remote:
, the template is fetched from the remote host. Example:
@extends('remote:login')
{{-- Content --}}
@section('content')
...
@endsection
The following would still resolve the view using the default Laravel (filesystem) behaviour:
@extends('layouts.master')
{{-- Content --}}
@section('content')
...
@endsection
When resolving the remote templates, the files are downloaded to a special view folder. The view folder can be customized via the view-folder
configuration option. If caching is enabled, these files will be re-used for subsequent requests and will not fire new requests to the remote host.
You can configure multiple remote hosts with unique identifiers using the hosts
configuration option. The identifier can then be appended to the remote delimiter.
'hosts' => [
'base' => [
...
],
],
The following example will use the base
remote host to resolve the template:
@extends('remote:base::login')
{{-- Content --}}
@section('content')
...
@endsection
If a default
host is specified, it will be used when no other namespace is used. The following example will use the default
host:
@extends('remote:login')
{{-- Content --}}
@section('content')
...
@endsection
The host base URL can defined using the hosts.*.host
configuration option:
'hosts' => [
'default' => [
'host' => env('CONTENT_DOMAIN'),
],
],
Any token following the remote delimiter and host identifier will be used to construct the URL for the remote host. In the example above, a request would be made to www.yourhost.com/login
, and the response would be used as the resolved view file. If you wish to configure custom mappings, you can do so using the mappings
configuration option:
'hosts' => [
'default' => [
...
'mapping' => [
'login' => '/index.php?id=51',
],
],
],
If you have URLs that you don't want to expose via these fallback, you can configure those in the config file;
ignore-urls
: Is an array that can hold multiple urls that should not be resolved via the content host.
'ignore-urls' => [
'foo'
],
If the request url to the remote host starts with any configured ignore-urls
, you will receive an UrlIsForbiddenException instead of content:
e.g:
- http://remote-content-host.dev/foo/
- http://remote-content-host.dev/foo
- http://remote-content-host.dev/foo/bar
- http://remote-content-host.dev/foo/index.php
Instead of configuring URLs starting with an particular string, you also can deny access to urls that end with a suffix:
'ignore-url-suffix' => [
'png',
'jpg',
'jpeg',
'css',
'js',
'woff',
'ttf',
'gif',
'svg'
],
In the case above we are denying the request to any static file.
hosts.*.cache
: When set to true, requests to the remote host are only made if no matching template could be found in the view folder. If a template is found, it will be re-used for resolving the view.hosts.*.request_options
: Array of request options that will be passed to the Guzzle HTTP client when making the request to the remote host. This option can be used to configure authentication.
By using a fallback route in combination with a default view, any requests that do not match a route defined in Laravel will instead be forwarded to any of the configured remote hosts.
In your routes/web.php
file:
Route::fallback('FallbackController@fallback');
The controller would then simply call a view and pass the requesting URL:
class FallbackController extends Controller
{
/**
* Fallback.
*
* @param Request $request
*
* @return View
*/
public function fallback(Request $request): View
{
return view('cms::fallback')->with('uri', $request->getRequestUri());
}
}
And the view would pass the URL to the remote host:
@extends('remote:'.$uri)
Now, for any requests made to routes not defined in the application, a request will be made to the remote host. If a successful response is returned, it will be used as the view. Otherwise a 404
response will be returned.
Someday, you will have the case, that you would like to force the remote host to render the template based on a state in your Laravel application. A very common case is definitely to change the navigation if a user is authenticated.
To achieve this, we have a callback that will be triggered right before the call to the remote host happens:
$this->app->make('remoteview.finder')->setModifyTemplateUrlCallback(function ($url) {
return $url;
});
In this callback, you have the chance to modify the request url as needed to tell the remote host to change its template rendering:
$this->app->make('remoteview.finder')->setModifyTemplateUrlCallback(function ($url) {
$glue = '?';
if (strpos($url, $glue) !== false) {
$glue = '&';
}
if (Auth::check() === true) {
$url .= $glue . 'login=true';
}
// ..... following by more role checks e.g.
return $url;
});
Last but not least you have the option to push handlers that will be executed after the call has happened: Those handlers are assigned to response codes, that the remote host returns:
In the following example the handler will be executed only if the remote host respond with a 301 HTTP status code.
$this->app->make('remoteview.finder')->pushResponseHandler(301, function (Response $result, array $config, RemoteTemplateFinder $service) {
// Do some stuff, and return the HTML.
});
A common case is that maybe the remote host responds with 301, which is a redirect to another url. In this case we would like to parse the destination out of the response, and fetch the content from there. To achieve this, an instance of RemoteTemplateFinder is injected in the function that can be used to execute further calls.
$this->app->make('remoteview.finder')->pushResponseHandler(301, function (Response $result, array $config, RemoteTemplateFinder $service) {
return $service->fetchContentFromRemoteHost($result->getHeaderLine('Location'), $config);
});