Skip to content

Commit

Permalink
Implement injection of data into attribute values
Browse files Browse the repository at this point in the history
  • Loading branch information
g105b committed May 28, 2018
1 parent 2aacc76 commit 34981ef
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 12 deletions.
69 changes: 61 additions & 8 deletions src/Bindable.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Gt\Dom\Element as BaseElement;
use DOMNode;
use Gt\Dom\HTMLCollection;
use stdClass;

trait Bindable {
public function bind(iterable $data, string $templateName = null):void {
Expand All @@ -14,18 +15,21 @@ public function bind(iterable $data, string $templateName = null):void {
$element = $element->documentElement;
}

$this->injectDataIntoAttributeValues($element, $data);

$this->bindExisting($element, $data);
$this->bindTemplates(
$element,
$data,
$templateName
);

$this->cleanBindAttributes($element);
}

protected function bindExisting(
DOMNode $parent,
iterable $data
$data
):void {
$childrenWithBindAttribute = $this->getChildrenWithBindAttribute($parent);

Expand Down Expand Up @@ -66,11 +70,19 @@ protected function bindTemplates(

$newNode = $fragment->insertTemplate($insertInto);
$this->bindExisting($newNode, $row);
$this->injectDataIntoAttributeValues(
$newNode,
$row
);
}
}
}

protected function setData(BaseElement $element, iterable $data):void {
protected function setData(BaseElement $element, $data):void {
if(is_array($data)) {
$data = $this->convertArrayToObject($data);
}

foreach($element->attributes as $attr) {
$matches = [];
if(!preg_match("/(?:data-bind:)(.+)/",
Expand Down Expand Up @@ -101,7 +113,7 @@ protected function handlePropertyData(
Attr $attr,
string $bindProperty,
BaseElement $element,
iterable $data
$data
):void {
$dataKeyMatch = $this->getKeyFromAttribute($element, $attr);
$dataValue = $dataKeyMatch->getValue($data) ?? "";
Expand All @@ -128,18 +140,50 @@ protected function handlePropertyData(
}
}

protected function injectDataIntoAttributeValues(
Element $element,
$data
):void {
foreach($element->xPath("//*[@*[contains(.,'{')]]")
as $elementWithBraceInAttributeValue) {
foreach($elementWithBraceInAttributeValue->attributes as $attr) {
if(!preg_match(
"/{([^}]+)}/",
$attr->value,
$matches)) {
continue;
}

$key = $matches[1];
if(!isset($data->{$matches[1]})) {
continue;
}

$attr->value = str_replace(
$matches[0],
$data->{$matches[1]},
$attr->value
);
}
}
}

protected function handleClassData(
Attr $attr,
BaseElement $element,
iterable $data
$data
):void {
$classList = explode(" ", $attr->value);
$this->setClassFromData($element, $data, ...$classList);
$this->setClassFromData(
$element,
$data, ...
$classList
);
}

protected function setClassFromData(
BaseElement $element,
iterable $data,
$data,
string...$classList
):void {
foreach($classList as $class) {
Expand All @@ -149,11 +193,11 @@ protected function setClassFromData(

list($keyMatch, $className) = explode(":", $class);

if(!isset($data[$keyMatch])) {
if(!isset($data->{$keyMatch})) {
continue;
}

if($data[$keyMatch]) {
if($data->{$keyMatch}) {
$element->classList->add($className);
}
else {
Expand Down Expand Up @@ -222,4 +266,13 @@ protected function cleanBindAttributes(DOMNode $element):void {
}
}
}

protected function convertArrayToObject(array $array) {
$object = new StdClass();
foreach($array as $key => $value) {
$object->$key = $value;
}

return $object;
}
}
16 changes: 12 additions & 4 deletions src/DataKeyMatch.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?php
namespace Gt\DomTemplate;

use DateTimeInterface;

class DataKeyMatch {
protected $key;
protected $required;
Expand All @@ -10,18 +12,24 @@ public function __construct(string $key, bool $required) {
$this->required = $required;
}

public function checkDataExists(iterable $data) {
public function checkDataExists($data) {
if(!$this->required) {
return;
}

if(!isset($data[$this->key])) {
if(!isset($data->{$this->key})) {
throw new BoundDataNotSetException($this->key);
}
}

public function getValue(iterable $data):?string {
public function getValue($data):?string {
$this->checkDataExists($data);
return $data[$this->key] ?? null;
$value = $data->{$this->key} ?? null;

if($value instanceof DateTimeInterface) {
$value = $value->format("Y-m-d H:i:s");
}

return $value;
}
}
58 changes: 58 additions & 0 deletions test/unit/BindableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use Gt\DomTemplate\BoundDataNotSetException;
use Gt\DomTemplate\HTMLDocument;
use Gt\DomTemplate\Test\Helper\Helper;
use stdClass;

class BindableTest extends TestCase {
public function testBindMethodAvailable() {
Expand Down Expand Up @@ -317,4 +318,61 @@ public function testBindClassColonMultiple() {
);
}
}

public function testBindWithObjectData() {
$document = new HTMLDocument(Helper::HTML_TODO_LIST_BIND_CLASS_COLON_MULTIPLE);
$todoData = [
[
"id" => 1,
"title" => "Write tests",
"dateTimeCompleted" => "2018-07-01 19:46:00",
"dateTimeDeleted" => null,
],
[
"id" => 2,
"title" => "Implement features",
"dateTimeCompleted" => null,
"dateTimeDeleted" => "2018-07-01 19:54:00",
],
[
"id" => 3,
"title" => "Pass tests",
"dateTimeCompleted" => "2018-07-01 19:49:00",
"dateTimeDeleted" => null,
],
];

$todoObjData = [];

foreach($todoData as $todo) {
$obj = new StdClass();
foreach($todo as $key => $value) {
$obj->$key = $value;
}

$todoObjData []= $obj;
}

$document->extractTemplates();
$todoListElement = $document->getElementById("todo-list");

$todoListElement->bind($todoObjData);
$items = $todoListElement->querySelectorAll("li");

foreach($todoObjData as $i => $todo) {
self::assertTrue($items[$i]->classList->contains("existing-class"));

$completed = (bool)$todo->dateTimeCompleted;
self::assertEquals(
$completed,
$items[$i]->classList->contains("complete")
);

$deleted = (bool)$todo->dateTimeDeleted;
self::assertEquals(
$deleted,
$items[$i]->classList->contains("deleted")
);
}
}
}

0 comments on commit 34981ef

Please sign in to comment.