Skip to content

DotBridge Example

Open the live demo at /documentation/examples/run/bridge (also available at /documentation/examples/run/forms3).

Introduction

This example is the live Examples:Forms@index3! / @submit3! page. Template tags call PHP over an encrypted AJAX POST to the current page URL. Inputs can be filtered, encrypted, rate-limited, and bound to a specific URL.

Prerequisites

Start from the named-forms example so the Examples module and Forms controller already exist.

Creating the View

The live demo uses a standalone view at /app/modules/Examples/views/bridge.view.php. The file is a complete HTML document.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>{{ var: $title }} — DotApp PHP Framework 2.0</title>
  <link rel="stylesheet" href="/assets/modules/Examples/css/examples.css" />
</head>
<body class="ex-body">
  <header class="ex-top">
    <a href="{{ var: $docsUrl }}">Documentation</a>
    <span>DotBridge</span>
  </header>
  <main class="ex-main">
    <h1>DotBridge</h1>
    <p class="ex-lead">Each button calls PHP over the current page URL.</p>
    <form method="post" class="ex-card" data-dotapp-nojs>
      <label for="email">Email</label>
      <input type="text" id="email" {{ dotbridge:input="email.address(email, 6, email_ok, email_bad)" }} placeholder="Enter your email" />
      <label for="category">Category</label>
      <select id="category" {{ dotbridge:input="category" }}>
        <option value="" disabled selected>Select a category</option>
        <option value="{{ enc(additionalKey): "AdminVal" }}">Admin</option>
        <option value="{{ enc(additionalKey): "EditorVal" }}">Editor</option>
        <option value="{{ enc(additionalKey): "OtherVal" }}">Other</option>
      </select>
      <div class="ex-bridge-actions">
        <div class="ex-btn" {{ dotbridge:on(click)="example.showEmailCategory(email.address, category)" oneTimeUse }}>Submit only once</div>
        <div class="ex-btn" {{ dotbridge:on(click)="example.showEmailCategory2(email.address, category)" regenerateId rateLimit(60,2) rateLimit(3600,5) url(/documentation/examples/run/forms3/) }}>Submit 2× per minute</div>
        <div class="ex-btn" {{ dotbridge:on(click)="example.showEmailCategory3(email.address, category)" regenerateId }}>Submit no limit</div>
      </div>
    </form>
    <div class="output" id="output1">Submit with the first button to see the result</div>
    <div class="output" id="output2">Submit with the second button to see the result</div>
    <div class="output" id="output3">Submit with the third button to see the result</div>
  </main>
  <script src="/assets/dotapp/dotapp.js"></script>
  <script src="/assets/modules/Examples/js/bridge.js"></script>
</body>
</html>

Adding Styles

Shared example chrome lives at /app/modules/Examples/assets/css/examples.css and is served as /assets/modules/Examples/css/examples.css.

Creating the Layout

setView('bridge') renders the full HTML document. Modules can also wrap views with {{ layout:name }}. This demo keeps the view self-contained.

DotBridge Inputs

Mark fields with {{ dotbridge:input="name" }} or the short form dotbridge="name". Nested names such as email.address arrive in PHP as $request->data(true)['data']['email.address'].

Built-in filters include email, url, phone, password, date, time, creditcard, username, and ipv4. Register more with Bridge::addFilter($name, $callback).

DotBridge Filters

The email field uses email.address(email, 6, email_ok, email_bad): the email filter, a minimum length of 6, and optional success/failure callbacks. Category options are encrypted in the template with {{ enc(additionalKey): "AdminVal" }} and decrypted in PHP with the same extra key.

DotBridge Events

Bind a click (or another event) with {{ dotbridge:on(click)="functionName(arg1, arg2)" }}. Modifiers on the same tag control reuse and limits:

  • oneTimeUse — the key is consumed after one successful call.
  • regenerateId — a fresh id is issued after the call.
  • rateLimit(seconds,count) — you can stack several windows, e.g. rateLimit(60,2) rateLimit(3600,5).
  • url(/path) — POST target for that call. An invalid bound URL returns HTTP 403 with error_code 6.

