(sorry for my bad English)
I'm creating a website where users can post something and other users can leave a comment on that post.
I learned from a video from HowCode (youtube) that if you want to display the comments on multiple pages, it is usefull to create 1 page that selects the comments and on all the other pages I can 'include' those comments.
The problem is that it now only displays one comment while I have multiple comments on some posts.
Does anyone know how to solve this?
On the page where I select the comments:
class Comment {
public static function displayComments($postId) {
$comments = DB::query('SELECT * FROM table WHERE post_id = $postId');
foreach($comments as $comment) {
$commentDisplay = $comment['comment'].' ~ by a user';
return $commentDisplay;
}
}
}
On the page where I display the comments:
$showComments = Comment::displayComments($_GET['postid']);
echo $showComments;
Yes, this is happening because you are returning on the first iteration of the foreach.
When you return it immediately exists the method displayComments() and therefore only displays the first one.
What you want to do is return all the comments by adding them to an array and turning the array:
class Comment {
public static function displayComments($postId) {
$comments = DB::query('SELECT * FROM table WHERE post_id = $postId');
foreach($comments as $comment) {
$commentDisplay[] = $comment['comment'].' ~ by a user';
}
return commentDisplay;
}
}
Then when you call it:
$showComments = Comment::displayComments($_GET['postid']); // this is an array
echo array_values($showComments);
Or use foreach on $showComments if you want to print it in a different way
Instead of returning a single comment, you should push all comments related to the post in an array and return it.
class Comment {
public static function displayComments($postId) {
$comments = DB::query('SELECT * FROM table WHERE post_id = $postId');
$commentDisplay = array();
foreach($comments as $comment) {
$commentDisplay[] = $comment['comment'].' ~ by a user';
}
return $commentDisplay;
}
}
And then loop through the returned array to display all comments.
$showComments = Comment::displayComments($_GET['postid']);
foreach($showComments as $comment){
// display comment
// echo $comment . '<br />';
}
Sidenote: Learn about prepared statement because right now your query is susceptible to SQL injection attack. Also see how you can prevent SQL injection in PHP.
You can do it with dependency injection...you should as below more flex more elastic,also data mapping support to user class
class Comment {
public function displayComments($postId) {
$data = DB::query('SELECT * FROM table WHERE post_id = $postId')->fetch(PDO::FETCH_ASSOC)
return $data;
}
}
class User
{
private $comment;
private $name;
private $id;
function __construct(Comment $comment,int $postid)
{
$this->comment=$comment;
$this->id=$postid;
}
function comment()
{
return $this->comment->displayComments($this->id)['comment'];
function name()
{
$this->comment->displayComments($this->id)['name'];
}
}
$comment=new Comment ();
$user=new User($comment,$postid);
echo $user->comment() .':'.$user->name();
Related
I am writing a class that I would like to be able to call later and have it return an array of values but it is returning only one.
I would like to be able to use my class like this. If I specify one user id new Blog([10]) then it shouldn't return an array but only one instance. If I specify more than one user id then it should return an array of items.
I am trying to create something similar to how Laravel works where you can say $posts = Posts::all(); or $posts = Post::where('id', 10)->first(); and in the first one it would return an array of all posts and in second it would return only one.
Example usage:
// Get one user's blog
$blog = new Blog([10]); // specify user ids
echo $blog->user->name; // Jane Smith
echo $blog->posts->title; // How to draw
echo $blog->posts->body; // In this post, I will teach you...
echo $blog->posts->created; // 2018-12-01
echo $blog->theme; // light/dark/other
echo $blog->is_awesome; // no
// Get blogs for users - 10, 20, 30
$blogs = new Blog([10, 20, 30]); // specify user ids
foreach ($blogs as $blog) {
echo $blog->user->name; // John Doe
echo $blog->posts->title; // 10 ways to live
echo $blog->posts->body; // Hello, in this post I will..
echo $blog->posts->created; // 2018-12-31
echo $blog->theme; // light/dark/other
echo $blog->is_awesome; // yes
}
My class
Class Blog
{
public $users;
public $posts;
public $comments;
public $theme;
public $is_awesome;
function __construct($users)
{
$this->users = new stdClass();
$this->users->id = $users; // array of ids
foreach ($this->users as $user) {
$this->user->name = self::getUsername($user->id) // John
$this->posts = self::getPosts($user->id); // array of posts
$this->comments = self::getComments($user->id); // array of comments
$this->theme = self::getTheme($user->id); // light/dark/other
if ($this->theme == 'dark') {
$this->is_awesome = 'yes';
} else {
$this->is_awesome = 'no';
}
}
}
}
I understand why you're trying to do, and since you asked another way, here it is. One of the approaches is to write a static method to retrieve yours blogs:
class Blog {
public static function fetchBlogsByIds() {
// [...]
}
// [...]
}
Then you call the method this way:
$blogs = Blog::fetchBlogsByIds(ids) {
$blogs = array();
foreach($ids as $id) {
$blogs[] = new Blog($id); // appending a new Blog entry
}
return $blogs;
}
You could also write a collection class named, for example BlogCollection and provide to it a constructor relying on an array of ids.
class BlogCollection {
// Builds the collection by the ids
function __construct(ids) {
// [...] similar implementation as fetchBlogsByIds() above
}
// [...]
}
Then you can retrieve your blogs this way:
blogs = new BlogCollection([10, 11]);
If you want to use foreach with your custom collection, you can make it implementing Traversable or Iterator.
Hey guys I have a question and I still consider myself pretty new at coding, so forgive me if I come off foolish.
I am studying in school as of now and we have a project to build a full stack recreation of craigslist. Any who the problem I am having deals with PHP. I have created an account page with text areas. I would like to echo out the user's information on their so the user can see what he put on and update as he likes. Since my navbar is included on every page, I added the code:
if(isset($_SESSION['logged_in_user'])){
var_dump($_SESSION['logged_in_user']);
$user = $_SESSION['logged_in_user'];
var_dump($user);
}
on my account page I figured I can echo it out as
<?= $attributes['first_name']?> within the placeholders. But I keep getting:
Undefined index: first_name
Also when I var_dump($user) I get an protected $attributes array.
In My Auth class is where I first defined $user as such:
public static function attempt($attemptedUsername, $attemptedPassword) {
$user = User::findByUserName($attemptedUsername);
if ($user == null) {
return false;
}
$validPassword = password_verify($attemptedPassword,$user->password);
if ($validPassword == true) {
$_SESSION['logged_in_user'] = $user;
}
return false;
}
and my findByUserName function is in the user class. the code is:
public static function findByUserName($user_name){
// Get connection to the database
self::dbConnect();
$stmt = self::$dbc->prepare('SELECT * FROM users WHERE user_name = :user_name');
$stmt->bindValue(':user_name', $user_name , PDO::PARAM_STR);
//execute gets its own line, t or false
$stmt->execute();
$result=$stmt->fetch(PDO::FETCH_ASSOC);
// #TODO: Create select statement using prepared statements
// #TODO: Store the result in a variable named $result
// The following code will set the attributes on the calling object based on the result variable's contents
$instance = null;
if ($result) {
$instance = new static($result);
}
return $instance;
}
Your problem seems to be with not being able to access the variable $user outside of the static method attempt() this can be fixed by declaring the variable globally at the beginning of the method attempt() like this:
public static function attempt($attemptedUsername, $attemptedPassword) {
global $user;
$user = User::findByUserName($attemptedUsername);
if ($user == null) {
return false;
}
$validPassword = password_verify($attemptedPassword,$user->password);
if ($validPassword == true) {
$_SESSION['logged_in_user'] = $user;
}
return false;
}
More information can be found on this in the PHP documentation here.
I can't put real code here because is very long and will be hard to
explain.
I have users table in database and I have data table in database too.
So, to get the user data I'll pass user_id as parameter. Like this:
public function get_user_data($user_id) {
}
But. I can only get 1 data per "request". (Keep reading)
public function user_data() {
$getUsers = $this->db->get('users');
foreach($getUsers->result_array() as $user)
{
$data = $this->get_user_data($user->ID);
var_dump($data); // Only return 1 data;
}
}
But, I guess that have an way to "bypass" this but I don't know. I'm having trouble thinking.
As I said, I want to "bypass" this, and be able to send multiple user IDs, my real function do not accept that by default and can't be changed.
Thanks in advance!
replace
foreach($getUsers->result_array() as $user)
{
$data = $this->get_user_data($user->ID);
var_dump($data); // Only return 1 data;
}
to this
foreach($getUsers->result_array() as $user)
{
$data[] = $this->get_user_data($user->ID);
}
var_dump($data);
If you are aiming at sending more data to the function, you always need to make signature change of your function as one of the below :
function get_user_data() {
$args = func_get_args();
/** now you can access these as $args[0], $args[1] **/
}
Or
function get_user_data(...$user_ids) {
/** now you can access these as $user_ids[0], $user_ids[1] **/
}
// Only higher version of PHP
But I am not sure how you will handle returning data.
EDIT: Yes, then in the function, you can collect data in array and return an array of data from function.
If you can change in your function from where to where_in I think you will get an easy solution.
public function get_user_data($user_ids)
{
// your db code
$this->db->where_in('ID',$user_ids); //replace where with where_in
}
public function user_data()
{
$getUsers = $this->db->get('users');
foreach($getUsers->result_array() as $user)
{
$user_ids[] = $user->ID;
}
$this->get_user_data($user_ids);
}
I am trying to move over to OOP and have a User class with the following:
<?php
class User {
public function __construct($sdb)
{
$this->sdb = $sdb;
}
// and some other methods
}
?>
I call the class using:
$Users = new User($sdb);
I can then use
echo $Users->GetAdminName($AdminId);
And it shows me the results just fine.
But then further down the page, I call another method named ShowUsers that returns all users from the table.
$results = $Users->ShowUsers();
foreach($results as $result)
{
echo my results ....
}
The issue I am having is that the ShowUsers() method is only showing 1 record instead of all the records.
BUT, if I comment out the top method call
//echo $Users->GetAdminName($AdminId);
It shows all the records and not just one.
I am not sure what I am doing wrong here. Any help is greatly appreciated.
function GetAdminName($AdminId)
{
$this->sdb->limit_val="0,1";
$results = $this->sdb->dbSelect("administrators",array("AdminId","FirstName","LastName"),array("AdminId"=>$AdminId));
foreach($results as $result)
{
$AdminName = $result["FirstName"]." ".$result["LastName"];
}
return $AdminName;
}
function ShowUsers($Status = '')
{
$this->sdb->order_by_column="LastName";
$tables=array("administrators", "roles");
$joins=array("administrators.RoleId=roles.RoleId");
$join_condition=array("INNER JOIN");
$columns=array("AdminId", "AdminEmail", "FirstName", "LastName", "Status", "RoleName");
if($Status)
{
$whereCondition=array("Status"=>"$Status");
}
return $this->sdb->dbSelectJoin($tables,$joins,$join_condition,$columns,$whereCondition);
}
I have users' table users, where I store information like post_count and so on. I want to have ~50 badges and it is going to be even more than that in future.
So, I want to have a page where member of website could go and take the badge, not automatically give him it like in SO. And after he clicks a button called smth like "Take 'Made 10 posts' badge" the system checks if he has posted 10 posts and doesn't have this badge already, and if it's ok, give him the badge and insert into the new table the badge's id and user_id that member couldn't take it twice.
But I have so many badges, so do I really need to put so many if's to check for all badges? What would be your suggestion on this? How can I make it more optimal if it's even possible?
Thank you.
optimal would be IMHO the the following:
have an object for the user with functions that return user specific attributes/metrics that you initialise with the proper user id (you probably wanna make this a singleton/static for some elements...):
<?
class User {
public function initUser($id) {
/* initialise the user. maby load all metrics now, or if they
are intensive on demand when the functions are called.
you can cache them in a class variable*/
}
public function getPostCount() {
// return number of posts
}
public function getRegisterDate() {
// return register date
}
public function getNumberOfLogins() {
// return the number of logins the user has made over time
}
}
?>
have a badge object that is initialised with an id/key and loads dependencies from your database:
<?
class Badge {
protected $dependencies = array();
public function initBadge($id) {
$this->loadDependencies($id);
}
protected function loadDependencies() {
// load data from mysql and store it into dependencies like so:
$dependencies = array(array(
'value' => 300,
'type' => 'PostCount',
'compare => 'greater',
),...);
$this->dependencies = $dependencies;
}
public function getDependencies() {
return $this->dependencies;
}
}
?>
then you could have a class that controls the awarding of batches (you can also do it inside user...)
and checks dependencies and prints failed dependencies etc...
<?
class BadgeAwarder {
protected $badge = null;
protected $user = null;
public function awardBadge($userid,$badge) {
if(is_null($this->badge)) {
$this->badge = new Badge; // or something else for strange freaky badges, passed by $badge
}
$this->badge->initBadge($badge);
if(is_null($this->user)) {
$this->user = new User;
$this->user->initUser($userid);
}
$allowed = $this->checkDependencies();
if($allowed === true) {
// grant badge, print congratulations
} else if(is_array($failed)) {
// sorry, you failed tu full fill thef ollowing dependencies: print_r($failed);
} else {
echo "error?";
}
}
protected function checkDependencies() {
$failed = array();
foreach($this->badge->getDependencies() as $depdency) {
$value = call_user_func(array($this->badge, 'get'.$depdency['type']));
if(!$this->compare($value,$depdency['value'],$dependency['compare'])) {
$failed[] = $dependency;
}
}
if(count($failed) > 0) {
return $failed;
} else {
return true;
}
}
protected function compare($val1,$val2,$operator) {
if($operator == 'greater') {
return ($val1 > $val2);
}
}
}
?>
you can extend to this class if you have very custom batches that require weird calculations.
hope i brought you on the right track.
untested andp robably full of syntax errors.
welcome to the world of object oriented programming. still wanna do this?
Maybe throw the information into a table and check against that? If it's based on the number of posts, have fields for badge_name and post_count and check that way?