Reputation: 3386
I have an array of HTML tags:
class sampleHTML {
public function __construct( $tag = [] ) {
$html = [
[
'type' => 'p',
'class' => '.sample-class',
'content' => 'Sample content'
],
[
'type' => 'div',
'class' => '.sample-class-2',
'content' => 'Sample content 2'
],
];
foreach( $html as $tag ) {
new Tag( $tag );
}
}
}
I want to generate HTML from the array. So far I have following class to generate the HTML.
class Tag {
public $tag;
public function __construct( $tag = [] ) {
$this->tag = $tag;
switch ($tag['type']):
case 'p':
$this->render_p();
break;
case 'div':
$this->render_div();
break;
endswitch;
}
private function render_p() {
echo '<p class"' . $this->tag['class'] . '">' . $this->tag['content'] . '</p>';
}
private function render_div() {
echo '<div class"' . $this->tag['class'] . '">' . $this->tag['content'] . '</div>';
}
}
It's OK but I want to make my codes better by the use of sub classes and removal of switch. Similar to following:
class Tag {
public $tag;
public function __construct( $tag = [] ) {
$this->tag = $tag;
$this->render()
}
public function render() {};
}
class P extends Tag {
public function render() {
echo '<p class"' . $this->tag['class'] . '">' . $this->tag['content'] . '</p>';
}
}
class Div extends Tag {
public function render() {
echo '<div class"' . $this->tag['class'] . '">' . $this->tag['content'] . '</div>';
}
}
Questions:
render
method of child class dynamically based on tag type.Upvotes: 1
Views: 123
Reputation: 5766
Is it possible to use sub classes without switch?
Yes! You can even do it without switch AND without subclasses.
You already have the tag type as string, so you can make use of it:
class Tag {
public $tag;
public function __construct( $tag = [] ) {
$this->tag = $tag;
$this->render();
}
private function render() {
echo '<'.$this->tag['type'].' class"' . $this->tag['class'] . '">' . $this->tag['content'] . '</'.$this->tag['type'].'>';
}
}
This way, there is no need for subclasses like Div extends Tag
.
how to call the render method of child class dynamically based on tag type?
Although with my solution you wouldn't need subclasses, I'm gonna answer that question too.
If you want to call a specific render function from a subclass, define/override the function in that subclass itself
class Tag {
// constructor
// render
function render(){
// standard tag html
echo '<'.$this->tag['type'].' class"' . $this->tag['class'] . '">' . $this->tag['content'] . '</'.$this->tag['type'].'>';
}
}
class Table extends Tag {
// render (this overrides the render function of Tag)
function render(){
echo '<table class"' . $this->tag['class'] . '"><th><td>Column 1</td><td>Column 2</td><td>Column 3</td></th><tbody></tbody></table>';
}
}
class Div extends Tag {
// no render function here
// this way Div uses the render function of Tag
}
Edit:
If you need subclasses (because some tags are built in a completely different way) but you still have basic tags like p
od div
you could define overriding render()
-functions for only those who have a different html. If it's a tag with standard html you can leave out the render()
function in that subclass so it will use the render()
function of the Tag class. I edited the code above to represent that idea.
Upvotes: 1
Reputation: 10094
First of all, I'd say it is an overhead to create classes for each tag type (until it's just a program for studying).
Basically, you may use some kind of pattern, like Factory or Strategy.
So I'd go with something like this:
interface TagInterface
{
public function render();
}
class TagFactory
{
public static function createTag($tag)
{
$class = ucfirst($tag['type']) . 'Tag';
return new $class($tag);
}
}
abstract class Tag implements TagInterface
{
protected $tag;
public function __construct(array $tag)
{
$this->tag = $tag;
}
}
class PTag extends Tag
{
public function render()
{
echo '<p class"' . $this->tag['class'] . '">' . $this->tag['content'] . '</p>';
}
}
class DivTag extends Tag
{
public function render()
{
echo '<div class"' . $this->tag['class'] . '">' . $this->tag['content'] . '</div>';
}
}
And then in your code:
foreach($html as $tag ) {
/** @var TagInterface $tag */
$tag = TagFactory::createTag($tag);
$tag->render();
}
This is a correct architecture for tasks like yours. But, I repeat, it's only okay for studying. In real life things would be much simpler, so @Syscall's answer is actually what it should look like.
Upvotes: 1
Reputation: 19764
You could made dynamic instances using new $var()
, and remove your switch.
foreach( $html as $tag ) {
$class = ucfirst($tag['type']);
$obj = new $class($tag); // will calls new P() or new Div()
//$obj->render();
}
Full code :
class sampleHTML {
public function __construct() {
$html = [
[
'type' => 'p',
'class' => '.sample-class',
'content' => 'Sample content'
],
[
'type' => 'div',
'class' => '.sample-class-2',
'content' => 'Sample content 2'
],
];
foreach( $html as $tag ) {
$class = ucfirst($tag['type']);
new $class( $tag );
}
}
}
class Tag {
public $tag;
public function __construct( $tag = [] ) {
$this->tag = $tag;
$this->render();
}
public function render() {}
}
class P extends Tag {
public function render() {
echo '<p class="' . $this->tag['class'] . '">' . $this->tag['content'] . '</p>';
}
}
class Div extends Tag {
public function render() {
echo '<div class="' . $this->tag['class'] . '">' . $this->tag['content'] . '</div>';
}
}
new sampleHTML();
Outputs :
<p class=".sample-class">Sample content</p><div class=".sample-class-2">Sample content 2</div>
Upvotes: 1