I am a learner, I have a class db to help me connect and fetch results in mySQL.
$set = $db->get_row("SELECT * FROM users");
echo $set->name;
this way i use echo results outside a class.
Now i have created another class name user and it has this function
public function name() {
global $db;
$set = $db->get_row("SELECT * FROM users");
$this->name = $set->name;
}
after initializing the class user, when i try to echo $user->name i dont get expected results.
Note i have declared above var $name; in class user
I'm pretty concerned by several things I see here
The method name name() is terribly uncommunicative as to what the method is supposed to do. Remember, methods are actions - try to give them some sort of verb in their name.
Usage of global in a class (or even usage of global period) when you should be using aggregation or composition.
You don't show any execution examples, but I can only assume you never actually call User::name(), which is why your test is failing
Here's some code that addresses these concerns.
<?php
class DB
{
/* Your methods and stuff here */
}
class User
{
protected $db;
protected $name;
public function __construct( DB $db )
{
$this->db = $db;
}
public function getName()
{
if ( is_null( $this->name ) )
{
$set = $this->db->get_row( "SELECT * FROM users" );
$this->name = $set->name;
}
return $this->name;
}
}
$db = new DB();
$user = new User( $db );
echo $user->getName();
class DB
{
public function get_row($q)
{
# do query and store in object
return $object;
}
}
class User
{
public $name;
public function __construct()
{
$this->name();
}
public function name() {
global $db;
$set = $db->get_row("SELECT * FROM users");
echo "<pre>".print_r($set)."</pre>"; # make sure $set is returning what you expected.
$this->name = $set->name;
}
}
$db = new DB();
$user = new User();
echo $user->name;
I am very much sorry, i figured out that problem was on my part, i was using cookies and had two cookies set which were giving problems :(
Related
I have a PHP class that extends another class, but i only get the MySQL to work at the extended class not the first class. Anyone knows what the problem can be? I can't seem to figure it out at all right now :S
# Vote class.
class vote {
public $newsID;
private $db;
# Construct.
public function __construct() {
global $_database;
$this->db = $_database;
}
# Vote Up.
public function voteUp() {
return '';
}
public function voteScore($newsID) {
$vote = mysqli_fetch_object($this->db->query("SELECT * FROM ".PREFIX."news WHERE newsID='".$newsID."' LIMIT 1"))->vote;
return '<span class="BigFontSize" style="position:absolute; top: 37px;right: 14px;">'.$vote.'</span>';
}
public function voteDown() {
return '';
}
}
# News class.
class news extends vote {
public $countNews;
private $db;
# Construct.
public function __construct() {
global $_database;
$this->db = $_database;
}
# Count News.
public function countNews() {
return $this->db->query("SELECT * FROM ".PREFIX."news ORDER BY date DESC")->num_rows;
}
# Print the news.
public function GetNews() {
$newsArray = array();
$sql = $this->db->query("SELECT * FROM ".PREFIX."news ORDER BY date DESC");
while ($rad = $sql->fetch_array()) {
$newsArray[] = array('headline' => $rad['headline'], 'content' => $rad['content'], 'date' => $rad['date'], 'poster' => $rad['userID'], 'published' => $rad['published'], 'intern' => $rad['intern'], 'newsID' => $rad['newsID']);
}
return $newsArray;
}
}
It's the vote class that doesnt have the functioning database. Am i missing something?
Just remove the constructor from news and it will inherit the vote constructor. You will have to make the $db class var in vote protected instead of private and remove it from news altogether. There is absolutely no gain from each having it's own reference to the same $database since it will still be the same instance.
While we are on the subject of better code design, do not use GLOBAL in PHP, EVER.
PHP global in functions
http://smartik.ws/2014/07/do-not-use-php-global-variables-never/
Instead of accessing a global connection object in the constructor as you are doing, use Dependency Injection, ie pass it into the constructor as a parameter:
# Vote class.
class vote {
public $newsID;
private $db;
# Construct.
public function __construct($_database)
{
$this->db = $_database;
}
}
$_database = new Database();
$news = new News($_database);
In the long term you will find that this is the prefered practice for very good reasons. It will also mark you out as a professional not an amateur. http://tutorials.jenkov.com/dependency-injection/index.html
I'm trying to use a session var and call the logged in users details through object on the page. The user has already logged in through my login object. Here are my objects:
class User {
public $userdata = array();
//instantiate The User Class
public function User(){ }
public function set($var, $value) {
$this->userdata[$var] = $value;
}
public function get($var) {
if(isset($this->userdata[$var]))
{
return $this->userdata[$var];
}
return NULL;
}
function __destruct() {
if($this->userdata){
$this->userdata;
}
}
}
class UserService {
private $db;
private $fields;
public $user;
private $session;
public function __construct() {
$this->db = new Database();
}
// //get current user by ID
public function getCurrentUser($session) {
$this->session = $session;
$query = sprintf("SELECT * FROM User WHERE idUser=%s",
$this->db->GetSQLValueString($this->session, "int"));
$result = $this->db->query($query);
if($result && $this->db->num_rows($result) > 0){
//create new user
$user = new User();
$row = $this->db->fetch_assoc($result);
//set as object
foreach($row as $key => $value) {
$user->set($key, $value);
break;
}
return $user;
//return $this->user;
}
return NULL;
}
}
On my page I've check my session var has a value which it does, so I call the object like so.
$um = new UserService();
$user = $um->getCurrentUser($_SESSION['MM_Username']);
echo $user->get('UserSurname');
however, I see no user surname on the page. I have checked with a none object query and I see a surname but as soon as its object is doesn't work.
I think the problem is here:
foreach($row as $key => $value) {
$user->set($key, $value);
break; // you should probably remove it
}
You should use unnecessary break and probably after setting for example id you stop setting another object properties (UserSurname, Name and so on).
In addition it's quite confusing that inside $_SESSION['MM_Username'] you store idUser and not UserName
Code review
Marcin Nabialek allready answered your question. That break in the foreach is. well. what is it doing there?
But, there are much more things broken in your code. So here is a code review:
Constrcutors
You obviously know what a constructor is. You use it in both classes. But, differently. why? You User class has a public function User() but your UserService has a public function __construct(). Pick one, and stick to it. And if you can choose, pick the correct one: __construct()
From the phpdoc:
As of PHP 5.3.3, methods with the same name as the last element of a namespaced class name will no longer be treated as constructor. This change doesn't affect non-namespaced classes.
So namespacing your User class will break your constructor. This may not be a problem now, but it smells. Simply use __construct(). It is the prefered and correct way to do it. We live today, and not in the past of php4- days :)
Code styling
Oh god, a lot of kittens died today!
Sometimes you have a bracket on a new line:
if (isset($this->userdata[$var]))
{
return $this->userdata[$var];
}
and sometimes you don't
if($this->userdata){
$this->userdata;
}
Again, pick something and stick to it. and if you want to save some kittens. stick to the standards: PSR-1 & PSR-2
Public atributes, jeuk
Your User class has a public var $attributes. So it is accessible from the outside world. But you also give us a get and set method. why?
A good rule is: public $var smells, protected $var should be used with caution and private $var is the good stuff.
What are your classes? and why don't you use __constructors?
If I look at the User class, I always look at the __constructor. The User class needs no variables. So I should be able to do something like this:
$me = new User();
$me->getName(); //who am I ?
This ofcourse doesn't work. A User without a name doesn't make sense. It always has one. So ask for it!
class User
{
public function __construct($name)
{
$this->name = $name;
}
}
$me = new User('jeroen');
$m->getName(); //I am jeroen :)
If you need something ask for it ;)
So:
public function __construct(Database $db)
Is the way to roll!
Don't make me read the database
Now, your get/set methods are tigthly coupled with your database. If you change the name of a column in the database. You can refractor your entire code to get('new_column_name'); . Sounds like fun!
Also, what does the method say me? Nothing, does it write easy? no
getName says what it does, it gets me the name.
get tels me i'm getting something. but what?
other questions rise: get('name') =?= get('Name')
It's ok for the User object to know what it has.
Summary
Ok, I outlined some things wrong in your code. Some concepts you should look into:
SOLID
PSR standards
Factory Pattern (this will help you with your UserService
Inversion Of Control
So, for the sake of the article, here is your code revamped. Note that I wrote it into this commentbox directly, so I could have missed some things and made some errors.
Changelist:
I cleaned up styling
I added some comments
removed _destruct() (it wasn't doing anything)
Used PDO instead of Databse class (no idea what you are using, but looks like a PDO wrapper)
changed some table names and the select query (never use * in a selct query. Onyl ask for that what you need)
used prepared statements
more flexibility
exceptions instead of returning null;
Your $DBH could be put into a singleton/factory to ease your $dbh creation: Database::getInstance(); or DatabaseFactory::getInstance()->createPDO(); or so. Long time since I wrote something like this
Usage:
$DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
$userRepository = new UserRepository($DBH);
$id = $_SESSION['MM_Username'];
try
{
$me = $userRepository->find($id);
}
catch( UserNotFoundException $e )
{
//user not found
}
print $me->getSurName();
User class:
class User
{
/**
* If the User persists in the DataBase
* $id holds it's db id
* #var int
*/
private $id;
/**
* #var String
*/
private $surName;
/**
* #var String
*/
private $firstName;
/**
* #param String $firstName
* #param String $surName
* #param int $dbId
*/
public function __construct($firstName, $surName, $dbId=null)
{
$this->id = $dbId;
$this->firstName = $surName;
$this->surName = $surName;
}
/**
* Does the user ecist in the DB?
* #return boolean
*/
public function hasId()
{
return $this->id !== null;
}
/**
* #return int
* #return null user doesn't persist in DB
*/
public function getId()
{
return $this->id;
}
/**
* Return the users full name
* The fullname consists of his FirstName and SurNAme
* #return String
*/
public function getName()
{
return $this->firstName . ' ' . $this->surName;
}
/**
* #return String
*/
public function getSurName()
{
return $this->surName;
}
/**
* #return String
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* Setters: we return $this to allow chaning
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
// ... other methods here. You can add extra swet stuff.
// for instance check or it is a valid firstName, or email or ...
//I removed your __destrouct, because wtf? it isn't doing anything at all
}
and your UserRepository:
/**
* The UserRepository queries the database and get's you your users
*/
class UserRepository
{
private $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
public function find($id)
{
$statement = $this->db->prepare('SELECT id,first_name,sur_name FROM users WHERE id = :id');
$statement->execute(array(
'id' => $id
));
if ( null === ($user = $statement->fetch(PDO::FETCH_ASSOC)) )
{
throw new UserNotFoundException();
}
return new User(
$user['first_name'],
$user['sur_name'],
$user['id']
);
}
}
and the exception:
class UserNotFoundException extends Exception();
I'm having a bit of trouble in designing my classes in php.
As you can see in my Code, i want to have one Class instance and having more classes as children which "talk" from one to another. im getting the logged user and get all his information stored to a variable. In my other Classes i recently need to get this UserData.
Any help and Ideas are welcome :)
class Factory
{
private $UserData;
public function Factory()
{
DB::connect();
$this->getLoggedUserData( $_SERVER['REMOTE_USER'] );
}
private function getLoggedUserData( $user )
{
$result = DB::query( "SELECT * FROM users WHERE user='$user' LIMIT 1" );
$this->UserData = $result->fetch_assoc();
}
public function getMyTasks()
{
// how to call that class, without instancing it over and over again
MyOtherClass -> getMyTasks();
}
}
class MyOtherClass
{
public function getMyTasks()
{
// how to access the "global" variable
$result = DB::query( "SELECT * FROM tasks WHERE userID=" . $UserData['userID'] . " LIMIT 1" );
// doSomething ($result);
}
}
class DB
{
private static $mysqli;
public static function connect()
{
$mysqli = new mysqli(MYSQL_SERVER, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DB);
if ($mysqli->connect_error) {
die('Connect Error (' . $mysqli->conect_errno . ')' . $mysqli->connect_error);
}
mysqli_set_charset($mysqli, 'utf8');
self::$mysqli = $mysqli;
}
public static function query( $query )
{
$result = self::$mysqli->query( $query );
if ( self::$mysqli->error ) {
error_log("QUERY ERROR: " . self::$mysqli->error);
error_log("QUERY: " . $query);
}
return $result;
}
}
$Factory = new Factory();
OK, here goes a simple trivial approach to your problem
Mind you, this is not complete. Gimme some feedback if this is closing in on what you'd expect
your classes changed a bit
<?php
class Factory {
private $UserData;
private $UserTask;
public function Factory() {
DB::connect();
$this->getLoggedUserData($_SERVER['REMOTE_USER']);
}
private function getLoggedUserData($user) {
$result = DB::query('SELECT * FROM users WHERE user="'.$user.'" LIMIT 1');
$this->UserData = $result->fetch_assoc();
}
public function getMyTasks() {
// how to call that class, without instancing it over and over again
if (!isset($this->UserTask)) $this->UserTask = new MyOtherClass($this->UserData);
return $this->UserTask->getMyTasks();
}
}
class MyOtherClass {
private $UserData;
public function __construct($userData) {
$this->userData = $userData;
}
public function getMyTasks() {
// how to access the "global" variable
$task = DB::query('SELECT * FROM tasks WHERE userID='.$this->UserData['userID'].' LIMIT 1');
return $this->performTask($task);
}
public function performTask($task) {/* doSomething(); */}
}
// usage is not complete, waiting for some extra input
$factory = new Factory();
$taskResults = $factory->getMyTasks();
Any input on how to improve this is very welcome
edit following comments
Let's take a look at how you can solve the problem of having to share instances between different "apps" in your code
the singleton approach: an instance is created on the first call, all subsequent calls are passed the single instance
the registry pattern: an object created at the start of the script picks up all initialized requirements and stores them. If any "app" needs the basic set of services (it's not standalone), then pass the registry object to it's initializer/constructor.
I hope I understood your comments well enough, if not feel free to ask and correct me
Hard to say what would be best for you when i dont know more about the scale of your application etc.
Anyway the simplest way is something like this:
$otherClass = new MyOtherClass();
$Factory = new Factory($otherClass);
Class Factory
class Factory
{
private $UserData;
private someClass;
public function Factory(&$someClass)
{
$this->someClass = $someClass;
DB::connect();
$this->getLoggedUserData( $_SERVER['REMOTE_USER'] );
}
...
Usage
$this->someClass->getMyTasks();
But in case you only want access to the methods/variables of the parent, then yes extend the class.
Hmmm, so how is this done?
I have a class
class Themes extends Access
{
public $theme_name;
public $theme_by;
public $theme_by_email;
public $theme_by_website;
public $theme_description;
public $theme_thumb;
public $theme_source;
public $theme_uploaded_on;
public function __construct()
{
parent::__construct();
//$this->get_theme();
}
public function get_theme()
{
$sql = "SELECT *
FROM `user_themes`
WHERE `user_id` = " . $this->session->get('user_id');
if($this->db->row_count($sql))
{
$result = $this->db->fetch_row_assoc($sql);
$this->$theme_name = $result['theme_name'];
$theme_by = $result['theme_by'];
$theme_by_email = $result['theme_by_email'];
$theme_by_website = $result['theme_by_website'];
$theme_description = $result['theme_description'];
$theme_source = $result['theme_source'];
$theme_uploaded_on = $result['theme_uploaded_on'];
}else{
die('no results');
}
}
}
How can I access these variables and their contents outside of the class?
in my PHP page I have
$theme = new Themes();
I tried to access my variable using
$theme->them_name but I get an undefined error
but don't really know how I can access the variable...
With your current setup, all you have to do is call:
$theme->theme_name;
$theme->theme_by;
etc
However it is generally not good practice to make instance variables public, rather make them private and make mutator methods.
An example would be:
private $theme_name;
public function getThemeName(){
return $this->theme_name;
}
public function setThemeName($theme){
$this->theme_name = $theme;
}
$this->theme_name = $result['theme_name'];
$this->theme_by = $result['theme_by'];
Note on $this prepended.
After that you can access the data using $theme->theme_name etc
here is my sample class to why i want to nest.
include("class.db.php");
class Cart {
function getProducts() {
//this is how i do it now.
//enter code here`but i dont want to redeclare for every method in this class.
//how can i declare it in one location to be able to use the same variable in every method?
$db = new mysqlDB;
$query = $db->query("select something from a table");
return $query
}
}
Take advantage of properties.
class Cart {
private $db;
public function __construct($db) {
$this->$db = $db;
}
public function getProducts() {
$query = $this->db->query( . . .);
return $query;
}
}
You'll create the database object outside of your class (loose coupling FTW).
$db = new MysqlDb(. . .);
$cart = new Cart($db);
Isolate the common code to each method/function into another private internal method/function.
If you need to have it run once automatically for the object when it's created, this is what __construct is for.
You could have something like this
<?php
class cart
{
protected $database;
function __construct()
{
$this->database = new mysqlDB;
}
function getProducts()
{
$this->database->query("SELECT * FROM...");
}
}
?>
__construct is the function that is called when you instantiate a class.