sy/component

将应用程序构建为更简单组件的树

2.5.3 2024-05-19 23:14 UTC

This package is auto-updated.

Last update: 2024-09-20 00:00:53 UTC


README

组件的概念允许您将应用程序构建为更简单组件的树(组合设计模式)。每个组件都可以重用。

基本上,我们可以假设组件是一个可字符串化的对象。在这个阶段,它只是使用模板生成字符串,可以是任何格式(html、xml、json、纯文本等...)。在此基础上,我们可以通过添加css和js属性来构建Web组件。

Sy\Component类是其他Web组件(sy/webcomponent)和HTML页面及元素(sy/html)如表单和表格等的基类...

组件模板引擎

使用的模板引擎是sy/template

模板语法概念:插槽

使用setVar方法填充插槽的示例,您的PHP脚本

<?php

$c = new Sy\Component();
$c->setTemplateFile(__DIR__ . '/template.tpl');
$c->setVar('NAME', 'World');

echo $c;

模板文件,template.tpl

Hello {NAME}

输出结果

Hello World

使用setBlock方法的示例,您的PHP脚本

<?php

$c = new Sy\Component();
$c->setTemplateFile(__DIR__ . '/template.tpl');

for ($i = 0; $i < 10; $i++) {
	$c->setVar('VALUE', $i);
	$c->setBlock('MY_BLOCK');
}

echo $c;

模板文件,template.tpl

<!-- BEGIN MY_BLOCK -->
Block {VALUE}
<!-- END MY_BLOCK -->

输出结果

Block 0
Block 1
Block 2
Block 3
Block 4
Block 5
Block 6
Block 7
Block 8
Block 9

ELSE块

当块未设置时,您可以使用ELSE块来显示默认内容。

<!-- BEGIN MY_BLOCK -->
Block content
<!-- ELSE MY_BLOCK -->
Block not parsed
<!-- END MY_BLOCK -->

使用独立变量设置块

默认情况下,当setBlock方法的第二个参数为空时,它将使用全局设置的变量在块作用域内设置插槽。使用第二个参数,可以使用独立变量在块作用域内设置插槽。

<?php

$c = new Sy\Component();
$c->setTemplateContent('<!-- BEGIN A -->{SLOT}<!-- END A -->');

$c->setVar('SLOT', 'Hello');

foreach (['Foo', 'Bar', 'Baz'] as $v) {
	$c->setBlock('A', ['SLOT' => $v]);
}

$c->setBlock('A');

echo $c;

结果

FooBarBazHello

使用数据数组设置块

class A extends Sy\Component {

	public function __construct() {
		parent::__construct();

		$this->setTemplateFile(__DIR__ . '/template.html');

		// setBlocks will set a block for each line in the data array
		$this->setBlocks('foo', [
			['firstname' => 'John', 'lastname' => 'Doe', 'age' => 32],
			['firstname' => 'John', 'lastname' => 'Wick', 'age' => 42],
			['firstname' => 'Jane', 'lastname' => 'Doe', 'age' => 25],
			['firstname' => 'Bob', 'lastname' => 'Doe'],
		]);
	}

}

echo new A();

模板文件,template.html

Nb persons: {FOO_COUNT}
<!-- BEGIN FOO_BLOCK -->
<div>
	Index: {FOO_INDEX}
	Firstname: {FOO_FIRSTNAME}
	Lastname: {FOO_LASTNAME}
	<!-- BEGIN FOO_AGE_BLOCK -->
	Age: {FOO_AGE}
	<!-- ELSE FOO_AGE_BLOCK -->
	Unknown age
	<!-- END FOO_AGE_BLOCK -->
</div>
<!-- END FOO_BLOCK -->

输出

Nb persons: 3

<div>
	Index: 1
	Firstname: John
	Lastname: Doe
	Age: 32
</div>
<div>
	Index: 2
	Firstname: John
	Lastname: Wick
	Age: 42
</div>
<div>
	Index: 3
	Firstname: Jane
	Lastname: Doe
	Age: 25
</div>
<div>
	Index: 4
	Firstname: Bob
	Lastname: Doe
	Unknown age
</div>

替代模板语法

可以使用简单的PHP模板语法。必须指定您正在使用PHP模板文件

<?php

$c = new Sy\Component();

// use a php template file with the second parameter
$c->setTemplateFile(__DIR__ . '/template.tpl', 'php');
$c->setVar('NAME', 'World');

echo $c;

PHP模板文件,template.tpl

Hello <?php echo $NAME ?>

输出结果

Hello World

创建组件

从Sy\Component类派生一个自定义类。

例如在Hello.php

<?php
use Sy\Component;

class Hello extends Component {

	public function  __construct($name = 'world') {
		$this->setTemplateFile(__DIR__ . '/Hello.tpl');
		$this->setVar('NAME', $name);
	}

}

Hello.tpl

Hello {NAME}!

使用您的组件

