Please check following class that I've create to build few XML frames..
class CommandBuilder
{
    public function __construct()
    {
        //
    }
    public function login($username, $password)
    {
        $frame = $this->frame();
        $command = $frame->addChild('command');
        $login = $command->addChild('login');
        $login->addChild('username', $username);
        $login->addChild('password', $password);
        $command->addChild('authKey', 'authkey');
        return $frame->asXML();
    }
    public function info($id)
    {
        $frame = $this->frame();
        $command = $frame->addChild('command');
        $login = $command->addChild('product');
        $login->addChild('id', $id);
        $command->addChild('authKey', 'authkey');
        return $frame->asXML();
    }
    protected function frame()
    {
        return new SimpleXMLElement(
            '<app/>'
        );
    }
}
What's the best way to avoid duplication of $frame->addChild('command') and $command->addChild('authKey', 'authkey') without changing order of elements?  
Please help to improve the code. Thanks
How about something like this, where you create a separate builder class:
class CommandBuilder
{
  private $commandName;
  private $params = [];
  public function __construct( $commandName ) {
    $this->commandName = $commandName;
  }
  // convenience method, to allow for cleaner fluent interface usage
  public static function create( $commandName ) {
    return new self( $commandName );
  }
  public function addParam( $paramName, $paramValue ) {
    $this->params[] = [ 'name' => $paramName, 'value' => $paramValue ];
    return $this;
  }
  public function build() {
    $app = new SimpleXMLElement( '<app/>' );
    $commandContainer = $app->addChild( 'command' );
    $command = $commandContainer->addChild( $this->commandName );
    foreach( $this->params as $param ) {
      $command->addChild( $param[ 'name' ], $param[ 'value' ] );
    }
    $commandContainer->addChild( 'authKey', 'authKey' );
    return $app->asXML();
  }  
}
and then have a separate class that builds the specific application commands:
class AppCommands
{
  public function login( $username, $password ) {
    return CommandBuilder::create( 'login' )->addParam( 'username', $username )
                                            ->addParam( 'password', $password )
                                            ->build();
  }
  public function info( $id ) {
    return CommandBuilder::create( 'product' )->addParam( 'id', $id )
                                              ->build();
  }
}
Usage stays the same, except you instantiate AppCommands, in stead of CommandBuilder:
$ac = new AppCommands;
echo $ac->login( 'MyUsername', 'MyPassword' );
echo PHP_EOL;
echo $ac->info( 5 );
View this example on eval.in
If you wanted, you could of course also dynamically pass the authKey to the CommandBuilder instead of hard-coding it inside, with something like:
class CommandBuilder
{
  private $commandName;
  private $authKey;
  private $params = [];
  public function __construct( $commandName, $authKey ) {
    $this->commandName = $commandName;
    $this->authKey = $authKey;
  }
  public static function create( $commandName, $authKey ) {
    return new self( $commandName, $authKey );
  }
  /* ... */
  public function build() {
    /* ... */
    $commandContainer->addChild( 'authKey', $this->authKey );
    return $app->asXML();
  }
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With