Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

extend or implement simplexml to avoid DRY

Tags:

php

xml

php-7

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

like image 757
seoppc Avatar asked Oct 28 '22 23:10

seoppc


1 Answers

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();
  }
like image 118
Decent Dabbler Avatar answered Nov 13 '22 21:11

Decent Dabbler