DotBridge JavaScript Functions

Pair each template function name with $dotapp().bridge(name, event). Hooks: before, onValueError, after, and onResponseCode. Wait for the dotapp event if the library is still loading.

Configuring Routes

Register the page GET and bind each bridge function to the same URLs in initialize($dotApp):

$bridgePages = array_merge($pair($p . '/forms3'), $pair($p . '/bridge'));
Router::get($bridgePages, 'Examples:Forms@index3!', Router::STATIC_ROUTE);
foreach (['example.showEmailCategory', 'example.showEmailCategory2', 'example.showEmailCategory3'] as $fn) {
    Router::bridge($bridgePages, $fn, 'Examples:Forms@submit3!', Router::STATIC_ROUTE);
}

Router::bridge($urls, $functionName, $handler, Router::STATIC_ROUTE) scopes the handler to those page URLs. Closures use Bridge::listen() with the same argument order.

Updating the Controller

Methods are public static and take $request. The GET action renders the view. The bridge action reads the payload and returns a string (or an array), which becomes the JSON body.

public static function index3($request)
{
    return self::view('bridge', [
        'title' => 'DotBridge demo',
        'docsUrl' => '/documentation/examples/dotbridge',
    ]);
}

public static function submit3($request)
{
    $payload = $request->data(true)['data'] ?? [];
    $email = (string) ($payload['email.address'] ?? '');
    $category = Crypto::decrypt((string) ($payload['category'] ?? ''), 'additionalKey');
    if ($category === false) {
        $category = '(invalid category)';
    }
    return 'This is reply from submit3() function. Email: ' . $email . ', category: ' . $category;
}

Failed decryption returns false — compare with === false.

Implementing JavaScript

Live file: /app/modules/Examples/assets/js/bridge.js. The client API is $dotapp.

(function () {
  var runMe = function ($dotapp) {
    function before(selector) {
      var categoryInput = $dotapp('[dotbridge-input="category"]').val();
      if (categoryInput === null || categoryInput === "") {
        alert("Select a category.");
        return $dotapp().halt();
      }
      $dotapp(selector).html("Loading...").removeClass("error").removeClass("success").addClass("loading");
    }

    function after(selector, body) {
      var text = "";
      if (typeof body === "string") text = body;
      else if (body && body.body) text = body.body;
      else if (body) text = String(body);
      if (text) {
        $dotapp(selector).html(text).removeClass("error").removeClass("loading").addClass("success");
      }
    }

    function onResponse(selector, data) {
      var reply = $dotapp().parseReply(data);
      var text = (reply && reply.status_txt) ? reply.status_txt : "Request failed.";
      $dotapp(selector).html(text).addClass("error").removeClass("loading").removeClass("success");
    }

    $dotapp()
      .bridge("example.showEmailCategory", "click")
      .before(function () { return before("#output1"); })
      .onValueError(function (inputname) {
        if (inputname == "email.address") alert("Enter a valid email address.");
      })
      .after(function (body) { after("#output1", body); })
      .onResponseCode(function (status, text) { onResponse("#output1", text); }, 429);
  };
  if (window.$dotapp) runMe(window.$dotapp);
  else window.addEventListener("dotapp", function () { runMe(window.$dotapp); }, { once: true });
})();

Repeat the same .bridge() chain for example.showEmailCategory2 and example.showEmailCategory3 with their output selectors.

Error Codes

Success is HTTP 200 with JSON { status: 1, body: <your return> } and the header X-Answered-By: dotbridge.

  • HTTP 400, error_code 1: CRC check failed.
  • HTTP 403, error_code 2: bridge key mismatch.
  • HTTP 404, error_code 3: function not registered or not callable.
  • HTTP 429, error_code 4: rate limit, invalid id, or missing valid key.
  • HTTP 403, error_code 5: CSRF referer mismatch.
  • HTTP 403, error_code 6: invalid dotbridge-url.

Live Demo

/documentation/examples/run/bridge and /documentation/examples/run/forms3 render the same page.