MVC OOP HELP!

Hi guys,

I am having an issue with my foreach loop. it is looping through an array of data that is coming from the database. the table has only one row but for some reason it is displaying it 6 X instead of once. Any suggestions would help, More details below.

Database : this is the table I am trying to output in my php

Screen-Shot-2017-11-26-at-11.23.45.png
But as mentioned above it is looping through and outputting 6 X (See image below)

Screen-Shot-2017-11-26-at-11.24.58.png
Here is the code :

MODEL
<?php

class PostsModel extends BaseModel
{
 
  // Get Post Details
	
	public function getPost($id)
	{	
				$stmt = $this->db->prepare("SELECT * FROM posts WHERE user_id=:id");
				$stmt->execute(array(':id'=>$id));
				
				$postDetails = $stmt->fetch(PDO::FETCH_ASSOC);
			
			try {	
							
				if($stmt->rowCount() == 1)
				{       
					return $postDetails;	
				} else {
					return false;
				}

			} catch(PDOException $e) {
					echo $e->getMessage();
				}
	}
    
    // Add Post
    
    public function addPost($firstName, $lastName, $email,$password)
	{
		try
		{	
			
			$password = sha1($password);
			$stmt = $this->db->prepare("INSERT INTO users(firstname, lastname, email, password) 
		                                               VALUES(:firstName, :lastName, :email, :password)");
												  
			$stmt->execute(array(':firstName'=>$firstName,
								 ':lastName'=>$lastName,
								 ':email'=>$email,
								 ':password'=>$password));							  
				
		} catch(PDOException $e) {
			echo $e->getMessage();
		}				
	}

    //Edit Post

	public function editPost($firstName, $lastName, $id)
	{
		try
		{	

			$stmt = $this->db->prepare("UPDATE users
										SET firstname=:firstname,
											lastname =:lastname
										WHERE  user_id = :id");
													
			$stmt->execute(array(':firstname'=>$firstName,
									':lastname'=>$lastName,
									':id'=>$id));							  
				
		} catch(PDOException $e) {
			echo $e->getMessage();
		}				
	}
    
    
    public function deletePost($firstName, $lastName, $id)
	{
		try
		{	

			$stmt = $this->db->prepare("UPDATE users
										SET firstname=:firstname,
											lastname =:lastname
										WHERE  user_id = :id");
													
			$stmt->execute(array(':firstname'=>$firstName,
									':lastname'=>$lastName,
									':id'=>$id));							  
				
		} catch(PDOException $e) {
			echo $e->getMessage();
		}				
	}
	
}

Open in new window


VIEW

<div id='posts-page'>    
<div class='wrapper'>
<div class='table'>
    
    <div class='row header blue'>
      <div class='cell'>
        Name
      </div>
      <div class='cell'>
        Description
      </div>
      <div class='cell'>
        Date
      </div>
      <div class='cell'>
        View 
      </div>
    </div>
<?php 
if(isset($postDetails)){     
foreach($postDetails as $post){ 
echo "<div class='row'>" . "<div class='cell'>" .
        $postDetails['post_name'] .
      "</div>" .
      "<div class='cell'>" .
        $postDetails['description'] .
      "</div>" .
      "<div class='cell'>" .
        $postDetails['post_date'] .
      "</div>" .
      "<div class='cell'>".
        "<a class='link-button' href='". URL . "/Posts/id/" .$postDetails['post_id']. "'>View Post<a>".
      "</div></div>";
  }
} else {
    header("Location:".URL."user/login");
}
?>
  </div>
  </div>
</div>

Open in new window


CONTROLLER

<?php

class PostsController extends BaseController
{
    public function __construct()
    {
			$this->loadModel('PostsModel');

	}


    public function index($status = '')
    {
          $pageTitle = 'Posts';
        // load views

        require APP . 'view/_templates/header1.php';

        if($this->is_loggedin()){

         $id = $_SESSION['user_session']['user_id'];
         $postDetails = $this->model->getPost($id);

         require APP . 'view/Posts/index.php';
         require APP . 'view/_templates/footer.php';

        } else{
            $this->redirect('user');	
           
        }


    }
    

   public function add($status = '')
    {
          $pageTitle = 'Add post';
        // load views

        require APP . 'view/_templates/header1.php';

        if($this->is_loggedin()){

         $id = $_SESSION['user_session']['user_id'];
         $profileDetails = $this->model->getPost($id);

         require APP . 'view/Posts/add.php';
         require APP . 'view/_templates/footer.php';

        } else{
            $this->redirect('user');	
           
        }
   }
    
    
    public function edit($status = '')
    {
          $pageTitle = 'Edit post';
        // load views

        require APP . 'view/_templates/header1.php';

        if($this->is_loggedin()){

         $id = $_SESSION['user_session']['user_id'];
         $profileDetails = $this->model->getPost($id);

         require APP . 'view/Posts/edit.php';
         require APP . 'view/_templates/footer.php';

        } else{
            $this->redirect('user');	
           
        }
   }
    
    
    public function delete($status = '')
    {
          $pageTitle = 'Delete post';
        // load views

        require APP . 'view/_templates/header1.php';

        if($this->is_loggedin()){

         $id = $_SESSION['user_session']['user_id'];
         $profileDetails = $this->model->getPost($id);

         require APP . 'view/Posts/delete.php';
         require APP . 'view/_templates/footer.php';

        } else{
            $this->redirect('user');	
           
        }
   }
    
   
    
public function id($id){

         $profileDetails = $this->model->getPost($id);

         if($profileDetails){
             $pageTitle = $profileDetails['post_name']; 
         } else {
             $pageTitle = "Unknown post Page";
             $error = "No Record Found";
         }
         


        require APP . 'view/_templates/header1.php';
        require APP . 'view/Posts/post.php';
        require APP . 'view/_templates/footer.php';

    }    
    
    
}

Open in new window


This is a uni project so any help asap would be highly appreciated.
Mike MooreAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Julian HansenCommented:
Let's look at your code
foreach($postDetails as $post){ 
echo "<div class='row'>" . "<div class='cell'>" .
        $postDetails['post_name'] .
      "</div>" .
      "<div class='cell'>" .
        $postDetails['description'] .

Open in new window


Let's look at where $postDetails comes from
public function getPost($id)
	{	
				$stmt = $this->db->prepare("SELECT * FROM posts WHERE user_id=:id");
				$stmt->execute(array(':id'=>$id));
				
				$postDetails = $stmt->fetch(PDO::FETCH_ASSOC);
			
			try {	
							
				if($stmt->rowCount() == 1)
				{       
					return $postDetails;	

Open in new window

Specifically
$postDetails = $stmt->fetch(PDO::FETCH_ASSOC);

Open in new window

So $postDetails is a single record

You notice in your first bit of code you have this
foreach($postDetails as $post){ 

Open in new window

But you access your fields like this
 $postDetails['post_name'] .

Open in new window


You don't actually use the $post foreach variable

If you did you would find that it would ouput the value in each cell.

So your problem is you are iterating over $postDetails as though it was an array of rows (when it is in fact an array of fields - 6 of them to be precise - which is why you get 6 rows output) and for each field you are outputing the record.

You either need to create a function that returns an array of rows - and then modify your foreach to use $post instead of $postDetails - or adapt your code to expect only one record - i.e no foreach - which one depends on what your code is meant to do.
Mike MooreAuthor Commented:
Thanks for your comment,

Still not understanding to be honest. I get what you mean now that its outputting 6 rows as its a 6 item array, However I just can't seem to figure out what I need to change exactly.

Thanks
Julian HansenCommented:
I can't answer that without knowing what your expectations are.

You are asking for a single record but then treating it as if it was an array of records.

Which is it?

Will you only ever expect 1 record or a range of records?

If 1 record then remove the foreach loop and just output the result of $postDetails as you are alrady.

If you want a range of records then
a) You have to create a new function that returns a list of record - not a single record as is the case with getPost
b) You need to iterate over that list with a foreach but use the iteration variable (current row) to output your values

Define which one you are expecting and I can give you more details on how (and which) of the above suggestions should be used.
OWASP: Forgery and Phishing

Learn the techniques to avoid forgery and phishing attacks and the types of attacks an application or network may face.

Mike MooreAuthor Commented:
I am looking to have a range of data, so I think I need an extra function then.

Basically I just want to output the details of each post in a list using this table.
Julian HansenCommented:
Is this going to be for a specific user or all users?

What is confusing is this line in getPost
$stmt = $this->db->prepare("SELECT * FROM posts WHERE user_id=:id");

Open in new window


If there are multiple posts for users then this will potentially return multiple rows yet this line
$postDetails = $stmt->fetch(PDO::FETCH_ASSOC);

Open in new window

Says you are only interested in the first one that comes up - which does not make sense I would expect to see this filtered by date or some other criteria to get a relevant post - this seems a bit arbitrary

If you want to get all posts for a user you would need to do this
public function getPosts($id)
{  
  $data = array();
  try {  
    $stmt = $this->db->prepare("SELECT * FROM posts WHERE user_id=:id");
    $stmt->execute(array(':id'=>$id));
    while($row = $postDetails = $stmt->fetch(PDO::FETCH_ASSOC)) {
      $data[] = $row
    }
  } catch(PDOException $e) {
    echo $e->getMessage();
  }
  
  return $data;
}

Open in new window


You would then output this data like so
// Convert defined variable into $ variable so we can use in HEREDOC below
$url = URL;
// assumes $userid is already set
$postDetails = getPosts($userid);
foreach($postDetails as $post) {
  echo <<< HTML
    <div class="row">
      <div class="cell">{$post['post_name']}</div>
      <div class="cell">{$post['description']}</div>
      <div class="cell">{$post['post_date']}</div>
      <div class="cell"><a class='link-button' href="{$URL}/Posts/id/{$post['post_id']}">View Post<a></div>
    </div>
HTML;
}

Open in new window

Mike MooreAuthor Commented:
yes each post needs to be linked to the user.

that codes gives me this error :

Fatal error: Uncaught Error: Call to undefined function getPosts() in /Applications/MAMP/htdocs/cobox/application/view/Posts/index.php:22 Stack trace: #0 /Applications/MAMP/htdocs/cobox/application/controller/PostsController.php(23): require() #1 /Applications/MAMP/htdocs/cobox/application/core/Application.php(67): PostsController->index() #2 /Applications/MAMP/htdocs/cobox/application/config/config.php(140): Application->__construct() #3 /Applications/MAMP/htdocs/cobox/public/index.php(22): require('/Applications/M...') #4 {main} thrown in /Applications/MAMP/htdocs/cobox/application/view/Posts/index.php on line 22

Sorry if I'm being bit thick, Im new to MVC and OOP.

I really do appreciate your help tho.
Julian HansenCommented:
You need to adapt the code I posted to your code base. I provided a general solution - you won't be able to just cut and paste.

Put the getPosts function in the same class as the getPost()

Then wherever you were setting $postDetails before change that from using getPost to getPosts.

Finally, replace the output code (your current foreach) with the foreach code in my sample
Mike MooreAuthor Commented:
I just can't get it working no matter what I do :(

its not even letting me use $post instead of $postDetails.

Originaly I would have done it like this :

foreach($postDetails as $post){ 
echo "<div class='row'>" . "<div class='cell'>" . 
       $post['post_name'] .
      "</div>" .
      "<div class='cell'>" .
        $post['description'] .
      "</div>" .
      "<div class='cell'>" .
        $post['post_date'] .
      "</div>" .
      "<div class='cell'>".
        "<a class='link-button' href='". URL . "/Posts/id/" .$post['post_id']. "'>View list<a>".
      "</div></div>";
  }

Open in new window


But still no luck
Julian HansenCommented:
How no luck - errors, unexpected output - need something to go on?
The logic is sound - so it is implementation we need to look at. I need to see what is happening and how you have implemented the code.

Sidebar: Seriously consider using HEREDOC - it will make your life so much easier than having to manage quotes (single / double) and variable insertions
Mike MooreAuthor Commented:
I get this error:

Warning: Illegal string offset 'post_name' in /Applications/MAMP/htdocs/cobox/application/view/Posts/index.php on line 23

And yeah I will definitely look into heardoc syntax thanks.
Julian HansenCommented:
Can I see what line 23 of index.php looks like - it sounds like you are trying to get an index of the post that does not exist but without a context it is not possible to nail this down.
Mike MooreAuthor Commented:
Line 23
 $postDetails['post_name'] .

Open in new window


index.php
<div id='posts-page'>    
<div class='wrapper'>
<div class='table'>
    
    <div class='row header blue'>
      <div class='cell'>
        Name
      </div>
      <div class='cell'>
        Description
      </div>
      <div class='cell'>
        Date
      </div>
      <div class='cell'>
        View 
      </div>
    </div>
<?php 
if(isset($postDetails)){     
foreach($postDetails as $post){ 
echo "<div class='row'>" . "<div class='cell'>" .
        $postDetails['post_name'] .
      "</div>" .
      "<div class='cell'>" .
        $postDetails['description'] .
      "</div>" .
      "<div class='cell'>" .
        $postDetails['post_date'] .
      "</div>" .
      "<div class='cell'>".
        "<a class='link-button' href='". URL . "/Posts/id/" .$postDetails['post_id']. "'>View Post<a>".
      "</div></div>";
  }
} else {
    header("Location:".URL."user/login");
}
?>
  </div>
  </div>
</div>

Open in new window

Julian HansenCommented:
You are using the array $postDetails instead of the post ($post) variable
foreach($postDetails as $post){ 
echo "<div class='row'>" . "<div class='cell'>" .
        $post['post_name'] .
      "</div>" .
      "<div class='cell'>" .
        $post['description'] .
      "</div>" .
      "<div class='cell'>" .
        $post['post_date'] .
      "</div>" .
      "<div class='cell'>".
        "<a class='link-button' href='". URL . "/Posts/id/" .$post['post_id']. "'>View Post<a>".
      "</div></div>";
  }

Open in new window

Mike MooreAuthor Commented:
yeah thats what the problem is the, When I use $post instead of $postDetails I get that error saying illegal string
Julian HansenCommented:
Please use $post and post the error message - you can't use $postDetails it makes no sense - providing $postDetails has been filled correctly

Before the foreach can you do this
echo "<pre>" . print_r($postDetails, true) . "</pre>";

Open in new window

and post the results
Mike MooreAuthor Commented:
yes this is what I get :

Screen-Shot-2017-11-28-at-15.01.46.png
Julian HansenCommented:
Ok we are going around in circles here

$postDetails is supposed to be an array of records - that is a single record.

What happend to the getPosts() function that is suppoed to return an array of rows?

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Mike MooreAuthor Commented:
thats in the controller :

I think I may have fixed it.
Mike MooreAuthor Commented:
yes it works, it was because I used :

$postDetails = $stmt->fetch(PDO::FETCH_ASSOC);

Instead of :

$postDetails = $stmt->fetchall(PDO::FETCH_ASSOC);

As I now understand Fetch and Fetchall are very different.

Thank you very much for your help, I would never have got to this conclusion without it.
Mike MooreAuthor Commented:
Thanks a bunch
Julian HansenCommented:
You are most welcome.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
PHP

From novice to tech pro — start learning today.