Jump to content
NotionCommotion

Using a while loop with a method

Recommended Posts

I've never tried using a while loop other than with a condition or with a query or something similar.  Is there any reason to do so?  If so, is it common practice?

<?php


$foo=new Foo();
foreach($foo->myGetFunction1($x) as $something) {
    //
}
while($something=$foo->myGetFunction2($x)) {
    //
}


class foo
{
    function myGetFunction1($x)
    {
        static $stmt;
        if(!stmt) $stmt=$this->pdo->prepare('SELECT bla WHERE x=?');
        $stmt->execute([$x]);
        return $stmt->fetchAll();
    }


    function myGetFunction2($x=false)
    {
        static $stmt;
        if(!stmt) $stmt=$this->pdo->prepare('SELECT bla WHERE x=?');
        if(!$x) $stmt->execute([$x]);
        return $stmt->fetch();
    }
}

Share this post


Link to post
Share on other sites

Yes, it's common practice to spend all day long trying to come up with the most obscure solution to an imaginary problem.

 

However, your approach is not optimal. You should foo the bar into the baz, then bla the baz together the qux until the garply is ready.

  • Like 2

Share this post


Link to post
Share on other sites

Yes, it's common practice to spend all day long trying to come up with the most obscure solution to an imaginary problem.

 

However, your approach is not optimal. You should foo the bar into the baz, then bla the baz together the qux until the garply is ready.

 

Obscure? while($rs=$stmt->fetch()){...} is often seen, and doing so as I showed with method is not much different.  Imaginary?  Retrieving data is a very common task.  Please do not reply if only to belittle me.

Share this post


Link to post
Share on other sites

Your question mentioned using 'while' loops (ie, could be also about ANY loops) specifically mentioned it in the context of a condition or a query or something similar.  Well - of course those are the places where a loop is or could be used.  What imaginary situation are you thinking of when you ask this question?  Your second post only mentioned a query fetch  - how about something else since that seems to be your concern.

 

Truthfully, I think your question is completely off the wall and apparently so do others here.

Share this post


Link to post
Share on other sites

ginerjm,  My typical approach is to have a function return all results and then have the calling script iterate over each using a foreach() as shown below.  Maybe it is good practice to have the function return one row at a time in a while loop, but as I have never done so (or recall even seeing others do so), I questioned the practice and thus asked the question. 

    protected function updateResetConfig($config,$id)
    {
        $config->xAxis->categories=[];
        $config->series=[];
        $series=[];
        foreach($this->getChartPoints($id) as $row) {
            if(!in_array($row->category,$config->xAxis->categories)) {
                $config->xAxis->categories[]=$row->category;
            }
            if(!isset($series[$row->series])) {
                $series[$row->series]=[];
            }
            $series[$row->series][]=$row->idPublic;
        }
        foreach($series as $name=>$data) {
            $config->series[]=(object)['name'=>$name,'data'=>$data];
        }
        return $config;
    }


    private function getChartPoints($id)
    {
        static $stmt;
        if(!stmt) {
            $sql="SELECT ccc.name category, ccs.name series, p.idPublic
            FROM charts c
            INNER JOIN charts_common_categories ccc ON ccc.chartsCommonId=c.id
            INNER JOIN charts_common_series ccs ON ccs.chartsCommonId=c.id
            LEFT OUTER JOIN charts_common_has_points cchp ON cchp.chartsCommonSeriesId=ccs.id AND cchp.chartsCommonCategoriesId=ccc.id
            LEFT OUTER JOIN points p ON p.id=cchp.pointsId
            WHERE c.id=?";
            $stmt = $this->pdo->prepare($sql);
        }
        $stmt->execute([$id]);
        return $stmt->fetchAll();
    }

Share this post


Link to post
Share on other sites

without reading thru your enormous chunk of code, I'll offer this.  How would you maintain the position of you 'loop' inside of a function/method once you return that single row?

 

Yes - if you have a distinct purpose for your function/method, handling the entire set of data and returning a single variable/value makes sense.  But having a function that has to return a piece of a result each time it is called sure makes it more complex.

 

How do you come up with these questions?

Share this post


Link to post
Share on other sites

without reading thru your enormous chunk of code, I'll offer this.  How would you maintain the position of you 'loop' inside of a function/method once you return that single row?

 

Yes - if you have a distinct purpose for your function/method, handling the entire set of data and returning a single variable/value makes sense.  But having a function that has to return a piece of a result each time it is called sure makes it more complex.

 

How do you come up with these questions?

 

Maybe wrong, but I thought the static $stmt would automatically maintain position.

 

So, you prefer the distinct code highlighting the issue as I originally posted over the enormous chunk?  Me too, but I get mixed messages.

 

How I come up with these questions?  1. Applicable to my task at hand.  2. Curiosity.  3. Always wish to learn.

