PHPackages                             tobento/app-view - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. tobento/app-view

ActiveLibrary[Templating &amp; Views](/categories/templating)

tobento/app-view
================

App view support.

2.0.3(2mo ago)0218↓76.5%20MITPHPPHP &gt;=8.4

Since Mar 23Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/tobento-ch/app-view)[ Packagist](https://packagist.org/packages/tobento/app-view)[ Docs](https://www.tobento.ch)[ RSS](/packages/tobento-app-view/feed)WikiDiscussions 2.x Synced today

READMEChangelog (10)Dependencies (33)Versions (18)Used By (20)

App View
========

[](#app-view)

The App View package provides a flexible foundation for building web application interfaces.
It includes convenient helpers for creating menus, forms, layouts, and other UI elements.

A default layout is included, styled with [Basis CSS](https://github.com/tobento-ch/css-basis), which you may use or replace with your own design.
Some [App Bundles](https://github.com/tobento-ch?tab=repositories&q=app) rely on this default layout, so keeping it available can be beneficial depending on your setup.

Table of Contents
-----------------

[](#table-of-contents)

- [Getting Started](#getting-started)
    - [Requirements](#requirements)
- [Documentation](#documentation)
    - [App](#app)
    - [View Boot](#view-boot)
        - [Rendering Views](#rendering-views)
        - [Global View Data And Variables](#global-view-data-and-variables)
        - [Available View Macros](#available-view-macros)
    - [Menus Boot](#menus-boot)
    - [Form Boot](#form-boot)
        - [CSRF Protection](#csrf-protection)
        - [Form Messages](#form-messages)
    - [Messages Boot](#messages-boot)
    - [Breadcrumb Boot](#breadcrumb-boot)
    - [Table Boot](#table-boot)
    - [Default Layout](#default-layout)
        - [Views](#views)
        - [Exception Views](#exception-views)
    - [Themes](#themes)
        - [Theme Views](#theme-views)
        - [Theme Assets](#theme-assets)
- [Credits](#credits)

---

Getting Started
===============

[](#getting-started)

Add the latest version of the app view project running this command.

```
composer require tobento/app-view

```

Requirements
------------

[](#requirements)

- PHP 8.4 or greater

Documentation
=============

[](#documentation)

App
---

[](#app)

Check out the [**App Skeleton**](https://github.com/tobento-ch/app-skeleton) if you are using the skeleton.

You may also check out the [**App**](https://github.com/tobento-ch/app) to learn more about the app in general.

View Boot
---------

[](#view-boot)

The view boot does the following:

- Migrates views and css assets for default layout
- Implements [ViewInterface](https://github.com/tobento-ch/service-view#view)
- Adds global view data from different services if available
- Adds multiple view macros

```
use Tobento\App\AppFactory;

// Create the app
$app = new AppFactory()->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'vendor', 'vendor')
    ->dir($app->dir('app').'views', 'views', group: 'views')
    ->dir($app->dir('root').'public', 'public');

// Adding boots
$app->boot(\Tobento\App\View\Boot\View::class);

// Run the app
$app->run();
```

### Rendering Views

[](#rendering-views)

You can render views in several ways:

**Using the app**

```
use Tobento\App\AppFactory;
use Tobento\Service\View\ViewInterface;

// Create the app
$app = new AppFactory()->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'vendor', 'vendor')
    ->dir($app->dir('app').'views', 'views', group: 'views')
    ->dir($app->dir('root').'public', 'public');

// Adding boots
$app->boot(\Tobento\App\View\Boot\View::class);
$app->booting();

$view = $app->get(ViewInterface::class);
$content = $view->render(view: 'about', data: []);

// or using the app macro:
$content = $app->renderView(view: 'about', data: []);

// Run the app
$app->run();
```

**Using autowiring**

You can also request the `ViewInterface::class` in any class resolved by the app.

```
use Tobento\Service\View\ViewInterface;

class SomeService
{
    public function __construct(
        protected ViewInterface $view,
    ) {}
}
```

**Using the view boot**

```
use Tobento\App\Boot;
use Tobento\App\View\Boot\View;

class AnyServiceBoot extends Boot
{
    public const BOOT = [
        // you may ensure the view boot.
        View::class,
    ];

    public function boot(View $view)
    {
        $content = $view->render(view: 'about', data: []);
    }
}
```

Check out the [**View Service**](https://github.com/tobento-ch/service-view) to learn more about it.

**Using the responser**

If you have booted the [App Http - Requester And Responser](https://github.com/tobento-ch/app-http#requester-and-responser-boot) boot, you may use the `render` method:

```
use Tobento\Service\Responser\ResponserInterface;
use Psr\Http\Message\ResponseInterface;

class SomeHttpController
{
    public function index(ResponserInterface $responser): ResponseInterface
    {
        return $responser->render(view: 'register', data: []);
    }
}
```

### Global View Data And Variables

[](#global-view-data-and-variables)

It adds the following global view data and variables accessible in your view files:

```
// by variable:
$htmlLang
$locale
$routeName

// by view get method:
$htmlLang = $view->get('htmlLang', 'en');
$locale = $view->get('locale', 'en');
$routeName = $view->get('routeName', '');
```

**$htmlLang / $locale**

Code snippet from the view boot:

```
use Tobento\Service\Language\LanguagesInterface;

if ($this->app->has(LanguagesInterface::class)) {
    $locale = $this->app->get(LanguagesInterface::class)->current()->locale();
    $view->with('htmlLang', str_replace('_', '-', $locale));
    $view->with('locale', $locale);
}
```

**$routeName**

Code snippet from the view boot:

```
use Tobento\Service\Routing\RouterInterface;

if ($this->app->has(RouterInterface::class)) {
    $matchedRoute = $this->app->get(RouterInterface::class)->getMatchedRoute();
    $view->with('routeName', $matchedRoute?->getName() ?: '');
}
```

**Adding global data and variables**

You may add more global data by using a boot:

```
use Tobento\App\Boot;
use Tobento\Service\View\ViewInterface;

class SomeGlobalViewDataBoot extends Boot
{
    public function boot()
    {
        // only add if view is requested:
        $this->app->on(ViewInterface::class, function(ViewInterface $view) {

            // using the with method:
            $view->with(name: 'someVariable', value: 'someValue');

            // using the data method:
            $view->data([
                'someVariable' => 'someValue',
            ]);
        });
    }
}
```

### Available View Macros

[](#available-view-macros)

**app**

Returns the app instance.

```
use Tobento\App\AppInterface;

var_dump($view->app() instanceof AppInterface);
// bool(true)
```

**assetPath**

The `assetPath` function returns the fully qualified path to your application's asset directory.

```
var_dump($view->assetPath('assets/editor/script.js'));
// string(33) "/basepath/assets/editor/script.js"
```

**menu**

Returns the menu for the specified name.

```
use Tobento\Service\Menu\MenuInterface;

var_dump($view->menu('main') instanceof MenuInterface);
// bool(true)
```

Check out the [Menu Service](https://github.com/tobento-ch/service-menu) to learn more about it in general.

**trans / etrans**

Returns the message translated by the [Translator](https://github.com/tobento-ch/service-translation#translator) if available within the app.

By default, the translator is not available, you might install the [App Translation](https://github.com/tobento-ch/app-translation) bundle to do so.

```
$translated = $view->trans(
    message: 'Hi :name',
    parameters: [':name' => 'John'],
    locale: 'de',
);

// The etrans method will escape the translated message
// with htmlspecialchars.

echo $view->etrans(
    message: 'Hi :name',
    parameters: [':name' => 'John'],
    locale: 'de',
);
```

**routeUrl**

`routeUrl()` is a helper that lets you generate a URL from a named route, instead of hard-coding paths in your templates.

If the application has the [Routing Boot](https://github.com/tobento-ch/app-http#routing-boot) enabled (which registers `Tobento\Service\Routing\RouterInterface`), then calling:

```
use Tobento\Service\Routing\UrlInterface;

$url = $view->routeUrl(
    name: 'route.name',
    parameters: [],

    // optionally disable throwing on missing route
    throw: false,
);

var_dump($url instanceof UrlInterface);
// bool(true)
```

This method internally calls `$router->url()`.
See the [Url Generation](https://github.com/tobento-ch/service-routing#url-generation) section to learn more.

**tagAttributes**

Returns the attributes for the specified tag name.

```
use Tobento\Service\Tag\AttributesInterface;

var_dump($view->tagAttributes('body') instanceof AttributesInterface);
// bool(true)
```

Check out the [Tag Service - Attributes Interface](https://github.com/tobento-ch/service-tag#attributes-interface) to learn more about it in general.

**date / dateTime / formatDate**

You may use `date`, `dateTime` and `formatDate` methods to format and display any dates using the [Date Formatter](https://github.com/tobento-ch/service-dater#date-formatter).

```
// date:
var_dump($view->date('2024-02-15 10:15'));
// string(27) "Thursday, 15. February 2024"

var_dump($view->date(
    value: 'now',
    format: 'EE, dd. MMMM yyyy',
    locale: 'de_DE',
));
// string(19) "Sa., 23. März 2024"

// dateTime:
var_dump($view->dateTime('now'));
// string(31) "Saturday, 23. March 2024, 07:36"

var_dump($view->dateTime(
    value: 'now',
    format: 'EE, dd. MMMM yyyy, HH:mm',
    locale: 'de_DE',
));
// string(26) "Sa., 23. März 2024, 07:45"

// formatDate:
var_dump($view->formatDate(
    value: '2024-02-15 10:15',
    format: 'd.m.Y H:i',
));
// string(16) "15.02.2024 10:15"
```

Menus Boot
----------

[](#menus-boot)

The menus boot does the following:

- Implements [MenusInterface](https://github.com/tobento-ch/service-menu#menus)
- Adds menu view macro

```
use Tobento\App\AppFactory;
use Tobento\Service\Menu\MenusInterface;
use Tobento\Service\Menu\MenuInterface;
use Tobento\Service\View\ViewInterface;

// Create the app
$app = new AppFactory()->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'vendor', 'vendor')
    ->dir($app->dir('app').'views', 'views', group: 'views')
    ->dir($app->dir('root').'public', 'public');

// Adding boots
$app->boot(\Tobento\App\View\Boot\View::class);
// no need to boot as already loaded by the view boot:
// $app->boot(\Tobento\App\View\Boot\Menus::class);
$app->booting();

// Get the menus:
$menus = $app->get(MenusInterface::class);

// View menu macro:
$view = $app->get(ViewInterface::class);

var_dump($view->menu('main') instanceof MenuInterface);
// bool(true)

// This will create a new menu without storing it to the menus:
var_dump($view->createMenu('name') instanceof MenuInterface);
// bool(true)

// Run the app
$app->run();
```

Check out the [Menu Service](https://github.com/tobento-ch/service-menu) to learn more about it in general.

**Using menus boot**

You may add menu items using the menus boot:

```
use Tobento\App\Boot;
use Tobento\App\View\Boot\Menus;

class AnyServiceBoot extends Boot
{
    public const BOOT = [
        // you may ensure the menus boot.
        Menus::class,
    ];

    public function boot(Menus $menus)
    {
        $menu = $menus->menu('main');
        $menu->link('https://example.com/foo', 'Foo')->id('foo');
    }
}
```

**Using the app on method**

You may add menu items only if the menus is requested from the app.

```
use Tobento\App\Boot;
use Tobento\Service\Menu\MenusInterface;

class AnyServiceBoot extends Boot
{
    public function boot()
    {
        $this->app->on(MenusInterface::class, function(MenusInterface $menus) {
            $menu = $menus->menu('main');
            $menu->link('https://example.com/foo', 'Foo')->id('foo');
        });
    }
}
```

Form Boot
---------

[](#form-boot)

The form boot does the following:

- Implements [FormFactoryInterface](https://github.com/tobento-ch/service-form#form-factory)
- Adds form view macro
- Adds VerifyCsrfToken middleware

This boot requires the [app-http](https://github.com/tobento-ch/app-http) bundle:

```
composer require tobento/app-http

```

```
use Tobento\App\AppFactory;
use Tobento\Service\Form\FormFactoryInterface;
use Tobento\Service\Form\Form;
use Tobento\Service\View\ViewInterface;

// Create the app
$app = new AppFactory()->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'vendor', 'vendor')
    ->dir($app->dir('app').'views', 'views', group: 'views')
    ->dir($app->dir('root').'public', 'public');

// Adding boots
$app->boot(\Tobento\App\View\Boot\View::class);
$app->boot(\Tobento\App\View\Boot\Form::class);
$app->booting();

// Get the form factory:
$formFactory = $app->get(FormFactoryInterface::class);

// View form macro:
$view = $app->get(ViewInterface::class);

$form = $view->form();
// var_dump($form instanceof Form);
// bool(true)

// Run the app
$app->run();
```

Check out the [Form Service](https://github.com/tobento-ch/service-form) to learn more about it in general.

You might boot the [App Http - Error Handler Boot](https://github.com/tobento-ch/app-http#error-handler-boot) which already handles exceptions caused by the form.

### CSRF Protection

[](#csrf-protection)

[CSRF protection](https://github.com/tobento-ch/service-form#csrf-protection) is implemented and csrf token will be verified by the registered [VerifyCsrfToken Middleware](https://github.com/tobento-ch/service-form#tokenizer-psr-15-middleware).

**CSRF Token**

The form boot adds the CSRF Token to the global view data accessible in your view files:

```
$token = $view->data()->get('csrfToken');
```

**X-CSRF-TOKEN**

In addition to checking for the CSRF token as a POST parameter, the [VerifyCsrfToken Middleware](https://github.com/tobento-ch/service-form#tokenizer-psr-15-middleware) will also check for the `X-CSRF-TOKEN` request header.

When using the [Default Layout](#default-layout), in the view file `inc/head`, the token will be stored in a HTML meta tag named `csrf-token`:

```

```

You may use this meta tag to get the token while setting the `X-CSRF-TOKEN` header:

```
fetch("https://example.com", {
    method: "POST",
    headers: {
        "X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]').getAttribute('content')
    },
    body: JSON.stringify(data)
});
```

### Form Messages

[](#form-messages)

As `Tobento\Service\Form\ResponserFormFactory::class` is the default `Tobento\Service\Form\FormFactoryInterface::class` implementation you can pass messages to your form fields by the following way:

```
use Tobento\Service\Responser\ResponserInterface;
use Tobento\Service\Requester\RequesterInterface;
use Psr\Http\Message\ResponseInterface;

class RegisterController
{
    public function index(ResponserInterface $responser): ResponseInterface
    {
        // set the key corresponding to your form field name:
        $responser->messages()->add('info', 'Some info message', key: 'firstname');

        return $responser->render(view: 'register');
    }

    public function register(
        RequesterInterface $requester,
        ResponserInterface $responser,
    ): ResponseInterface {
        // validate request data:
        //$requester->input();

        // add message on error:
        $responser->messages()->add('error', 'Message error', key: 'firstname');

        // redirect - messages and input data will be flashed:
        return $responser->redirect(
            uri: 'uri',
        )->withInput($requester->input()->all());
    }
}
```

In your view file:

```
