Home > Net >  How to chain same method more than once in PHP
How to chain same method more than once in PHP

Time:08-14

The questions regarding chaining were asked here many times, but I couldn't find the answer for mine one. I'd like to chain the same method more than once. I'm trying to make form builder and generate HTML inputs with method "addInput", This method return $this, but if it's called again it just overwrites previous object's state. Take a look:

My class and method to create inputs:

class FormInputBuilder implements FormInputInterface
{

    public $label;

    public function addInput(
                            string $inputType, 
                            string $placeholder, 
                            string $inputName,
                            string $label = '',
                            string $inputCssClasses = '',
                            string $labelCssClasses = '')
    {
        if ($label != '') {
            $inputLabel = sprintf("<label for='%s'>%s</label>", $inputName, $label);
        }
       $this->label = $inputLabel . sprintf("<input type='%s' name='%s' placeholder='%s' class='%s'>", 
                        $inputType, 
                        $inputName,
                        $placeholder,
                        $inputCssClasses,
                        );

       return $this;
    }
}

This method works fine, but when I call it I get just the last state of the object (I was hoping to get two in my array "elements"):

 $formBuilder = new FormInputBuilder();
        $elements = [];
        $elements[] = $formBuilder
                ->addInput(
                    inputType: 'text',
                    placeholder: '', 
                    inputName: 'address', 
                    label: 'address', 
                    inputCssClasses: 'd-block')
                ->addInput(
                    inputType: 'text',
                    placeholder: 'test2', 
                    inputName: 'age', 
                    label: 'age', 
                    inputCssClasses: 'd-block')
                ;
        dump($elements);

$elements holds only one entry which is the last state of $formBuilder (I have only one input in my array with inputName "age", but no inputName "address"). How to keep (store) all states of the object or just all results of the addInput() method (which is just string) with chaining?

CodePudding user response:

In a fluent interface, you need to keep the state of the object that you're building. Here you are building a collection of inputs, so you need an array to keep them in, then a way to access them when you're done building.

<?php

interface FormInputInterface
{
    public function addInput(
        string $inputType,
        string $placeholder,
        string $inputName,
        string $label = '',
        string $inputCssClasses = '',
        string $labelCssClasses = '');
    
    // Add getInputs to our interface
    public function getInputs(): array;
}

class FormInputBuilder implements FormInputInterface
{
    // Make input array private, no need to allow full access
    private $inputs = [];
    
    public function addInput(
        string $inputType,
        string $placeholder,
        string $inputName,
        string $label = '',
        string $inputCssClasses = '',
        string $labelCssClasses = '')
    {
        if ($label != '')
        {
            $inputLabel = sprintf("<label for='%s'>%s</label>", $inputName, $label);
        }
        
        // Add this input to the array
        $this->inputs[] = $inputLabel . sprintf("<input type='%s' name='%s' placeholder='%s' class='%s'>",
                                                $inputType,
                                                $inputName,
                                                $placeholder,
                                                $inputCssClasses
            );
        
        return $this;
    }
    
    // Return the inputs
    public function getInputs(): array
    {
        return $this->inputs;
    }
}

$formBuilder = new FormInputBuilder();
$elements    = [];
$elements[]  = $formBuilder
    ->addInput(
        inputType:       'text',
        placeholder:     '',
        inputName:       'address',
        label:           'address',
        inputCssClasses: 'd-block')
    ->addInput(
        inputType:       'text',
        placeholder:     'test2',
        inputName:       'age',
        label:           'age',
        inputCssClasses: 'd-block')
    ->getInputs(); // Need to get the inputs after we're finished adding

var_dump($elements);

CodePudding user response:

A builder pattern works slightly different. Consider this (simplified example):

<?php
class FormInputBuilder {

    public $elements = [];

    public function addInput(
                            string $inputType, 
                            string $placeholder, 
                            string $inputName,
                            string $label = '',
                            string $inputCssClasses = '',
                            string $labelCssClasses = '') {
       $this->elements[] = sprintf(
           "<input type='%s' name='%s' placeholder='%s' class='%s'>", 
           $inputType, 
           $inputName,
           $placeholder,
           $inputCssClasses,
       );

       return $this;
    }
    
    public function build() {
        return $this->elements;
    }
}

$formBuilder = new FormInputBuilder();
$elements = $formBuilder
    ->addInput(
        inputType: 'text',
        placeholder: '', 
        inputName: 'address', 
        label: 'address', 
        inputCssClasses: 'd-block')
    ->addInput(
        inputType: 'text',
        placeholder: 'test2', 
        inputName: 'age', 
        label: 'age', 
        inputCssClasses: 'd-block')
    ->build();
var_dump($elements);

The output obviously is:

array(2) {
  [0]=>
  string(65) "<input type='text' name='address' placeholder='' class='d-block'>"
  [1]=>
  string(66) "<input type='text' name='age' placeholder='test2' class='d-block'>"
}

The main differences:

  1. You need to store each element you create with that method inside the builder. It needs to store everything , otherwise prior stuff is lost.

  2. You need to use an additional terminating method (typicalled "build()") which returns the actual payload you created. Since that is what you are interested in, not the builder object itself.


Here is an online version of the code you can use to play around a bit ...

  • Related