Share this post


Link to post
Share on other sites

As with the others, I'm not really sure what your question is really about or what kind of an answer you're looking for. A loop is a loop. The difference between while / for / foreach is how you determine whether or not to keep looping.

 

while is a general loop that just repeats so long as a condition is true. You can use it for anything basically.

 

for is a counting loop. Its just a more refined syntax for the common operation of looping a specific number of times using a counter variable.

 

foreach is a set loop. It's an even more refined syntax for the common operation of looping over a set of items (typically an array).

 

 

For PDO when you're using fetch, you're loop condition is loop as long as fetch returns a valid result. This type of condition calls for a while loop as the number of iterations is essentially unknown.

 

When using fetchAll PDO will instead give you an array which allows you to use a foreach loop instead as you're then dealing with a set of a known size.

 

With PDO you can use foreach directly on the statement object since it implements the Traversable interface. You don't need to use either fetch or fetchAll.

$stmt = $pdo->query('SELECT * FROM example');
foreach ($stmt as $row){
    //...
}
That's my preferred way of PDO results. When creating a method that performs a query for data I generally just return the statement object and loop it using a foreach.

Share this post


Link to post
Share on other sites

Thanks Kicken,

 

Sorry, I thought I was clear, but obviously was not.  My question focused about using a function or method as the loop's condition and having it return one value at a time for each loop iteration using fetch() opposed to returning all using fetchAll(). In hindsight, whether a while loop or a for loop or any other loop for that mater is used is irrelevant which likely furthermore obscured my original question.   I also should have better indicated that the records from the database needed to be manipulated, else a fetchAll() should typically be used.

 

Additionally, I did not explain what I am trying to solve.  I am not a purist about a separate controller and model (separate view, yes!), but sometimes need to do a better job of it.  Often I do the following.  While it only has one loop, it is difficult to read and troubleshoot, and does not allow the query to be reused in other methods.

    public function scriptWithLogicAndDataMixed()
    {
        //....do something
        $sql="SELECT a_bunch_of_columns FROM a_bunch_of_tables WHERE a_bunch_of_conditions=?";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([$id]);
        $data=[];
        while ($row=$stmt->fetch()) {
            //manipulate $row as needed
            $data[]=$row;
        }
        //...do something 
    }

 

Trying to mitigate these deficiencies, I changed to something like the following.  Probably negligible, but as a matter of principle don't like to have redundant loops.

 

    public function scriptWithLogicButNotData()
    {
        //....do something
        $data=$this->getData($id);
        foreach($data as $row) {
            //....do something
        }
        //...do something 
    }


    private function getData($id)
    {
        static $stmt;
        if(!stmt) {
            $sql="SELECT a_bunch_of_columns FROM a_bunch_of_tables WHERE a_bunch_of_conditions=?";
            $stmt = $this->pdo->prepare($sql);
        }
        $stmt->execute([$id]);
        $data=[];
        foreach ($stmt as $row){
            //manipulate $row as needed
            $data[]=$row;
        }
        return $data;
    }    

So, then I come up with the following which was why I originally  asked this question.

    public function methodWithLogicButNotData()
    {
        //....do something
        $this->getData($id);    //Initiate query
        foreach($this->getData() as $row) {
            //....do something
        }
        //...do something 
    }


    private function getData($id=false)
    {
        static $stmt;
        if(!stmt) {
            $sql="SELECT a_bunch_of_columns FROM a_bunch_of_tables WHERE a_bunch_of_conditions=?";
            $stmt = $this->pdo->prepare($sql);
        }
        //Probably should use two methods: first to load and second to retreive?
        if(!$id) {
            return $stmt->execute([$id]);
        }
        else {
            $row=$stmt->fetch();
            //manipulate $row as needed
            return $row;
        }
    }  
I am thinking now that maybe some wrapper class should be used?

 

By the way, never knew I can fetch records without a fetch statement as shown in your last example.  While very concise, it appears a little magical, and I currently don't know where I stand.

 

Thanks again

 

 

Share this post


Link to post
Share on other sites

What is the point of calling getData() a second time (and more):

 

$this->getData($id);    //Initiate query
foreach($this->getData() as $row)
{
      //....do something
}

when a simple fetch is all you need in this spot.  Why add the overhead of calling a function that is itself going to call a function???

 

You spend so much time writing code examples about things that just don't need to be analyzed so much....

Share this post


Link to post
Share on other sites

I think the problem here is that the question is really just hypothetical and the "examples" provided are poor. Plus, the forum title is misleading.

 

The real question, as I understand it, is if it ever makes sense to create a function/method that returns a finite amount of data to just return one piece of data at a time as opposed to returning all of the data in one call. The answer is simple: Yes. What is not simple is knowing WHEN that makes sense to do. The first example of a method to return the first value from a query is flawed because it would return the same value every time it is executed and would result in an infinite loop/

 

