2

I have a simple recursive array function that looks like this:

function recursive_array($results) {
    global $DBH;
    if (count($results)) {
        echo $res - > Fname;
        foreach($results as $res) {
            $STH = $DBH - > query("SELECT FID,FParentID,Fname FROM list WHERE FParentID  = ".$res - > FID."");
            $fquerycount = $STH - > rowCount();
            $STH - > setFetchMode(PDO::FETCH_OBJ);
            recursive_array($STH);
        }
    }
}


$FID = isset($_GET['FID']) ? $_GET[' FID'] : 0;
$STH = $DBH - > query("SELECT FID,FParentID,Fname FROM list WHERE FParentID ='0' ");
$STH - > setFetchMode(PDO::FETCH_OBJ);
recursive_array($STH);

I also have created a simple query class that looks like this:

class queryloop {
    function __construct($args) {
        global $DBH;
        $table = $args['tbl'];
        if (array_key_exists('orderby', $args)): $orderby = 'ORDER BY '.$args['orderby'];
        else: $orderby = '';endif;
        if (array_key_exists('groupby', $args)): $groupby = 'GROUP BY '.$args['groupby'];
        else: $groupby = '';endif;
        if (array_key_exists('start', $args)): unset($orderby);$start = $args['start'].' , ';
        else: $start = '';endif;
        if (array_key_exists('limit', $args)): $limit = 'LIMIT '.$start.' '.$args['limit'];
        else: $limit = '';endif;
        // UNSET the previously used array keys so they are not use again to create the query string
        unset($args['tbl']);
        unset($args['orderby']);
        unset($args['groupby']);
        unset($args['start']);
        unset($args['limit']);
        // Checks if args still an array after UNSET above.  If not empty create the query string
        if (!empty($args)): foreach($args as $k = > $v): $querystr. = 'AND '.$k.' = \''.$v.'\'';endforeach;
        // If args array empty return empty query string
        else: $querystr = '';endif;$STH = $DBH - > query("SELECT * FROM ".$table." WHERE key = '".KEY."'  ".$querystr."  ".$groupby." ".$orderby."  ".$limit." ");
        if ($STH): $STH - > setFetchMode(PDO::FETCH_OBJ);
        while ($row = $STH - > fetch()): foreach($row as $key = > $val):
        // check if value is numeric //        
        if (is_numeric($row - > $key)): $data[$row - > ID][$key] = $row - > $key;
        // check if value is array //
        elseif(is_array($row - > $key)): $data[$row - > ID][$key] = $row - > $key;
        // check if value is not numeric or array convert to html entities //
        else: $data[$row - > ID][$key] = htmlentities($row - > $key);endif;endforeach;endwhile;$this - > data = json_encode($data); // return json array if data
        else: $this - > data = ''; // return 'null' if no data
        endif;
    }
}

$args = array('tbl' = > 'atable', 'limit' = > '5', 'start' = > '200', 'orderby' = > 'ID DESC');
$loop = new queryloop($args) // run the loop etc.

How do I turn my recursive array into something like the class queryloop so that I can "pull out" json endoded data I know that this (below) is totally wrong but what ever I do I cannot get a correctly formed json array or even anything to return form my attempted class below. Help would be much appreciate. Thanks in advance.

class recloop {
    function __construct() {}

    function recursive_array($results) {
        global $DBH;
        if (count($results)) {
            foreach($results as $res) {
                echo $res - > Name;
                $STH = $DBH - > query("SELECT * FROM atable WHERE ParentID  = ".$res - > ID."");
                $fquerycount = $STH - > rowCount();
                $STH - > setFetchMode(PDO::FETCH_OBJ);
                recursive_array($STH);
            }
        }
    }

    function recursive_start() {
        global $DBH;
        $ID = isset($_GET['ID']) ? $_GET['ID'] : 0;
        $STH = $DBH - > query("SELECT * FROM atable WHERE ParentID  = '".$ID."' ");
        $STH - > setFetchMode(PDO::FETCH_OBJ);
        recursive_array($STH);
    }
}
9
  • 9
    Oh, what a... code. 1) Use IDE with auto-indenting (NetBeans, for example). 2) Don't send queries to database in recursive function, it's recursively slow. 3) Don't use global variables (never). 4) use prepared statements or escape data in your SQL queries. Commented Aug 13, 2011 at 7:32
  • 2
    Act like a tween and get BRACES! I know that : and endif are legit in PHP, but they actively irritate most people. Commented Aug 13, 2011 at 7:43
  • 5) Please just read some PHP. The operator is $object->member. Nobody does $object - > member. 6) Don't use if: else:, use if { } else { }. Get used to C-syntax, you're going to encounter it in programming. A lot. Commented Aug 13, 2011 at 7:43
  • You might also be interested in: codereview.stackexchange.com Commented Aug 13, 2011 at 7:46
  • Meagar - OK so it seems I'm coming in for a lot of flack, BUT I did not create object - > master! it was edited. Commented Aug 13, 2011 at 7:46

