Trees

SimpleTree

This type is created by adding parent field into the model, that will store paren’t id of the current record. For root records the value of this field is "-1".

Attention! In Model there can be only one "parent" field.

Sample of standard use of tree when creating a menu of nested pages.

<?
class Pages extends Model
{
    protected $name = "Pages menu";

    protected $model_elements = array(
        array("Activate", "bool", "active", array("on_create" => true)),
        array("Display in menu ", "bool", "in_menu", array("on_create" => true)),
        array("Name", "char", "name", array("required" => true)),
        array("Parent section", "parent", "parent", array("max_depth" => 3)),
        array("Position", "order", "order"),
        array("Content", "text", "content", array("rich_text" => true))
    );
}
?>

Linked Tree

To create a structure as "catalog -> catalog -> catalog -> product" or "album -> album -> image " you need to link the model with the “parent ” field of another model in a way so the end parent of the first model will be a foreign key for other model records.

Sample of creating a linked tree for multi-level product catalog.

class Catalogs extends Model 
{
    protected $name = "Catalog sections";

    protected $model_elements = array(
        array("Active", "bool", "active", array("on_create" => true)),
        array("Name", "char", "name", array("required" => true)),
        array("Catalog", "parent", "parent", array("parent_for" => "Products")),                
        array("Link", "url", "url"),
        array("Position", "order", "order")                    
    );
}

class Products extends Model 
{
    protected $name = "Catalog Products";

    protected $model_elements = array(
        array("Active", "bool", "active", array("on_create" => true)),
        array("Name", "char", "name", array("required" => true)),
        array("Catalog section", "enum", "parent", array("foreign_key" => "Catalogs", "is_parent" => true)),
        array("Price", "int", "price", array("required" => true)),
        array("Images", "multi_images","images"),
        array("Description", "text", "desc", array("rich_text" => true)),
        array("Position", "order", "order")
    );
}

Special Tree Methods

For a quick access to parents and descendants there are ad hoc methods designed in a tree.

  • getParents($id) - returns an array of parent records (till a record with value of a parent field as "-1"). In a result array the keys - are ids of records, values - names of records according to "$name_field" parameter from Model Settings section.
  • getChildren($id) - returns an array of all child records, recursively bypassing all branches of the tree. The resulting array is built similarly by previous method.
  • displayBreadcrumbs($id, $url_first [, $url_field]) - displays a linked path to a record with the passed id. Returns a sequence of HTML tags as "the link-> the link-> the link-> span". Input parameters: an id of the current record, $url_first - the first part of url, optional parameter of $url_field - the name of a field in which the text link for this record is stored (to create an url as "catalog/toys/"). For pre-installed "Pages" model the links are automatically assembled as "/page/4/" and "/contacts/ without the first part).
$catalog = $mv -> catalogs -> findRecordById(43);

//Parent records
$parents = $mv -> catalogs -> getParents($catalog -> id);

//Descendant records
$children = $mv -> catalogs -> getChildren($catalog -> id);

//Link path to catalog section
echo $mv -> catalogs -> displayBreadcrumbs($catalog -> id, "category", "url");

//Link path to catalog product  
echo $mv -> products -> displayBreadcrumbs($product -> id, "category");

//Link path will be like 
<a href="/category/toys/">Toys</a>
<a href="/category/balls/">Ball</a>
<span>Red ball</span>

Above mentioned methods can be called from within a model object. In the sample below we will define a catalog page based on URL.

public function defineCatalogPage(Router $router)
{
    $url_parts = $router -> getUrlParts();
    $content = false;

    if(count($url_parts) == 2)
        if(is_numeric($url_parts[1]))
            $content = $this -> findRecord(array("id" => $url_parts[1], "active" => 1));
        else
            $content = $this -> findRecord(array("url" => $url_parts[1], "active" => 1));

    if($content)
        $this -> parents = $this -> getParents($content -> id);

    return $content;
}

Then we are recursively building a catalog menu.

public function displayCatalogMenu($parent)
{
    $rows = $this -> select(array("parent" => $parent, "active" => 1, "order->asc" => "order"));
    $html = "";
    $root_path = $this -> root_path."category/";

    foreach($rows as $row)
    {
        $active = "";
        $children = false;

        if($row['id'] == $this -> id || array_key_exists($row['id'],  $this -> parents))
        {
            $children = $this -> select(array("parent" => $row['id'], "active" => 1));
            $active = ' class="active"';
        }

        $url = $root_path.($row['url'] ? $row['url'] : $row['id'])."/";
        $html .= '<li><a'.$active.' href="'.$url.'">'.$row['name'].'</a>';

        if($children)
            $html .= "<ul>".$this -> displayCatalogMenu($row['id'])."</ul>";

        $html .= "</li>";        
    }

    return $html;
}

//Call of this method in the template 
<ul id="catalog">
    <? echo $mv -> catalogs -> displayCatalogMenu(-1); ?>
</ul>