1[![PHPUnit](https://github.com/salesforce/handlebars-php/actions/workflows/ci.yml/badge.svg)](https://github.com/salesforce/handlebars-php/actions/workflows/ci.yml) 2 3--- 4 5#handlebars-php 6 7--- 8 9#### A simple, logic-less, yet powerful templating engine for PHP 10 11--- 12 13Name: **handlebars-php** 14 15License: MIT 16 17Requirements: PHP >= 5.4 18 19--- 20 21 22## About Handlebars 23 24Handlebars provides the power necessary to let you build semantic templates effectively with no frustration, 25that keep the view and the code separated like we all know they should be. 26 27 28Fork of: [Handlebars.php by XaminProject](https://github.com/mardix/Handlebars) 29 30Handlebars, is the PHP port of [Handlebars.js](http://handlebarsjs.com/) 31 32--- 33 34## Install Handlebars 35 36 37You can just download Handlebars.php as is, or with Composer. 38 39To install with composer, add the following in the require key in your **composer.json** file 40 41`"salesforce/handlebars-php": "1.*"` 42 43composer.json 44 45```json 46{ 47 "name": "myapp/name", 48 "description": "My awesome app name", 49 "require": { 50 "salesforce/handlebars-php": "1.*" 51 } 52} 53``` 54 55----- 56 57## Getting Started 58 59At the minimum, we are required to have an array model and a template string. Alternatively we can have a file containing handlebars (or html, text, etc) expression. 60 61 62 63#### Template 64 65Handlebars templates look like regular HTML, with embedded handlebars expressions. 66 67Handlebars HTML-escapes values returned by a {{expression}}. 68 69```html 70<div class="entry"> 71 <h1>{{title}}</h1> 72 <div class="body"> 73 Hello, my name is {{name}} 74 </div> 75</div> 76``` 77 78The string above can be used as is in your PHP file, or be put in a file (ie: */templates/main.tpl*), to be called upon rendering. 79 80#### PHP file 81 82Now the we've created our template file, in a php file (index.php) we'll create the data to passed to the model. The model is a key/value array. 83 84Below we are going to create the Handlebars object, set the partials loader, and put some data in the model. 85 86**/index.php** 87 88```php 89<?php 90 91# With composer we can autoload the Handlebars package 92require_once ("./vendor/autoload.php"); 93 94# If not using composer, you can still load it manually. 95# require 'src/Handlebars/Autoloader.php'; 96# Handlebars\Autoloader::register(); 97 98use Handlebars\Handlebars; 99use Handlebars\Loader\FilesystemLoader; 100 101# Set the partials files 102$partialsDir = __DIR__."/templates"; 103$partialsLoader = new FilesystemLoader($partialsDir, 104 [ 105 "extension" => "html" 106 ] 107); 108 109# We'll use $handlebars throughout this the examples, assuming the will be all set this way 110$handlebars = new Handlebars([ 111 "loader" => $partialsLoader, 112 "partials_loader" => $partialsLoader 113]); 114 115# Will render the model to the templates/main.tpl template 116$model = [...]; 117echo $handlebars->render("main", $model); 118``` 119 120#### Assign Data 121 122The simplest way to assign data is to create an Array model. The model will contain all the data that will be passed to the template. 123```php 124<?php 125 126$model = [ 127 "name" => "Yolo Baggins", 128 "title" => "I'm Title", 129 "permalink" => "blog/", 130 "foo" => "bar", 131 "article" => [ 132 "title" => "My Article Title" 133 ], 134 "posts" => [ 135 [ 136 "title" => "Post #1", 137 "id" => 1, 138 "content" => "Content" 139 ], 140 [ 141 "title" => "Post 2", 142 "id" => 2, 143 "content" => "Content" 144 ] 145 ] 146]; 147``` 148 149#### Render Template 150 151Use the method `Handlebars\Handlebars::render($template, $model)` to render you template once everything is created. 152 153***$template*** : Template can be the name of the file or a string containing the handlebars/html. 154 155***$model*** : Is the array that we will pass into the template 156 157The code below will render the model to the *templates/main.tpl* template 158 159```php 160echo $handlebars->render("main", $model); 161``` 162 163 164Alternatively you can use $handlebars itself without invoking the render method 165 166```php 167echo $handlebars("main", $model); 168``` 169 170--- 171 172## Expressions 173 174Let's use this simple model for the following examples, assuming everything is already set like above. 175 176```php 177<?php 178 179$model = [ 180 "title" => "I'm Title", 181 "permalink" => "/blog/", 182 "foo" => "bar", 183 "article" => [ 184 "title" => "My Article Title" 185 ], 186 "posts" => [ 187 [ 188 "title" => "Post #1", 189 "id" => 1, 190 "content" => "Content" 191 ], 192 [ 193 "title" => "Post 2", 194 "id" => 2, 195 "content" => "Content" 196 ] 197 ] 198]; 199``` 200 201Let's work with the template. 202 203Handlebars expressions are the basic unit of a Handlebars template. You can use them alone in a {{mustache}}, pass them to a Handlebars helper, or use them as values in hash arguments. 204 205 206The simplest Handlebars expression is a simple identifier: 207 208```html 209{{title}} 210 211-> I'm Title 212``` 213 214Handlebars nested expressions which are dot-separated paths. 215 216```html 217{{article.title}} 218 219-> My Article Title 220``` 221 222Handlebars nested expressions in an array. 223 224```html 225{{posts.0.title}} 226 227-> Post #1 228``` 229 230Handlebars also allows for name conflict resolution between helpers and data fields via a this reference: 231 232```html 233{{./name}} or {{this/name}} or {{this.name}} 234``` 235 236Handlebars expressions with a helper. In this case we're using the upper helper 237 238```html 239{{#upper title}} 240 241-> I'M TITLE 242``` 243 244Nested handlebars paths can also include ../ segments, which evaluate their paths against a parent context. 245 246```html 247{{#each posts}} 248 <a href="/posts/{{../permalink}}/{{id}}">{{title}}</a> 249 {{content}} 250{{/each}} 251``` 252 253Handlebars HTML-escapes values returned by a {{expression}}. If you don't want Handlebars to escape a value, use the "triple-stash", {{{ }}} 254 255```html 256{{{foo}}} 257``` 258 259--- 260 261 262## Control Structures 263 264`if/else` and `unless` control structures are implemented as regular Handlebars helpers 265 266### IF/ELSE 267 268You can use the if helper to conditionally render a block. If its argument returns false, null, "" or [] (a "falsy" value), Handlebars will not render the block. 269 270**Example** 271 272```html 273{{#if isActive}} 274 This part will be shown if it is active 275{{#else if isValid}} 276 This part will be shown if it is valid 277{{else}} 278 This part will be shown if isActive and isValid are both "falsy" values 279{{/if}} 280``` 281 282```php 283<?php 284 285$model = [ 286 "isActive" => true, 287 "isValid" => false 288]; 289 290echo $handlebars->render($template, $model); 291``` 292 293### UNLESS 294 295You can use the unless helper as the inverse of the if helper. Its block will be rendered if the expression returns a falsy value. 296 297```html 298{{#unless isActive}} 299 This part will not show if isActive is true 300{{/unless}} 301``` 302 303--- 304##Iterators: EACH 305 306You can iterate over a list using the built-in each helper. Inside the block, you can use {{this}} or {{.}} to reference the element being iterated over. 307 308**Example** 309 310```html 311<h2>All genres:</h2> 312{{#each genres}} 313 {{.}} 314{{/each}} 315 316 317{{#each cars}} 318 <h3>{{category}}</h3> 319 Total: {{count}} 320 <ul> 321 {{#each list}} 322 {{.}} 323 {{/each}} 324 </ul> 325{{/each}} 326``` 327 328```php 329<?php 330 331$model = [ 332 "genres" => [ 333 "Hip-Hop", 334 "Rap", 335 "Techno", 336 "Country" 337 ], 338 "cars" => [ 339 "category" => "Foreign", 340 "count" => 4, 341 "list" => [ 342 "Toyota", 343 "Kia", 344 "Honda", 345 "Mazda" 346 ], 347 "category" => "WTF", 348 "count" => 1, 349 "list" => [ 350 "Fiat" 351 ], 352 "category" => "Luxury", 353 "count" => 2, 354 "list" => [ 355 "Mercedes Benz", 356 "BMW" 357 ] 358 ], 359]; 360 361 echo $engine->render($template, $model); 362``` 363 364### EACH/ELSE 365 366You can optionally provide an {{else}} section which will display only when the list is empty. 367 368```html 369<h2>All genres:</h2> 370{{#each genres}} 371 {{.}} 372{{else}} 373 No genres found! 374{{/each}} 375``` 376 377### Slice EACH Array[start:end] 378 379The #each helper (php only) also has the ability to slice the data 380 381 * {{#each Array[start:end]}} = starts at start through end -1 382 * {{#each Array[start:]}} = Starts at start though the rest of the array 383 * {{#each Array[:end]}} = Starts at the beginning through end -1 384 * {{#each Array[:]}} = A copy of the whole array 385 * {{#each Array[-1]}} 386 * {{#each Array[-2:]}} = Last two items 387 * {{#each Array[:-2]}} = Everything except last two items 388 389```html 390<h2>All genres:</h2> 391{{#each genres[0:10]}} 392 {{.}} 393{{else}} 394 No genres found! 395{{/each}} 396``` 397 398#### {{@INDEX}} and {{@KEY}} 399 400When looping through items in each, you can optionally reference the current loop index via {{@index}} 401 402```html 403{{#each array}} 404 {{@index}}: {{this}} 405{{/each}} 406 407 408{{#each object}} 409 {{@key}}: {{this}} 410{{/each}} 411``` 412 413--- 414 415## Change Context: WITH 416 417You can shift the context for a section of a template by using the built-in with block helper. 418 419```php 420<?php 421 422$model = [ 423 "genres" => [ 424 "Hip-Hop", 425 "Rap", 426 "Techno", 427 "Country" 428 ], 429 "other_genres" => [ 430 "genres" => [ 431 "Hip-Hop", 432 "Rap", 433 "Techno", 434 "Country" 435 ] 436] 437]; 438``` 439 440```html 441<h2>All genres:</h2> 442{{#with other_genres}} 443{{#each genres}} 444 {{.}} 445{{/each}} 446{{/with}} 447``` 448 449--- 450 451## Handlebars Built-in Helpers 452 453### If 454```html 455{{#if isActive}} 456 This part will be shown if it is active 457{{#else if isValid}} 458 This part will be shown if it is valid 459{{else}} 460 This part will be shown if isActive and isValid are both "falsy" values 461{{/if}} 462``` 463 464### Unless 465```html 466{{#unless isActive}} 467 This part will show when isActive is false 468{{else}} 469 Otherwise this one will show 470{{/unless}} 471``` 472 473### Each 474```html 475{{#each genres[0:10]}} 476 {{.}} 477{{else}} 478 No genres found! 479{{/each}} 480``` 481 482### With 483```html 484{{#with other_genres}} 485{{#each genres}} 486 {{.}} 487{{/each}} 488{{/with}} 489``` 490 491--- 492 493## Other Helpers 494 495#### For convenience, Voodoo\Handlebars added some extra helpers. 496 497--- 498 499### Upper 500 501To format string to uppercase 502```html 503{{#upper title}} 504``` 505 506### Lower 507 508To format string to lowercase 509```html 510{{#lower title}} 511``` 512 513 514### Capitalize 515 516To capitalize the first letter 517```html 518{{#capitalize title}} 519``` 520 521### Capitalize_Words 522 523To capitalize each words in a string 524```html 525{{#capitalize_words title}} 526``` 527 528### Reverse 529 530To reverse the order of string 531```html 532{{#reverse title}} 533``` 534 535### Format_Date 536 537To format date: `{{#format_date date '$format'}}` 538```html 539{{#format_date date 'Y-m-d H:i:s'}} 540``` 541 542### Inflect 543 544To singularize or plurialize words based on count `{{#inflect count $singular $plurial}}` 545```html 546{{#inflect count '%d book' '%d books'}} 547``` 548 549### Truncate 550 551To truncate a string: `{{#truncate title $length $ellipsis}}` 552```html 553{{#truncate title 21 '...'}} 554``` 555 556### Default 557 558To use a default value if the string is empty: `{{#default title $defaultValue}}` 559```html 560{{#default title 'No title'}} 561``` 562 563### Raw 564 565This helper return handlebars expression as is. The expression will not be parsed 566```html 567{{#raw}} 568 {{#each cars}} 569 {{model}} 570 {{/each}} 571{{/raw}} 572 573-> 574 575{{#each cars}} 576 {{model}} 577{{/each}} 578``` 579 580 581### Repeat 582 583To truncate a string: `{{#repeat $count}}{{/repeat}}` 584```html 585{{#repeat 5}} 586 Hello World! 587{{/repeat}} 588``` 589 590Variable and blocks can still be used 591```html 592{{#repeat 5}} 593 Hello {{name}}! 594{{/repeat}} 595``` 596 597 598### Define/Invoke 599 600Allow to define a block of content and use it later. It helps follow the DRY (Don't repeat yourself) principle. 601 602 603Define 604```html 605{{#define $definedName}} 606 content 607{{/define}} 608``` 609 610Invoke 611```html 612{{#invoke $definedName}} 613``` 614 615 616Example: 617```html 618{{#define hello}} 619 Hello World! How do you do? 620{{/define}} 621 622{{#invoke hello}} 623 624-> 625 626Hello World! How do you do? 627``` 628 629--- 630 631### Template Comments 632You can use comments in your handlebars code just as you would in your code. Since there is generally some level of logic, this is a good practice. 633 634```html 635{{!-- only output this author names if an author exists --}} 636``` 637 638--- 639 640### Partials 641 642Partials are other templates you can include inside of the main template. 643 644To do so: 645 646```html 647{{> my_partial}} 648``` 649 650which is a file under /templates/my_partial.html 651 652--- 653 654## Writing your own helpers 655 656Block helpers make it possible to define custom iterators and other helpers that can invoke the passed block with a new context. 657 658To create your own helper, use the method: `Handlebars::addHelper($name, $callback)` 659 660The following helper will UPPERCASE a string 661 662```php 663$handlebars->addHelper("upper", 664 function($template, $context, $args, $source){ 665 return strtoupper($context->get($args)); 666 } 667); 668``` 669 670And now we can use the helper like this: 671 672```html 673{{#upper title}} 674``` 675 676--- 677 678## Data Variables for #each 679 680In Handlebars JS v1.1, data variables `@first` and `@last` were added for the #each helper. Due to the these variables 681not being backwards compatible, these data variables are disabled by default and must be enabled manually. 682 683To enable the new data variables, set the `enableDataVariables` option to `true` when instantiating the Handlebars 684instance. 685 686```php 687$handlebars = new Handlebars([ 688 "loader" => $partialsLoader, 689 "partials_loader" => $partialsLoader, 690 "enableDataVariables" => true 691]); 692``` 693 694Given the following template and data: 695``` 696{{#each data}}{{#if @first}}FIRST: {{/if}}{{this}}<br>{{/each}} 697``` 698```php 699'data' => ['apple', 'banana', 'carrot', 'zucchini'] 700``` 701The output will be 702```html 703FIRST: apple<br>banana<br>carrot<br>zucchini<br> 704``` 705 706Given the following template and the data above: 707``` 708{{#each data}}{{@first}}: {{this}}<br>{{/each}} 709``` 710The output will be 711```html 712true: apple<br>banana<br>carrot<br>zucchini<br> 713``` 714 715Data variables also support relative referencing within multiple #each statements. 716Given 717``` 718{{#each data}}{{#each this}}outer: {{@../first}},inner: {{@first}};{{/each}}{{/each}} 719``` 720```php 721'data' => [['apple', 'banana'], ['carrot', 'zucchini']] 722``` 723The output will be 724``` 725outer: true,inner: true;outer: true,inner: false;outer: false,inner: true;outer: false,inner: false; 726``` 727 728Be aware that when data variables are enabled, variables starting with `@` are considered restricted and will override 729values specified in the data. 730 731For example, given the following template and the following data, the output will be different depending on if data 732variables are enabled. 733 734``` 735{{#each objects}}{{@first}}, {{@last}}, {{@index}}, {{@unknown}}{{/each}} 736``` 737 738```php 739$object = new stdClass; 740$object->{'@first'} = 'apple'; 741$object->{'@last'} = 'banana'; 742$object->{'@index'} = 'carrot'; 743$object->{'@unknown'} = 'zucchini'; 744$data = ['objects' => [$object]]; 745 746$engine = new \Handlebars\Handlebars(array( 747 'loader' => new \Handlebars\Loader\StringLoader(), 748 'helpers' => new \Handlebars\Helpers(), 749 'enableDataVariables'=> $enabled, 750)); 751$engine->render($template, $data) 752``` 753 754When `enableDataVariables` is `false`, existing behavior is not changed where some variables will be return. 755 756``` 757apple, banana, 0, zucchini 758``` 759 760 761When `enableDataVariables` is `true`, the behavior matches HandlebarsJS 1.1 behavior, where all data variables replace 762variables defined in the data and any data variable prefixed with `@` that is unknown will be blank. 763 764``` 765true, true, 0, 766``` 767 768 769#### Credits 770 771* Fork of [Handlebars.php by XaminProject](https://github.com/XaminProject/handlebars.php) 772* The documentation was edited by [Mardix](http://github.com/mardix). 773 774#### Contribution 775 776Contributions are more than welcome!