1 Answer 1

2

How do I turn my recursive array into something like the class queryloop so that I can "pull out" json endoded data I know that this (below) is totally wrong but what ever I do I cannot get a correctly formed json array or even anything to return form my attempted class below. Help would be much appreciate. Thanks in advance.

To answer your question, I would say it's not specific if you encapsulate your routines into objects or not that much, but that you take care that each object is there for a sole purpose. For example:

  • One object is fetching the data from the database.
  • One object/composite/array is the data-structure, representing the data.
  • One object or function is taking over the job to convert/encode the data into json.

Within your code I see that you right now are only running SQL-queries. The data fetched from the database server is not stored into a return variable at all, it get's directly consumed while being recursively processed. I assume you do this for debugging reasons.

So the actual question is, what do you want to do? You write that you want to encode an object into json output, which is perfectly possible with json_encodeDocs, however I think you refer to some specific data, like the entity (data) of the most parentId or something.

Following is some mock-up code based on your code for reading purposes (not tested, must not match your needs) that can provide all parent objects of that one specified by ID by using recursion. The recursion has been criticised because this can result in running a lot of queries - and additionally there is risk to create an endless loop which will result in a recursion stack overflow - your program crashes then.

To handle that alternatively, this is bound to the database design (which should be done before the design of the code, and I don't know your database design nor what you actually want to do, so I can't add assumptions for that). So the following code takes care of already queried objects only while still using recursion as the strategy to query your database.

For the actual data-structure I opted for an array of plain old PHP objects, keyed by the ID field from the database (which I assume that it exists per record):

/**
 * HTTP Get Parameter (Input)
 */
class HTTPGetParameter {
    private $name;
    private $default;
    public function __construct($name, $default = '') {
        $this->name = (string) $name;
        $this->default = (string) $default;   
    }
    /**
     * @return string
     */
    public function getValue()
    {
        return isset($_GET[$name]) ? $_GET[$name] : $this->default;
    }
    /**
     * @return int
     */
    public function getValueInt()
    {
        return (int) $this->getValue();
    }
    /**
     * @link http://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.tostring
     */
    public function __toString()
    {
        return $this->getValue();
    }
}

/**
 * Data Provider
 */
class PDODataProvider
{
    private $pdo;
    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }
    /**
     * @return array
     */
    public function findAllATableParents($id)
    {
        return $this->findAllOn('atable', 'ParentID', $id);
    }
    public function findAllBTableParents($id)
    {
        return $this->findAllOn('btable', 'ParentID', $id);
    }
    private function findAllOn($table, $field, $id)
    {
        $id = (int) $id;

        $objects = array();

        $sql = sprintf("SELECT * FROM %s WHERE %s  = '%d'", $table, $field, $id);
        $pdoStatement = $this->pdo->query($sql);
        $pdoStatement->setFetchMode(PDO::FETCH_OBJ);

        foreach($pdoStatement as $parent)
        {
             $parentId = $parent->ID;

             # parents that had been queried are skipped
             if (isset($objects[$parentId]))
                 continue;

             $objects[$parentId] = $parent;

             # add parent objects by recursion
             $objects += $this->findAllParents($parentId);
        }
        return $objects;
    }
}


/**
 * main
 */
$data = new PDODataProvider($DBH);

$id = new HTTPGetParameter('ID', 0);

$objects = $data->findAllParents($id->getValueInt());

echo json_encode($objects);

I hope this example is helpful for you to answer your question.

Sign up to request clarification or add additional context in comments.

7 Comments

Reading this - very slowly - :) Think I can work it out - hence the slow reading! not too used to creating classes as not called for much in my general type of work (plus don't really understand them ;) so I tend to create "hard" queries most of the time. Think I can learn from your example. Many thanks for answer.
Ummmmm think I have it now as said this is a bit above me :) OK, so the class works - thank you - but how/where could I add $args for the actual table to query "similar" to my first class i.e. an array of thinks like table name as if it can be done I can see this replacing my "basic" class at the top of the page - has more functionality. i.e. we do something like this in the query: $sql = sprintf("SELECT * FROM ".$args['table']." WHERE ParentID = '%d'", $id);
Create a private helper function that get's the table name as a second parameter but does the same. Then move the code from the actual function into the helper function and parametrize the table name. Then add another public function that is looking for the second table objects you need. I'll update the code.
Sorry Hakre, Nope lost it now I can't see where you "declare" the table when calling the class, all I see is "$objects = $data->findAllParents($id->getValueInt());". I see the helper functions for "ATable" and "BTable" but not where you actually call the table (if that is the phrase) - should it be something like this? $objects = $data->findAllParents($id->getValueInt(),'SOMETHING HERE TO CALL THE TABLE');?
No, I hide the table name inside the class. The normal script should not know the table name, but only which data it requests. Table names are hidden in the DataProvider then. Instead call findAllATableParents or findAllBTableParents.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.