简体   繁体   中英

Php create Dynamic Xml in foreach

I built an basic xml class (using simple xml), so far I built simple xml nodes.

Now I want to create a function in which generate a specific number of nodes in a foreach, which I specify in the parameter.

it looks like this now:

class Xml
{

    private $data = "";

    protected $xml = "";
    protected $xmlManager = "";
    protected $menus = "";
    protected $xmlMenus = "";

    public function __construct($data = [])
    {
        $this->data = $data;
        $this->xml  = new \SimpleXmlElement('<test></test>');
        $this->setMenus();
        return $this->xml;
    }
    private function setMenus()
    {
        $this->xmlMenus = $this->xmlManager->addChild('menus');
    }

    public function setMenuNode($parameter)
    {
        $this->data->menu = []
        foreach ($this->data->menus as $menuKey => $menuValue)
        {
            $this->xmlMenu = $this->xmlMenus->addChild('menu');
            $menueValue->addAttribute($param1, $param2, $param3);
            $menueValue->addAttribute($param1, $param2, $param3);
        }

    }

}

Later on I want to call it like this

Xml->setMenuNode($param1, $param2, $param3);

Which should create 3 menu nodes.

My xml should look like this later on.

<?xml version="1.0"?>
    <menus>
      <menu id="1" entry="title">
        ...
      </menu>
      <menu id="2" entry="title2">
        ...
      </menu>
      <menu id="3" entry="title2">
        ...
      </menu>
    </menus>
</dvd>

I am not quiet sure how to manager this in a good way.

Based on what you described you would get (PHP 5.6):

function setMenuNode(...$parameters)
{
  foreach ($parameters as $parameter) {
    $this->xmlMenu = $this->xmlMenus->addChild('menu');
    $menueValue->addAttribute('id', $parameter);
    $menueValue->addAttribute('entry', 'title' . $parameter);
  }
}

This are two jobs, so you should split it into two classes. For a loose dependency define an interface. The menu and the items need to be append itself to their parent.

I use DOM because SimpleXMLElement does not support an empty XML document. This solution would work with SimpleXML, too. But only with an additional outer XML element node.

Interface

This is a contract, so you should define an interface for it.

interface DOMAppendable {
  public function appendTo(\DOMNode $parent);
}

Item Class

For a single item implement the interface:

class MenuItem implements DOMAppendable {

  private $_id = '';
  private $_title = '';

  public function __construct($id, $title) {
    $this->_id = $id;
    $this->_title = $title;
  }

  public function appendTo(\DOMNode $parent) {
    $document = $parent->ownerDocument ?: $parent;
    $item = $parent->appendChild($document->createElement('menu'));
    $item->setAttribute('id', $this->_id);
    $item->setAttribute('title', $this->_title);
  }
}

The constructor stores the data into private properties. The method from the interface appends the nodes to the provided parent. The menu items contain more data, have subitems. As long as they implement the interface it will still work.

Menu/List Class

A class for the menu itself stores added menu items into an array property. It implements the interface, too. (So it can be appended to another menu). Implementing __toString() allows to cast the menu into a string.

class Menu implements DOMAppendable {

  private $_items = [];

  public function add(\DOMAppendable $item) {
    foreach (func_get_args() as $item) {
      if (!($item instanceOf DOMAppendable)) {
        throw new \InvalidArgumentException("Invalid menu item.");
      }
      $this->_items[] = $item;   
    }
  }

  public function appendTo(\DOMNode $parent) {
    $document = $parent->ownerDocument ?: $parent;
    $menu = $parent->appendChild($document->createElement('menus'));
    foreach ($this->_items as $item) {
      $item->appendTo($menu);
    }
  }

  public function __toString() {
    $document = new \DOMDocument();
    $document->formatOutput = TRUE;
    $this->appendTo($document);
    return $document->saveXml();
  }
}

Use

With that you can create the menu using the new classes.

$menu = new Menu();
$menu->add(
  new MenuItem(1, 'title1'),
  new MenuItem(2, 'title2'),
  new MenuItem(3, 'title3')
);

echo $menu;

Output:

<?xml version="1.0"?>
<menus>
  <menu id="1" title="title1"/>
  <menu id="2" title="title2"/>
  <menu id="3" title="title3"/>
</menus>

The interface allows to use different menu item classes, even a menu can be an item:

$menu = new Menu();
$menu->add(
  new MenuItem(1, 'title1'),
  new MenuItem(2, 'title2')
);
$subMenu = new Menu();
$subMenu->add(new MenuItem(3.1, 'title3.1'));
$menu->add($subMenu);

echo $menu;

Output:

<?xml version="1.0"?>
<menus>
  <menu id="1" title="title1"/>
  <menu id="2" title="title2"/>
  <menus>
    <menu id="3.1" title="title3.1"/>
  </menus>
</menus>

PHP >= 5.6

PHP 5.6 and later support the new variadics syntax. This allows to remove the func_get_args() call and simplify the Menu::add() method.

class Menu implements DOMAppendable {

  private $_items = [];

  public function add(\DOMAppendable ...$items) {
    foreach ($items as $item) {
      $this->_items[] = $item;   
    }
  }
  ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM