/**
  * Persists an ioMenuItem tree to the database.
  *
  * Typically, you'll persist your entire menu tree. This will save the root
  * menu item as a root in Doctrine's nested set with the whole tree under it:
  *
  * $menu = new ioMenuItem('root');
  * $menu->addChild('Home', '@homepage');
  * Doctrine_Core::getTable('ioDoctrineMenuItem')->persist($menu);
  *
  * You can also persist part of a tree or persist a full menu under an
  * existing Doctrine nested set node:
  *
  * $menu->addChild('Links');
  * $menu['Links']->addChild('Sympal', 'http://www.sympalphp.org');
  * $tbl = Doctrine_Core::getTable('ioDoctrineMenuItem');
  * $node = $tbl->findOneByName('some name'); // find an existing node
  * // save the Links submenu under the above node
  * $tbl->persist($menu['Links'], $node);
  *
  * @param  ioMenuItem $menu
  * @param  ioDoctrineMenuItem $parentDoctrineMenu Optional parent node, else
  *                                                it will save as root
  * @return ioDoctrineMenuItem
  * @throws sfException
  */
 public function persist(ioMenuItem $menu, ioDoctrineMenuItem $parent = null)
 {
     // run a few sanity checks and create the root node
     if (!$parent) {
         // protect against people calling persist on non-root objects, which
         // would otherwise cause those items to persist as new roots
         if (!$menu->isRoot()) {
             throw new sfException('Non-root menu items as root items. Either persist the entire
       tree or pass an ioDoctrineMenuItem parent as the second argument.');
         }
         // Make sure the root has a name
         if (!$menu->getName()) {
             throw new sfException('A root object cannot be persisted without a name. Call setName()
       on the root menu item to set its name');
         }
         $root = $this->fetchRootByName($menu->getName());
         if (!$root) {
             // create a new root
             $root = new ioDoctrineMenuItem();
             $root->name = $menu->getName();
             $root->save();
             $this->getTree()->createRoot($root);
         }
         $parent = $root;
     }
     // merge in the menu data into the parent menu
     $parent->persistFromMenuArray($menu->toArray());
 }
$menu->setLabel('admin default');
$t->is($menu->getLabel(), 'admin default', '->getLabel() returns "admin default", the default label');
$menu->setLabel('administration', 'en');
$t->is($menu->getLabel(), 'administration', '->getLabel() returns "administration", the "en" culture label');
sfConfig::set('sf_default_culture', 'es');
$t->is($menu->getLabel('fake'), 'administración', '->getLabel() retrieves the sf_default_culture label if the given culture does not have a label.');
$menu->setCulture('es');
$t->is($menu->getLabel(), 'administración', '->getLabel() returns the label of the culture on the label');
$t->info('  9.3 - Test toArray() and fromArray()');
$arr = $menu->toArray();
$t->is($arr['i18n_labels'], array('es' => 'administración', 'en' => 'administration'), '->toArray() exports an i18n_labels array');
$newMenu = ioMenuItem::createFromArray($arr);
$arr = $newMenu->toArray();
$t->is($arr['i18n_labels'], array('es' => 'administración', 'en' => 'administration'), '->fromArray() correctly imports the i18n_labels');
$menu = new ioMenuItem('root');
$arr = $menu->toArray();
$t->is(isset($arr['i18n_labels']), false, 'If no i18n labels are set, the key is hidden entirely from ->toarray().');
$t->info('10 - Test item reordering.');
$menu = new ioMenuItem('root');
$menu->addChild('c1');
$menu->addChild('c2');
$menu->addChild('c3');
$menu->addChild('c4');
$menu['c3']->moveToFirstPosition();
$arr = array_keys($menu->getChildren());
$t->is($arr, array('c3', 'c1', 'c2', 'c4'), 'c3 moved to first position');
$menu['c2']->moveToLastPosition();
$arr = array_keys($menu->getChildren());
$t->is($arr, array('c3', 'c1', 'c4', 'c2'), 'c2 moved to last position');
$menu['c1']->moveToPosition(2);
$arr = array_keys($menu->getChildren());
// create the tree and make its vars accessible
print_test_tree($t);
$children = $rt->getChildrenIndexedByName();
$t->is(count($children), 2, '->getChildrenIndexedByName() returns 2 for rt');
$t->is(array_keys($children), array('Parent 1', 'Parent 2'), '->getChildrenIndexedByName() has the correct indexes');
$t->is($children['Parent 1']->name, 'Parent 1', '->getChildrenIndexedByName() returns the correct items.');
$t->is(count($pt1->getChildrenIndexedByName()), 3, '->getChildrenIndexedByName() returns 3 item for pt1.');
$t->is(count($pt2->getChildrenIndexedByName()), 1, '->getChildrenIndexedByName() returns 1 item for pt2.');
$t->info('2 - Test persistFromMenuArray() in a varierty of situations.');
$menu = new ioMenuItem('Root li');
$t->info('  2.1 - First try it without any children - should just update root values.');
$t->info('    2.1.1 - Persist a menu with mostly blank fields.');
$rt = create_root('rt');
$menu->setAttributes(array());
// clear the default "root" class attribute
$rt->persistFromMenuArray($menu->toArray(false));
$t->is($rt->getName(), 'Root li', '->getName() returns "Root li".');
$t->is($rt->getLabel(), null, '->getLabel() returns null.');
$t->is($rt->getRoute(), null, '->getRoute() returns null.');
$t->is($rt->getAttributes(), '', '->getAttributes() returns an empty string.');
$t->is($rt->getRequiresAuth(), false, '->getRequiresAuth() returns false.');
$t->is($rt->getRequiresNoAuth(), false, '->getRequiresNoAuth() returns false.');
$t->is(count($rt->Permissions), 0, '->Permissions matches 0 items');
// setup some interesting values to persist
$menu->setLabel('sympal');
$menu->setRoute('http://www.sympalphp.org');
$menu->setAttributes(array('class' => 'root', 'id' => 'sympal_menu'));
$menu->requiresAuth(true);
$menu->requiresNoAuth(false);
$menu->setCredentials(array(array('c1', 'c2')));
$t->info('    2.1.2 - Persisting a menu with multi-level credentials is not supported - an exception is thrown.');
function persist_menu(lime_test $t, ioDoctrineMenuItem $rt, ioMenuItem $menu)
{
    $timer = new sfTimer();
    $rt->persistFromMenuArray($menu->toArray());
    $timer->addTime();
    $rt->refresh(true);
    $t->info(sprintf('### Menu took %s to persist (%s nodes/min)', round($timer->getElapsedTime(), 4), floor(8 * 60 / $timer->getElapsedTime())));
}