PHP.GT

Binding data to the DOM

Instead of building HTML strings in PHP and echoing them into the response, WebEngine pages bind data into an existing HTML document. That keeps the structure of the page in the view file and keeps the PHP concerned with data and behaviour rather than markup assembly.

Basic binding concepts

The usual binding syntax is the data-bind attribute. It tells WebEngine which part of an element should receive a value.

For example:

<h1 data-bind:text="title">Default title</h1>
<img data-bind:src="photoUrl" alt="" />

In page logic, we can then bind values such as title and photoUrl into the document:

use GT\DomTemplate\Binder;

function go(Binder $binder):void {
	$binder->bindKeyValue("title", "Title from PHP");	
	$binder->bindKeyValue("photoUrl", "/asset/photo/dynamic.jpg");	
}

Common binding targets include:

  • text for text content
  • html for raw HTML content
  • value for form values
  • normal attributes such as src, href, class, and alt

This development pattern allows HTML to include default content, which will remain in the page if PHP doesn’t bind a value to it.

Binding different shapes of data

Binding can work with single values, associative arrays, ordinary objects, and mapped objects that describe how their data should appear in the page.

Binding individual keys

When we only need to bind one or two values, bindKeyValue is usually the clearest option.

Page view:

<h1>Hello, <span data-bind:text="name">guest</span></h1>
<a data-bind:href="profileUrl" href="/login">View profile</a>

Page logic:

function go(Binder $binder):void {
	$binder->bindKeyValue("name", "Cody");
	$binder->bindKeyValue("profileUrl", "/user/cody");
}

Note that the HTML is perfectly valid before binding: the heading reads Hello, guest, and the View profile link goes to the login screen, but once the page is bound, real data can be injected to add user-specific dynamic content.

bindKeyValue is useful when the page needs only a few explicit values and each bind should remain obvious at a glance.

Binding associative arrays

When several values naturally belong together, bindData is usually more concise than repeated bindKeyValue calls.

Page view:

<h1 data-bind:text="username">Guest</h1>
<p>Email: <span data-bind:text="email">guest@example.com</span></p>
<p>Status: <span data-bind:text="status">Offline</span></p>

Page logic:

function go(Binder $binder):void {
	$binder->bindData([
		"username" => "Ada",
		"email" => "ada@example.com",
		"status" => "Reviewing pull requests",
	]);
}

bindData can take any bindable key-value data source. In practice that usually means an associative array, an object with public properties, or an object that maps its own data for binding.

Binding objects

Associative arrays are fine for quick examples, but in real applications we often already have proper objects representing our data.

DomTemplate can bind those objects directly, which means we can keep the type safety and behaviour of our domain model instead of flattening everything into arrays first, and objects also come with additional benefits when binding.

PHP class:

readonly class Customer {
	public function __construct(
		public string $id,
		public string $name,
		public DateTime $createdAt,
	) {}
	
	#[BindGetter]
	public function getAgeString():string {
		$now = new DateTime();
		$interval = $this->createdAt->diff($now);

		if ($interval->y > 0) {
			return $interval->y . " years";
		}

		if ($interval->m > 0) {
			return $interval->m . " months";
		}

		return $interval->d . " days";
	}
}

Page view:

<h1 data-bind:text="name">Customer name</h1>
<p>Customer ID: <span data-bind:text="id">000</span></p>
<time data-bind:text="createdAt">2000-01-01</time> (<span data-bind:text="ageString">0 days ago</span>)

Page logic:

$customer = new Customer(
	"abc123",
	"Cody",
	new DateTime("2016-07-06 15:04:00"),
);

$binder->bindData($customer);

Note how the object is constructed with specific values, but the object can represent calculated values such as ageString for displaying the age of the Customer.

Read more detail at https://www.php.gt/docs/DomTemplate/Binding-objects/

Binding lists

For lists and repeated structures, DomTemplate can clone and bind repeated elements from iterable data. That is what makes table rows, card lists, and repeated content blocks practical without hand-building every node in PHP.

Page view:

<ul>
	<li data-list="orders">
		<span data-bind:text="reference">REF-000</span>
		<strong data-bind:text="total">0.00</strong>
	</li>
</ul>

Page logic:

function go(Binder $binder):void {
	$binder->bindData([
		"orders" => [
			["reference" => "REF-101", "total" => "19.99"],
			["reference" => "REF-102", "total" => "42.50"],
		],
	]);
}

The element marked with data-list="orders" is cloned once per item, and each cloned element is then bound with that item’s data. The value of orders could also be the property of a more complex object, rather than an associative array as in the example above.

Read more detail at https://www.php.gt/docs/DomTemplate/Binding-lists/

Page logic workflow

The most useful way to think about binding in page logic is as the last step of request orchestration:

  1. page logic gathers data
  2. application classes shape or load that data
  3. the document or part of the document receives the bound values

In practice, the page view should already describe the structure of the response. The job of page logic is then to decide what values belong in that structure.

That separation keeps the HTML readable on its own and stops the PHP from turning into string-building code. It also makes the binding surface explicit: if the page needs a customerName, an orderTotal, or a list of orders, those requirements are visible directly in the markup.

When the page gets more complex, it also becomes easier to move the data-loading and business rules into application classes while leaving the final binding step in the page entry point.

Common patterns

Some binding patterns show up repeatedly in real pages:

  • detail pages where one object or record is bound into a fixed layout
  • dashboards and account pages where several small sections each bind their own data
  • tables and lists rendered from arrays, iterators, or result sets
  • status badges, links, and images where only attributes change while the structure stays fixed
  • forms that bind existing values back into inputs during editing

The common thread is that the document structure already exists in the HTML file. If the structure is stable and the changing part is the data, binding is usually the cleanest approach. If the structure itself must be rearranged heavily, DOM manipulation may be a better fit for that part of the page.

Binding is provided by DomTemplate, which is documented in more detail at https://www.php.gt/docs/DomTemplate/.


Now we know how to get data out of PHP and on to the page, read user input for information on how to get data back from the browser to PHP, or see how to perform DOM manipulation manually.