Trees

Simple tree

This data type is created by adding parent field into the model, that will store parent's id of the current record. For root records the value of this parent is "-1". So all records in model will have their parents, and we get the tree structure with parent and child elements (inside single model).

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

Sample of standard use of tree structure 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 with another model in such way so the last parent of the first model will be a foreign key for other records located in second model.

Sample of creating a linked tree for multi-level products 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 quick access to parents and descendants there are special methods designed for managing the elements of the tree structure.

  • getParents($id) - returns an array of parent records (untill a record with value of a parent field has "-1" value). In a result we get array where keys - are ids of records, values - names of records according to "$name_field" parameter from Model Settings section (usually value of "name" field).
  • getChildren($id) - returns an array of all child records, recursively by going through all branches of the tree. The resulting array is built similarly to 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 "link -> link-> 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 which keeps the text link for this record (to create an url like "catalog/toys/"). For pre-installed "Pages" model the links are automatically assembled as "/page/4/" and "/contacts/ without the first part, it's an exception.
$catalog = $mv -> catalogs -> findRecordById(43);

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

//Child 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 built like 
<a href="/category/toys/">Toys</a>
<a href="/category/balls/">Ball</a>
<span>Red ball</span>

Given methods can be also called inside a model object. In the sample below we will create function to define a catalog page based on passed 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 with opened branch where is being located current active category.

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>