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
Related
I have a website written with procedural PHP and now I'm re-coding it in (at least partly) OOP style so that I can start learning it. It is a simple page displaying several learning courses (title, description, etc) from a database. The admin can add or delete anything so it has to be dynamic.
At first I ran a single line of code:
$courses=$pdo->run("SELECT id,title,description FROM courses WHERE status=1 ORDER BY id")->fetchAll(PDO::FETCH_CLASS, 'Course');
$cmax = count($courses);
and echoed e.g. $courses[3]->description but I felt like I'm doing nothing else but pretending OOP while just using a multidimensional array. Again, it would be ok for the purpose of the website but my question is, in order to get used to OOP logic, can I do it like this: I'm generating a dropdown menu with only the titles and IDs from the database, and after either is clicked, only then I'm creating the object (to get the description, date, teacher, whatever):
$obj = new Course($pdo,$userSelectedID);
echo $obj->getTitle($pdo);
$obj->showDetails($pdo); // etc
The class:
class Course {
protected $id;
protected $title;
protected $description;
public function __construct($pdo,$id) {
$this->id=$id;
}
public function getTitle($pdo) {
$this->title=$pdo->run("SELECT title FROM courses WHERE id=?",[$this->id])->fetchColumn();
return $this->title;
}
public function getDescription($pdo) {
$this->description=$pdo->run("SELECT description FROM courses WHERE id=?",[$this->id])->fetchColumn();
return $this->description;
}
public function showDetails($pdo) {
echo "<h3>".$this->getTitle($pdo)."</h3>".$this->getDescription($pdo);
}
}
Is this a wrong approach? Is it ok to run sql commands inside a class? Especially when I already had to use some DB data to generate the dropdown menu. I hope my question makes sense.
PS: I've heard passing the PDO object every time isn't the best practice but I'm not there yet to do it with the recommended instance(?)
A good approach is to create model classes for every table you have in your database.
A model contains private attributes corresponding yo your table columns, with associated getters and setters.
For your Course table:
class Course {
protected $id;
protected $title;
protected $description;
public function getId() {
return $this->id;
}
public function setId($id) {
$this->id = $id;
}
public function getTitle() {
return $this->title;
}
public function setTitle($title) {
$this->title = $title;
}
public function getDescription() {
return $this->description;
}
public function setDescription($description) {
$this->description = $description;
}
}
Then, you have the concept of Data Access Objects. You can create an abstract class that will be extended by your data access objects:
abstract class AbstractDAO {
protected $pdo;
public function __construct($pdo)
{
$this->pdo = $pdo;
}
abstract public function find($id);
abstract public function findAll();
abstract protected function buildModel($attributes);
}
For your course table:
class CourseDAO extends AbstractDAO {
public function find($id) {
$statement = $this->pdo->prepare("SELECT * FROM courses WHERE id = :id");
$statement->execute(array(':id' => $id));
$result = $statement->fetch(PDO::FETCH_ASSOC);
return $this->buildModel($result);
}
public function findAll() {
$statement = $this->pdo->prepare("SELECT * FROM courses");
$statement->execute();
$results = $statement->fetchAll(PDO::FETCH_ASSOC);
$courses = [];
foreach($results as $row)
$courses[] = $this->buildModel($row);
return $courses;
}
public function findByTitle($title)
{
...
}
public function create(Course $course)
{
...
}
protected function buildModel($attributes) {
$course = new Course();
$course->setId($attributes['id']);
$course->setTitle($attributes['title']);
$course->setDescription($attributes['description']);
return $course;
}
}
Modern frameworks do this automatically, but I think it is good to understand how it works before using powerful tools like Eloquent or Doctrine
Hi I'd like to ask if my implementation of a model class is at least correct to OOP style.
I declare it as an abstract so that it can only be used on extending the
class
sample code :
<?php
/**
* class model handles dbconfig and some common query transaction
* i declare it as an abstract so that it can only be used on extending the
*class
*/
abstract class Model
{
//db config
protected $sHost = "localhost";
protected $sUser = "root";
protected $sPass = " ";
protected $sDb = "test";
protected $sEngine = "MySQL";
protected $conn ;
//constructor
public function __construct()
{
$this->conn = new mysqli($this->sHost, $this->sUser, $this->sPass , $this->sDb);
}
protected function db_query_list($sSql){
if ($resultset = $this->conn->query($sSql)) {
if ($resultset->num_rows > 0) {
$data = array();
while( $row = $resultset->fetch_assoc() ) {
$data[] = array_change_key_case($row);
}
}else {
$data = false;
}
} else {
$data = false;
}
$resultset->close();
return $data;
}
protected function execute_query($SQL) {
$run = $this->conn->query($this->sEngine);
return $run;
}
}
Then on implementation i extend model in CustomerModel
<?php
require "Model.php";
class CustomerModel extends Model
{
public function __construct()
{
parent::__construct();
}
public function getAllCustomer()
{
$sSql = "SELECT *
FROM t_classification_header
";
return $this->db_query_list($sSql);
}
}
Notice that i use parent::__construct();.
I'm new in OOP any help would be my pleasure
Any comments and suggestions are welcome .
Thank you
1) Create separated class, maybe even singleton, for actually interaction with DB and pass it as argument of model constructor. Why? If you work with 5 customers than your code will create 5 connections to DB. (Dependency Injection / composition)
2) Don't do return $this->db_query_list($sSql);. Wrap results into some CustomersList and/or wrap each row into Customer.
3) I would create class Customers($DB) with methods all(), byId($id), etc. instead of CustomerModel. Seriously, we're not a photo agency and do not work with models. Yes, the class is a data model, but why bother with this code? For us, this is a concrete Customer, with specific data and behavior.
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.
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.
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 :(