Every situation is unique so there is no way (without a specific problem statement) to say when such a process should be used. So, instead of asking is some hypothetical thing can be done, a better way to approach is to look into such things when you have a problem that needs to be solved.

  • Like 1

Share this post


Link to post
Share on other sites

@Psycho.  Thanks for your response.  I actually do have a problem to solve now.  How to use a method to return one record at a time in a loop was just a solution to that problem which I was investigating, and my real problem is how to best separate logic and the database.  I will later start a new post with actual script.

 

@ginergm  Doing so is exactly what I am trying to get away from.

Share this post


Link to post
Share on other sites

You don't seem to be trying very hard to 'get away' from writing code.  You're going to start a new post for more it seems.

 

As for this problem - As I said - why call a function to call a function that you can/should just do in your current script/module?  To write a function to handle the return of a single record/row makes no sense.  You still have to process it in the calling script/module, otherwise you will be writing a custom function for each and every place in which you wish to place this idea of yours.  Even a script to execute the query seems meaningless since queries are different each time and therefore the parms will be different and therefore the prepare and the vars assigned will change.  Write a function for what purpose?

 

Functions are great for the reason they were invented.  They keep you from replicating code; help you maintain code that needs to be altered occasionally; enforce standard practices in your coding style and probably several other reasons.  They are not necessarily meant to be global solutions to calling already-written functions such as you are talking about.  Sure - write a function/module to handle the db connection and return a handle to it.  Great!  But to then try and make a function that will do your prepare and your query and then to return a single record - that's just not feasible.

 

Write an SQL statement.

Assign it to a query statement variable with the prepare function and check the result

Build your parms array with local vars

Execute the query statement and check the result

Loop thru the query results, processing each row in a block of code that is most likely specifically for the current task and none other.

 

Who needs a function for any of that?

 

And since you seem to have said you solved the 'return one record' problem, just how did you accomplish that?  Did you use the 'frowned-upon' global keyword?

Share this post


Link to post
Share on other sites

Thanks ginerjm,

 

My reasons for the function were:

  1. Readability.  The method in question was getting rather large and hard to understand and troubleshoot.
  2. Flexibility.  Several sections of code need to query the same table and loop through the results and modify them, and while the modifications are mostly the same, there are a few small differences and I didn't want to loop through them a second time as a mater of principle.
  3. Reuse.  Didn't want to duplicate the SQL statement to meet the flexibility requirement.

And no, didn't say I solved the 'return one record' problem, but just that doing so wasn't the end goal.  And, no, not using 'frowned-upon' global keyword but will inject.

Share this post


Link to post
Share on other sites

My reasons for the function were:

  1. Readability.  The method in question was getting rather large and hard to understand and troubleshoot.
  2. Flexibility.  Several sections of code need to query the same table and loop through the results and modify them, and while the modifications are mostly the same, there are a few small differences and I didn't want to loop through them a second time as a mater of principle.
  3. Reuse.  Didn't want to duplicate the SQL statement to meet the flexibility requirement.

 

OK, now you are throwing out new requirements. #2 and #3 are things which I don't believe you stated previously. You say you want to loop over the same data multiple times and modify them. If you modify them, doesn't that necessitate running a new query next time to get the current data? Or are you not saving these modifications. Either way, why are you making this so difficult. Why not just return an array and loop over that array once, twice, or many times (modifying the data in the array each time). If you need to save the results after all the loops are done, just have a separate method that you pass the array to once finished to update the records.

 

Everything you keep asking about can be done. But, the real question is whether it should be done. You keep providing vague responses rather than a specific use case and business need.

Share this post


Link to post
Share on other sites

OK, now you are throwing out new requirements. #2 and #3 are things which I don't believe you stated previously. You say you want to loop over the same data multiple times and modify them. If you modify them, doesn't that necessitate running a new query next time to get the current data? Or are you not saving these modifications. Either way, why are you making this so difficult. Why not just return an array and loop over that array once, twice, or many times (modifying the data in the array each time). If you need to save the results after all the loops are done, just have a separate method that you pass the array to once finished to update the records.

 

Everything you keep asking about can be done. But, the real question is whether it should be done. You keep providing vague responses rather than a specific use case and business need.

 

 

Additionally, I did not explain what I am trying to solve.  I am not a purist about a separate controller and model (separate view, yes!), but sometimes need to do a better job of it.  Often I do the following.  While it only has one loop, it is difficult to read and troubleshoot, and does not allow the query to be reused in other methods.

 

Psycho, I will do as you say and loop twice, and my immediate problem is solved.  I am not throwing out new requirements but am just looking design patterns to interface to the db.   Sorry for beating around the bush.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.