Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

PHP

Vince Brown
Vince Brown
16,249 Points

Php/MySQL code refactor to separate concerns

I am working on a personal project and wrote a function that queries the database for resources by a specific type and outputs them. The original function has everything together, And I really wanted to go back and refactor to separate concerns. The whole concept of Separation of Concerns still goes over my head but wanted to give it a try. Excuse me if I dont use the "terms" right. This question might get a little long so if you end up taking the time to read all the way through and help I want to say thank you up front.

Original Function -

Fetches all resources with type article from the database and displays them in list items correctly.

<?php
function show_resources($db,$type='article') { 

    $query_resources = "SELECT *
    FROM resources
    WHERE type = '$type'
    ORDER BY recommended DESC";

  // run query
    $result_resources = $db -> query($query_resources);

  //check to see if rows were found
    if ($result_resources -> num_rows >= 1) {
      while($row = $result_resources -> fetch_assoc()){
        ?>

        <li class="resource--<?php echo $type; ?>">
          <div class="resource__main-info">
            <a href="<?php echo $row['link'];?>" class="resource__link">
              <h2 class="resource__title"><?php echo $row['title'];?></h2>
              <p class="resource__summ"><?php echo $row['summary'];?></p>
            </a>
          </div>
</li>
<?php 
      } // end while
    } else { 
      echo 'Sorry no resources found' ;
    } // end if else.
    ?>

The function above works perfectly but when I started to refactor. My refactored code below only is fetching the first result from the database and when displayed with a while() gets stuck in an infinite loop.

Refactored Function

MODEL?

<?php 

function get_resource_by_type($db,$type){
  $query = "SELECT *
    FROM resources
    WHERE type = '$type'
    ORDER BY recommended DESC";
  try{
     $results = $db -> query($query);
  } catch (Exception $e) {
    echo "Data could not be retrieved from the database.";
    exit;
  }
  $resources = $results->fetch_assoc();

  // print_r($resources); printed $resources to screen and only an array with the first result is displayed

return $resources;
}
?>

VIEW?

 <li class="resource--article">
  <div class="resource__main-info">
    <a href="<?php echo $resource['link'];?>" class="resource__link">
      <h2 class="resource__title"><?php echo $resource['title'];?></h2>
      <p class="resource__summ"><?php echo $resource['summary'];?></p>
    </a>
  </div>
</li>

CONTROLLER?

<?php require_once('includes/config.php');?>
<?php require_once(ROOT_PATH . 'includes/resources.php');?>
<?php require_once(ROOT_PATH . 'includes/header.php'); ?>
<?php include(ROOT_PATH . 'includes/functions.php') ;?>
<?php $article_resources = get_resource_by_type($db,'article');?>


<section class="resource-section articles">
  <h2 class="section__header">Articles</h2>
  <p class="section__summary">Articles that have been written about SVG.</p>
  <ul class="resources">
    <?php 
    while($resource = $article_resources) {
        include(ROOT_PATH . 'includes/partial-resource-item.html.php');
    }
    ?>
  </ul>
</section>

The While Loop in the controller just outputs the first resource over and over again.(Infinite loop?)

Hopefully this question made sense. And if you made it this far thank you!

Cheers!

2 Answers

Your while loop doesn't make much sense now you've removed the PDO 'stuff' from it. A while loop checks for a truthy value. In Your original function:

<?php

    if ($result_resources -> num_rows >= 1) {
      while($row = $result_resources -> fetch_assoc()){

$result_resources is a PDO object. You're looping through each row in the database (dependent on your query) and that row is being assigned to the $row variable as an array. This statement will continue to be true - i.e. the while loop will continue to run - until you've run out of rows. When you've run out of rows, $row will be equal to null - not an array (in php, a non empty array is true and an empty array or null will be false) - and the statement is false. Here endeth the while loop.

In your new code $article_resources is no longer a PDO object. Instead it's an array of a single row returned from the database. Therefore your statement here:

<?php

 while($resource = $article_resources) {

Is just assigning the $article_resources array to the $resource variable over and over again. Remember a non empty array returns true - meaning this statement is always true, meaning your while loop is forever.

Your $article_resources variable at this point only contains data from one row. To get and return all the rows in the article resources variable, you'll need a different method on your pdo object.

<?php

$result = $sth->fetch_assoc(); // this returns one row

// to:

$article_resources = $results->fetchAll(PDO::FETCH_ASSOC); // this returns all the rows

$article_resources should now contain an array of multiple rows etc..

PHP has an inbuilt function to loop around all the items in an array called a forach loop - as already mentioned :-)

<?php

foreach($article_resources as $resource) {
        include(ROOT_PATH . 'includes/partial-resource-item.html.php');
    }

RE your MVC - separation of concerns, in your case here it's still a little confused - but this isn't the end of the world. Your 'Model' is looking okay because it's a file that handles everything to do with your resource - it's an interface that the application can interact with and have information returned. Yay!

Your controller and view are almost one in the same. The view you've described above is indeed a view - more specifically a 'partial'. It's a small piece of reusable display code, so it makes sense to have this separated.

Your controller is the only section where you've not quite separated out your concerns to MVC. I.e. you shouldn't have any code that outputs to the screen in your controller file. Strictly speaking, a controller receives a request, decided what is has to do such as redirecting, sending mail, returning a view - all good things. In your sections of includes, you're including the header (I guess navigation etc...) with function includes. You'll want to try and separate those.

There's a course on Treehouse for Laravel, which is one of the most popular MVC frameworks for php. After that course, you should have a really clear understanding of Models, Views and Controllers.

This is going one step further so you might want to ignore this until you've got it working again. If you haven't got onto binding your data yet, I would almost certainly bind your parameters instead of including them in your query string. PHP Docs for binding params

<?php


  try{
    $results = $db->prepare(
    "SELECT *
    FROM resources
    WHERE type = :type
    ORDER BY recommended DESC";
);
    $results->bindParam(':type', $type);
    $results->execute();
  } catch (Exception $e) {
    echo "Data could not be retrieved from the database.";
    exit;
  }

$resources = $results->fetchAll(PDO::FETCH_ASSOC);
Vince Brown
Vince Brown
16,249 Points

Hey Tom, Thank you ! You really helped to clarify everything I had questions about I really appreciate it your answer was really easy to grasp. I am definitely going to be checking out the Laravel class on here to further my knowledge on MVC.

Happy Holidays! Cheers, Vince

John Wheal
John Wheal
27,969 Points

Can't you use a foreach loop in the controller to simply loop through the $article_resources? At the moment you are setting $resource equal to $article_resources (two variables) and this will therefore always be true.

foreach (article_resources as article_resource) {
    include(ROOT_PATH . 'includes/partial-resource-item.html.php');
}
Vince Brown
Vince Brown
16,249 Points

Hey John , I originally was using a foreach loop

<?php
foreach($article_resources as $resource) {
        include(ROOT_PATH . 'includes/partial-resource-item.html.php');
    }
?>

But the output was funky and spitting out

<li class="resource--article">
  <div class="resource__main-info">
    <a href="1" class="resource__link">
      <h2 class="resource__title">1</h2>
      <p class="resource__summ">1</p>
    </a>
  </div>
</li> 

 <li class="resource--article">
  <div class="resource__main-info">
    <a href="a" class="resource__link">
      <h2 class="resource__title">a</h2>
      <p class="resource__summ">a</p>
    </a>
  </div>
</li> 

 <li class="resource--article">
  <div class="resource__main-info">
    <a href="h" class="resource__link">
      <h2 class="resource__title">h</h2>
      <p class="resource__summ">h</p>
    </a>
  </div>
</li> 

So my guess is something with the view partial cause it seems like the echos arent working.

<a href="<?php echo $resource['link'];?>" class="resource__link">
      <h2 class="resource__title"><?php echo $resource['title'];?></h2>
      <p class="resource__summ"><?php echo $resource['summary'];?></p>

Thanks again.

John Wheal
John Wheal
27,969 Points

Try vardump($resource) to see what the array contains. That will help you narrow down the problem.