<?php

// echo 'Hello world!'
$hello = new Hello();
echo $hello;

// echo 'Hello toto!'
$hello = new Hello('toto');
echo $hello;

在另一个组件中添加组件

使用setVar方法在另一个组件中添加组件。

<?php

$c = new Sy\Component();
$c->setTemplateFile(__DIR__ . '/template.tpl');
$c->setVar('NAME', new Hello());

组件动作

actionDispatch方法可以帮助您调用动作方法。

此方法接受两个参数

  • actionName: $_REQUEST变量名,index.php?action=foo
  • defaultMethod: 这一个是可选的,如果没有调用动作,它将执行此方法

动作方法名称必须以'Action'结尾:fooAction

例如在MyComponent.php

<?php
use Sy\Component;

class MyComponent extends Component {

	public function  __construct() {
		parent::__construct();
		$this->setTemplateFile(__DIR__ . '/MyComponent.tpl');

		// if $_REQUEST['action'] is not set, call initAction
		$this->actionDispatch('action', 'init');
	}

	public function initAction() {

	}

	public function fooAction() {

	}

}

组件翻译器

可以在组件中添加翻译器。每个翻译器将从一个指定目录中的文件加载翻译数据。此翻译文件必须命名为检测到的语言。例如,如果检测到的语言是"fr",则PHP翻译器将尝试加载"fr.php"。Gettext翻译器将尝试加载"fr.mo"。

此功能由库sy/translate提供

语言检测

将使用以下顺序中的变量来检测语言

  1. $_SESSION['sy_language']
  2. $_COOKIE['sy_language']
  3. $_SERVER['HTTP_ACCEPT_LANGUAGE']

翻译方法

  • void Component::addTranslator(string $directory [, string $type = 'php', string $lang = ''])
  • string Component::_(mixed $values)

示例

<?php

use Sy\Component;

class MyComponent extends Component {

	public function __construct() {
		parent::__construct();
		$this->setTemplateFile(__DIR__ . '/tpl/mycomponent.tpl');

		// Add a translator, it will look for translation file into specified directory
		$this->addTranslator(__DIR__ . '/lang', 'php', 'fr');

		// Use translation method
		$this->setVar('SLOT1', $this->_('Hello world'));
		$this->setVar('SLOT2', $this->_('This is %s', 'an apple'));
		$this->setVar('SLOT3', $this->_('This is %s', 'an pineapple'));
		$this->setVar('SLOT4', $this->_('Number of %d max', 10));
	}

}

echo new MyComponent();

PHP翻译文件

<?php
return array(
	'Hello world' => 'Bonjour monde',
	'This is %s' => 'Ceci est %s',
	'an apple' => 'une pomme',
	'a pineapple' => 'un ananas',
	'Number of %d max' => 'Nombre de %d max',
);

模板文件

{"Hello world"}

{"No traduction"}

{SLOT1}
{SLOT2}
{SLOT3}
{SLOT4}

输出结果

Bonjour monde

No traduction

Bonjour monde
Ceci est une pomme
Ceci est an pineapple
Nombre de 10 max

添加多个翻译器

可以在组件中添加多个翻译器。添加的顺序很重要,因为翻译过程将在找到第一个翻译数据后立即停止。

将翻译器传输到内部Web组件

当在Web组件A中添加Web组件B时,A的所有翻译器都将添加到B中。

<?php

use Sy\Component;

class A extends Component {

	public function __construct() {
		$this->mount(function () {
			$this->addTranslator(__DIR__ . '/lang', 'php', 'fr');
			$this->setTemplateContent('<a>{HELLO/} {"world"} {B}</a>');
			$this->setVars([
				'HELLO' => $this->_('hello'),
				'B' => new B()
			]);
		});
	}

}

class B extends Component {

	public function __construct() {
		$this->mount(function () {
			$this->setTemplateContent('<b>{HELLO/} {"world"} {C}</b>');
			$this->setVars([
				'HELLO' => $this->_('hello'),
				'C' => new C()
			]);
		});
	}

}

class C extends Component {

	public function __construct() {
		$this->mount(function () {
			$this->addTranslator(__DIR__ . '/lang/alt', 'php', 'fr');
			$this->setTemplateContent('<c>{HELLO/} {"world"}</c>');
			$this->setVars([
				'HELLO' => $this->_('hello'),
			]);
		});
	}

}

echo new A();

在组件A中添加的翻译文件

return array(
	'hello' => 'bonjour',
	'world' => 'monde',
);

在组件C中添加的翻译文件

return array(
	'hello' => 'salut',
);

输出结果

<a>bonjour monde <b>bonjour monde <c>salut monde</c></b></a>

A的翻译器被传输到B和C。C将优先使用自己的翻译器。

在这里我们需要使用mount方法来注册在渲染阶段之前触发的mount事件的回调。这是因为我们需要确保所有叶子组件都从其父组件接收到了翻译器。