We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 13346
    • 6 Posts
    I needed a way to get documents and TV’s by specifying a template variable value so I came up with a few functions to do that. Here they are if anyone is interested- you’ll need to have the latest API functions installed for these to work I believe. They’re basically extensions of getDocumentChildren and getDocumentChildrenTVarOutput functions but I didn’t want to touch the original functions just to keep upgrades easy. If anyone wants to incorporate the changes into the original functions it should be pretty simple. One note, because this is building a sql query by adding joins, you probably don’t want to make tvars more than few elements - around 5 elements, our machine slowed considerably but it works great if you just need to search by one or two.

    <?php
    //tvars is an associative array with key as tvname and value as tv value
    	function getDocumentChildrenByTVar($parentid=0, $tvars=array(), $published=1, $fields="*", $sort="menuindex", $dir="ASC", $limit="")
    	{
    		$tbn = $this->dbConfig['dbase'].".".$this->dbConfig['table_prefix'];
    		$limit = ($limit != "") ? "LIMIT $limit" : "";
                    $sql = "SELECT sc.$fields ";
    		$i=0;
    		while(list($key, $value) = each($tvars))
    		{
    			$i++;
    			$sql .= " , stc$i.value AS '$key' ";	
    		}
    		$sql .= "FROM ".$tbn."site_content sc ";
    		reset($tvars);
    		$fields = 'sc.'.implode(',sc.',preg_replace("/^\s/i","",explode(",",$fields)));
    		$sort = 'sc.'.implode(',sc.',preg_replace("/^\s/i","",explode(',', $sort)));
    		if($_SESSION['docgroups']) $docgrp = implode(",",$_SESSION['docgroups']);
    		$i=0;
    
    		while(list($key, $value) = each($tvars))
    		{
    			$i++;
    			$sql .= "INNER JOIN ".$tbn."site_tmplvar_contentvalues stc$i ON stc$i.contentid = sc.id INNER JOIN ".$tbn."site_tmplvars st$i ON st$i.id = stc$i.tmplvarid AND st$i.name = '$key' AND stc$i.value = '$value' ";
    		}
    		
    		$sql .= " LEFT JOIN ".$tbn."document_groups dg on dg.document = sc.id LEFT JOIN ".$tbn."documentgroup_names dgn ON dgn.id = dg.document_group WHERE sc.parent = $parentid AND sc.published = $published AND  (1='".$_SESSION['role']."' OR ".(IN_ETOMITE_PARSER ? "NOT(dgn.private_webgroup<=>1)":"NOT(dgn.private_memgroup<=>1)").(!$docgrp ? "":" OR dg.document_group IN ($docgrp)").") ORDER BY $sort $dir $limit;";
    		$result = mysql_query($sql) or die($sql);
    		$resourceArray = array();
    		while($row = mysql_fetch_array($result, MYSQL_ASSOC))
    		{
    			$resourceArray[] = $row;
    		}
    		return $resourceArray;
    		
    	}
    
    	function getDocumentChildrenTVarOutputByTVar($parentid=0, $tvidnames=array(), $tvars=array(), $published=1, $docsort="menuindex", $docsortdir="ASC")
    	{
    		$docs = $this->getDocumentChildrenByTVar($parentid, $tvars, $published, 'id', $docsort, $docsortdir);
    		if(!$docs) return false;
    		else
    		{
    			$result = array();
    			for($i=0;$i<count($docs);$i++)
    			{
    				$tvs = $this->getTemplateVarOutput($tvidnames, $docs[$i]["id"],$published);
    				if($tvs) array_push($result, $tvs);
    			}
    			return $result;
    		}
    	}
    ?>
      • 21301
      • 93 Posts
      I have a set of documents that have a date-TV. The question I have is: What is the easiest way to sort documents by a TV (eg date-TV)?

      In search of an API-call like ’getActiveChildren’ that returns the Template Variables (TVs) as well, I stumbled across the post above. An API addition like this seems usefull to me.

      Would a new API call be the solution? Or is combining two calls to ’getDocumentChildren’ and ’getDocumentChildrenTVars’ the way to go?

      Did someone do this before, any examples?

        • 14267
        • 113 Posts
        Definitely an area needing some work. The TV concept is what Modx is all about, why Modx was forked from Etomite, and IMHO where it really gets its power. But you point out its limitation: when you start trying to sort and filter based on TV’s, things can get complicated due to the way TV’s are stored as table records rather than as actual table fields. For basic presentation sites, the current setup is fine, but for applications that require extensive searching/sorting on TV’s, actual table fields would be much better. For my part I’d love to see a way to handle those incorporated into Modx in the future. Meanwhile I agree that the current function set could use some improvement.


          • 25663 MODX Staff
          • 12,272 Posts
          Gentlemen, please feel free to continue to make suggestions here. Sample code won’t hurt anyone’s feelings, either. Nor will DB tweaks as needed. wink
            Ryan Thrash, MODX Co-Founder
            Follow me on Twitter at @rthrash or catch my occasional unofficial thoughts at thrash.me
            • 1764
            • 680 Posts
            Here are a few more API funcitons I’ve written. They all realate to finding out where you are in the tree. I haven’t had a lot of time to test it with 3.3 but it should work fine.

            <?php
            
             // Written by Adam Crownoble 12/8/2004
             function getParents($page) {
            
              $parents = array();
            
              // Loop while the page hase a valid parent
              while($page = $this->getPageInfo($page,0,'id, parent')) {
            
               // Add the current page to the parents array
               $parents[] = $page['id'];
            
               // Set page to the parent of the current page
               $page = $page['parent'];
            
              }
            
              // Reverse the array so we go from the top down
              $parents = array_reverse($parents);
            
              // Return the array
              return $parents;
            
             }
            
             // Written by Adam Crownoble 1/11/2005
             // Checks to see if a page has a certain page as one of its parents
             function hasParent($page,$parent) {
            
              // Get an array of all parents
              $parents = $this->getParents($page);
            
              // check to see if the page we're looking for is in the list of parents
              if(in_array($parent,$parents)) {
               // We found it return true
               return true;
              }
            
             }
            
             // Written by Adam Crownoble 12/8/2004
             // Get a pages depth in the document tree
             function getPageDepth($pageid) {
            
              // Get an array of all parents
              $parents = $this->getParents($pageid);
            
              // Return the number of parents
              return count($parents);
            
             }
            
            ?>
            
              • 25663 MODX Staff
              • 12,272 Posts
              Those are very cool. I could have used multiple in the DropMenu code to make it more simple!
                Ryan Thrash, MODX Co-Founder
                Follow me on Twitter at @rthrash or catch my occasional unofficial thoughts at thrash.me
                • 21301
                • 93 Posts
                Very cool indeed! I’ll have a look into a new API function to get my docs sorted by TV’s.

                Just for my understanding, please correct me if I’m wrong:

                - ’getActiveChildren’, ’getAllChildren’ and others not document in the API Quick Reference are Etomite functions
                - Functions listed in the API Quick Reference are MODx specific

                What confused me at first is why there’s no reference to the Etomite functions. Will all Etomite functions be supported in the future for backward compatibility? If so, shouldn’t we add them to the API function list? Or refer to them at least?

                  • 25663 MODX Staff
                  • 12,272 Posts
                  Actually, the API Quick Reference is not complete. ALL API fucntions should be in there... no matter their original source.
                    Ryan Thrash, MODX Co-Founder
                    Follow me on Twitter at @rthrash or catch my occasional unofficial thoughts at thrash.me
                    • 21301
                    • 93 Posts
                    Most work was already done. I’ve adapted the API function a little so that you can sort on the TV if you like. The initial function matches for TV-name and TV-value. I’ve added a wilcard value "*" for the TV. This way you can match on the TV-name only. In addition the ’sql like operator’ might be more apropriate here.

                    A little example. Assume you have the TV’s [*date*] and [*file*] assigned to the children of a page with ID 10. This example gives you an array of documents including their TV’s.

                    Unfortunately a date-TV is stored in the database in text format like ’DD-MM-YYYY HH:MM’. Sorting this text string doesn’t make sense. So you’ll still have to sort it later on. See a later post in this thread for doing this.
                    <?php
                    // When the * is replaced with another value, the function matches for that value.
                    $tvars = array(
                      "date" => "*",
                      "file" => "*"
                    );
                    $parent=10;
                    $docs = $modx->getDocumentChildrenByTVar($parent , $tvars, 1, "*", "date ASC, pagetitle DESC, file ASC");
                    
                    // Print variable types and values found
                    for ($index=0; $index<count($data); $index++) {
                      foreach ($data[$index] as $name => $value) {
                        if ($data[$index]["$name.type"]) {
                          print "Variable value: ".$data[$index]["$name"]."<br /";
                          print "Variable type: ".$data[$index]["$name.type"]."<br /";
                        }
                      }
                    }
                    
                    ?>
                    


                    <?php
                    // Version 1.1
                    // Now supports the TV-type
                    //
                    // tvars is an associative array with key as tvname and value as tv value
                      function getDocumentChildrenByTVar($parentid=0, $tvars=array(), $published=1, $fields="*", $sort="menuindex ASC", $limit="")
                      {
                        $tbn = $this->dbConfig['dbase'].".".$this->dbConfig['table_prefix'];
                        $limit = ($limit != "") ? "LIMIT $limit" : "";
                        $fields = 'sc.'.implode(',sc.',preg_replace("/^\s/i","",explode(",",$fields)));
                        $sql = "SELECT $fields ";
                        $i=0;
                        $sort = 'sc.'.implode(',sc.',preg_replace("/^\s/i","",explode(',', $sort)));
                        while(list($key, $value) = each($tvars))
                        {
                          $i++;
                          $sql .= " , stc$i.value AS '$key' ";
                          $sql .= " , st$i.type AS '$key.type' ";
                    
                          // replace the TV names with stc[number].value in the sort string
                          $sort = str_replace("sc.$key","stc$i.value",$sort);
                        }
                        $sql .= "FROM ".$tbn."site_content sc ";
                        reset($tvars);
                        if($_SESSION['docgroups']) $docgrp = implode(",",$_SESSION['docgroups']);
                        $i=0;
                    
                        while(list($key, $value) = each($tvars))
                        {
                          $i++;
                    
                          // Create the 'join on value=' part of the join
                          $join_on_value = ($value=="*") ? "" : " AND stc$i.value like '$value'";
                          $sql .= "INNER JOIN ".$tbn."site_tmplvar_contentvalues stc$i ON stc$i.contentid = sc.id INNER JOIN ".$tbn."site_tmplvars st$i ON st$i.id = stc$i.tmplvarid AND st$i.name = '$key'$join_on_value";
                        }
                    
                        
                        $sql .= " LEFT JOIN ".$tbn."document_groups dg on dg.document = sc.id LEFT JOIN ".$tbn."documentgroup_names dgn ON dgn.id = dg.document_group WHERE sc.parent = $parentid AND sc.published = $published AND  (1='".$_SESSION['role']."' OR ".(IN_ETOMITE_PARSER ? "NOT(dgn.private_webgroup<=>1)":"NOT(dgn.private_memgroup<=>1)").(!$docgrp ? "":" OR dg.document_group IN ($docgrp)").") ORDER BY $sort $limit;";
                    
                        $result = mysql_query($sql) or die($sql);
                        $resourceArray = array();
                        while($row = mysql_fetch_array($result, MYSQL_ASSOC))
                        {
                          $resourceArray[] = $row;
                        }
                        return $resourceArray;
                        
                      }
                    
                    ?>
                    
                      • 21301
                      • 93 Posts
                      Unfortunately a date-TV is stored in the database in text format like ’DD-MM-YYYY HH:MM’. Sorting this text string doesn’t make sense. So you’ll still have to sort it later on.
                      Here’s how I did that:
                      <?php
                      // Parameters:
                      // data: an array of MODx documents
                      // field1, field2: sort on these fields fields
                      // order: 'ASC' or 'DESC'
                      function customSort(&$data, $field1, $field2, $order) {
                        $code = "\$retval = strnatcmp(\$a['$field1'], \$b['$field1']); if(!\$retval) return strnatcmp(\$a['$field2'], \$b['$field2']); return \$retval;";
                        $params = ($order=='ASC') ? '$a,$b' : '$b,$a';
                        usort($data, create_function($params, $code)); 
                      }
                      
                      // This function corrects locally formatted date-TVs
                      // like 'dd-mm-yyyy' to the universal unix time stamp
                      // You might adapt this to your local time format
                      
                      // Parameter: data: an array of MODx documents
                      function correctTime(&$data) {
                        for ($index=0; $index<count($data); $index++) {
                          foreach ($data[$index] as $name => $value) {
                            if ($data[$index]["$name.type"]=='date') {
                              list($date,$time) = split('[ ]', $data[$index][$name]);
                              list($mday,$mon,$year) = split('[-]', $date);
                              $data[$index][$name] = strtotime("$mon/$mday/$year");
                            }
                          }
                        }
                      }
                      ?>