I am new to PHP OOP and would like to try to nest several classes within another class, to latter call them like so:
$sql = new SQL();
$sql->Head()->Description($_SESSION['page']);
//OR
$sql->Head()->Keywords($_SESSION['page'])
//OR
$sql->Body()->Clients($_SESSION['client'])
//ETC
$query = $sql->Run(); // equivalent to mysql_query("...");
As you can guess, I run into some problems and ended with this poor code:
<?php
require( $_SERVER['DOCUMENT_ROOT'] . '/#some_db_directory/database.php');
//This file contains $db['host'], $db['user'], etc...
class SQL {
public $sql;
public function __construct() {
global $db;
}
public class Head() {
public function Description($page) {
return "SELECT * FROM `$db['database']`.`desciption` WHERE `page` = '$page'";
}
public function Keywords($page) {
return "SELECT * FROM `$db['database']`.`keywords` WHERE `page` = '$page'";
}
}
}
$sql = new SQL();
echo $sql->Head()->Description('home'); //For testing
Is it possible to nest classes in PHP?
If so, how is it done?
What you are trying to do is called Encapsulation. Try google search on PHP encapsulation to learn more.
Here is a code example from http://www.weberdev.com/get_example.php3?ExampleID=4060,
<?php
class App {
private static $_user;
public function User( ) {
if( $this->_user == null ) {
$this->_user = new User();
}
return $this->_user;
}
}
class User {
private $_name;
public function __construct() {
$this->_name = "Joseph Crawford Jr.";
}
public function GetName() {
return $this->_name;
}
}
$app = new App();
echo $app->User()->GetName();
?>
I'm assuming that database.php is a database class. In that case you could do something like this.
head.php
Class Head{
private $_db;
private $_dbName;
public function __construct($db, $dbName){
$this->_db = $db;
$this->_dbName = $dbName;
}
public function Description($page) {
$results = $this->_db->query("SELECT `text` FROM `$this->_dbName`.`description` WHERE `page` = '$page'");
return '<meta name="description" content="' . $results['text'] . '">';
}
public function Keywords($page) {
$results = $this->_db->query("SELECT * FROM `$this->_dbName`.`keywords` WHERE `page` = '$page'");
$keywords = array();
foreach($results as $result){
array_push($keywords, $result['word']);
}
return '<meta name="keywords" content="' . implode(',', $keywords) . '">';
}
}
sql.php
require( $_SERVER['DOCUMENT_ROOT'] . '/#some_db_directory/database.php');
// Require head class file
require( $_SERVER['DOCUMENT_ROOT'] . '/#some_db_directory/head.php');
Class SQL{
public $Head;
public function __construct($dbName){
global $db;
$this->Head = new Head($db, $dbName);
}
}
You would then pass the name of the database into the SQL class (which propogates through to the Head class).
// Require the sql class file
require( $_SERVER['DOCUMENT_ROOT'] . '/#some_db_directory/sql.php');
$sql = new SQL('mydatabase');
echo $sql->Head->Description('home');
Again note that your database class might not return results the way I'm using them here. You will have to modify this to work with your particular database class.
Try it like this
<?php
require( $_SERVER['DOCUMENT_ROOT'] . '/#some_db_directory/database.php');
//This file contains $db['host'], $db['user'], etc...
class SQL {
public $sql;
private $_head;
public function __construct() {
global $db;
$_head = new HeadClass();
}
public function Head() {
return $this->_head;
}
}
class HeadClass { // Class cannot have a public access modifier
public function Description($page) {
return "SELECT * FROM `" . $db['database'] . "`.`desciption` WHERE page = $page";
}
public function Keywords($page) {
return "SELECT * FROM `" . $db['database'] . "`.`keywords` WHERE page = $page";
}
}
$sql = new SQL();
echo $sql->Head()->Description('home.html');
?>
I am moving the class declaration outside the class and creating an instance of the class with in SQL. This is then made available via the Head() function.
Note: For body you will need to create a separate class and use a reference in the SQL class to it like I have done for head.
Related
I have a WordPress plugin with 2 extended classes, Area and Loc, each of which has a helper function Get. Each class has static variables for table_name. If I call Loc::Get($id) directly, it works as expected. If, however, I call Loc::Get($id) from Area, it uses the table_name from Area rather than Location.
Can anyone explain how to correct this? Regards,
class _Base {
function Get($id) {
$instance = new self();
$sql = "SELECT * FROM " . static::$table_name . " WHERE id=$id";
return $sql;
}
}
class Area extends _Base {
static $table_name = "Area";
function getLoc($id) {
$sql = Loc::Get($id);
return $sql;
}
}
class Loc extends _Base {
static $table_name = "Loc";
}
$sql = Area::Get(1); // -> "SELECT * FROM **Area** WHERE id=1"
$sql = Loc::Get(1); // -> "SELECT * FROM **Loc** WHERE id=1"
$sql = $area->GetLoc(1); // -> "SELECT * FROM **Area** WHERE id=1"
I think your problem is you run NON static method Get as static. When i changed it then all start work fine. Try my code below:
<?php
class _Base {
static function Get($id) {
$instance = new self();
$sql = "SELECT * FROM " . static::$table_name . " WHERE id=$id";
return $sql;
}
}
class Area extends _Base {
static $table_name = "Area";
function getLoc($id) {
return Loc::Get($id);
}
}
class Loc extends _Base {
static $table_name = "Loc";
}
var_dump(Area::Get(1));
var_dump(Loc::Get(1));
var_dump((new Area)->getLoc(1));
First
Function Get is not static by definition
function Get($id) {
Second
$area is not defined
Let's say that we have a MVC pattern. If we have something like this:
blog_controller.php
<?php
class c_blog{
private $model;
public function __construct(){
include( [model_path] );
$this->model = new m_blog();
}
public function post_list(){
return $this->model->get_list();
}
}
blog_model.php
<?php
class m_blog{
public function get_list(){
global $db;
$sel = $db->query("...");
$sel = $db->fetch_array($sel);
return $sel;
}
}
index.php
<?php
$db = new database();
include "blog_controller.php";
$blog = new c_blog();
var_dump( $blog->post_list() );
?>
Why the above example isn't good, and a lot of devs want OOP like class m_blog extends database{} ?
I want only the advantages and disavantages, because I use this "worry" method on over 15 sites with a high traffic and I don't have problems (some of that websites work with multidatabases and it's very easy to change global $db, to global $db_five.)
Thanks!
Why not use inheritance?
<?php
class model {
private $db;
public function __construct() {
$this->initDb();
}
private function initDb();
}
class c_blog {
private $model;
public function __construct(){
include( [model_path] );
$this->model = new m_blog();
}
public function post_list(){
return $this->model->get_list();
}
}
class m_blog extends model{
public function __construct() {
parent::__construct;
}
public function get_list(){
$sel = $this->db->query("...");
$sel = $this->db->fetch_array($sel);
return $sel;
}
}
I have just a little progress in practicing. Most of my code works but I am not sure if I do things the right way?
Please, can you tell me if I do mistakes and correct me.
First, I create autoload functions:
function autoload_models($model) {
if (file_exists(MODELS_PATH . $model . '.php')) {
require_once MODELS_PATH . $model . '.php';
return true;
} else {
return false;
}
}
spl_autoload_register('autoload_models');
function autoload_controllers($controller) {
if (file_exists(CONTROLLERS_PATH . $controller . '.php')) {
require_once CONTROLLERS_PATH . $controller . '.php';
return true;
} else {
return false;
}
}
spl_autoload_register('autoload_controllers');
I have a class like this:
class Category {
public $db;
public $rows;
public $id;
public function build_category() {
global $db;
global $rows;
$db = new Database();
$db->query("SELECT * from categories");
$rows = $db->resultset();
}
public function category_items() {
global $db;
global $rows;
global $id;
$db = new Database();
$db->query("SELECT * from posts WHERE category_id = '$id'");
$rows = $db->resultset();
}
}
I extend with another class (still have some issues here. Nothing prints):
class Category_Items extends Category {
public $db;
public $rows;
public $id;
public function display_category_items() {
// Call the parent class function
parent::category_items();
global $rows;
global $id;
// Check if the page parameter is integer
if (ctype_digit($_GET['id'])) {
$id = $_GET['id'];
} else {
print "Illegal category page parameter";
}
foreach ($rows as $row) {
print "test";
print $row['post_title']; // This does not work yet. Nothing prints
}
}
}
Class for building a menu with categories (Everything works here):
class Categories_Menu extends Category {
public $db;
public $rows;
public function build_category_menu() {
parent::build_category();
global $rows;
foreach ($rows as $row) {
require VIEWS_PATH . 'categories/categories_menu.php';
}
}
}
And finally instances:
$category_menu = new Categories_Menu();
$category_menu->build_category_menu();
$category_items = new Category_Items();
$category_items->display_category_items();
Thank you for your time and help!
Where do the global variables come from?
Anyway, you should get rid of them.
I guess your rows var does not get changed, after any interaction. Using globals also will not be relevant in extending classes.
Your public properties and globals mentioned, does no interact each other. Thus, the object members seems to be totally useless.
What I would suggest in simple schems would be
class Model {
protected $_db;
public function __construct(Database $db) {
$this->_db = $db;
}
}
class Category extends Model {
public $_rows;
public $_id;
public function build_category() {
$this->_db->query("SELECT * from categories");
$this->_rows = $this->_db->resultset();
}
public function category_items() {
$this->_db->query("SELECT * from posts WHERE category_id = '{$this->_id}'");
$this->_rows = $this->_db->resultset(); // here you will overwrite $_rows ?
}
class Categories_Menu extends Category {
public $_rows;
public function build_category_menu() {
$this->build_category();
foreach ($this->_rows as $row) {
require VIEWS_PATH . 'categories/categories_menu.php';
}
}
}
class Category_Items extends Category {
public $_rows;
public $_id;
public function display_category_items() {
if (ctype_digit($_GET['id'])) { // just intval it, or use is_int?
$this->_id = $_GET['id'];
} else {
print "Illegal category page parameter";
}
// You assign value to $_id, then call the function that requires it
$this->category_items();
foreach ($this->_rows as $row) {
print "test";
print $row['post_title'];
}
}
}
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 :(