I have been looking hard for a solution on this and i can't solve it so i will ask my question here and hopefully it will help others searching for the same question.
I have a Class for an items based on "Armor" for example.
class Armor {
public $name, $defense, $durability;
function __construct($name, $defense, $durability){
$this->name = $name;
$this->defense = $defense;
$this->durability = $durability;
}
}
Then i create a bunch of objects like this:
$heavy_armor = new Armor("Heavy Armor", 250, 50);
Then in a totally different file and use, I have another class for Magic Items which will create a brand new Magic Item but the base i want to use is the already existing heavy armor. However, there is a catch. I am already extending a different MagicArmors class because i will be creating a lot of these new classes and i want the common properties that will be the same across all Magic Armors to stay in one place - the MagicArmors class. Example:
class MagicHeavyArmor extends MagicArmors {
public function __construct() {
$this->name = "Magic Heavy Armor";
// so here i want to point to the Defense and Durability of the $heavy_armor object
$this->defense = 625; // Ideally i want $this->defense = $heavy_armor->defense * 2.5
$this->durability = 87.5; // Same here - I want $this->durability = $heavy_armor->durability * 1.75
}
So how do I make this work? The reason is that i will be creating a huge load of these and i want all of them to be able to looking for their base properties from other objects. And if i need to change a value of a property, to ease my life later.
The constructor should take the previous armor as a parameter, and can refer to its properties.
public function __construct($armor) {
$this->name = "Magic " . $armor->name;
$this->defense = $armor->defense * 2;
$this->durability = $armor->durability * 1.75;
}
Then you create the object like this:
new MagicHeavyArmor($heavy_armor);
Related
I'm relatively new to PHP (and to programming in general). Keep that in mind.
Here's the basic scenario. I am building a web-based mini-university.
There are two key classes: Lesson and LessonSeries.
Lesson has a Series property, which stores a singular LessonSeries if that lesson belongs to a series. It may not.
LessonSeries has a Lessons property, which will hold an array of Lesson objects..
Both classes have an 'Id' property, which is an integer and will be unique to their class. My database ensures there will not be two Lessons or two LessonSeries with the same Id.
In numerous pages throughout my website, Lesson objects are iterated and (if they have a series), they will display the series they belong to, and sometimes use some of the other LessonSeries methods and properties.
So here's the issue: When a Lesson is pulled from the Db, it constructs a LessonSeries to correspond to it. But I don't want there to exist 20 instances of the same LessonSeries. Certain methods trigger a db query that only needs to be executed once per LessonSeries. This could lead to exponentially more db activity than necessary if there were 20 instances of essentially the same series.
I'm new to programming patterns, but here's what I think I need:
I want a registry of unique LessonSeries.
I want each unique LessonSeries to be shared by all lessons that
belong to it.
Ideally, I want this functionality to be within the LessonSeries class, without having to have a second manager class, if it's at all possible.
Basically, what I want is that, whenever a LessonSeries is constructed, the registry will be checked first for the existence of an the id. If so, what is returned is a reference to the existing LessonSeries. If it DOESN'T exist in the registry, then it is created as usual.
With this said, I have no idea how to make this happen in PHP.
Edit:
It was pointed out in the comments that I need to demonstrate I've attempted to solve the problem for this to be a good question, of sorts, on SO. But that's exactly my problem.
I considered doing something like this:
class LessonSeries {
private static $registry;
public $Id;
public $SeriesName;
public $ImagePath;
public $Description;
private $index;
private $lessons;
private $lessonCount;
public function __construct($seriesName, $imagePath=null, $Id=null, $description = null) {
if(isset(self::$registry[$id])){
return self::$registry[$Id];
}else{
$this->SeriesName = $seriesName;
$this->ImagePath = $imagePath;
$this->Id = $Id;
$this->Description = $description;
self::$registry[$Id] = $this;
}
}
However, I don't think this is how php constructors work. I think I know how to do what I want in Python (using subclasses and such), but PHP doesn't have subclasses. And that's where I'm struggling.
Here's what you can do.
Create a factory object. Move all creation/caching logic there.
Pass that factory to newly created lessons and series so that they could delegate creation to it.
In your page code, strat by creating a factory.
Try to avoid statics if you can. For instance, your code will be easier to unit test if it's real objects.
Here's a pseudo-code demonstrating it:
class LessonsFactory {
private $lessons = [];
private $series = [];
public getLesson($id) {
if (isset($this->lessons[$id])) {
return $this->lessons[$id];
}
$lesson = $this->loadLesson($id);
$this->lessons[$id] = $lesson;
$this->getSeries($lesson[$id]);
}
private loadLesson($id) {
$data = ... // load lesson from db
return new Lesson($data, $this);
}
public getSeries($id) {
if (isset($this->series[$id])) {
return $this->series[$id];
}
$series = $this->loadSeries($id);
$this->series[$id] = $series;
}
private loadSeries($id) {
$data = ... // load series from db
return new LessonSeries($data, $this);
}
}
class Lesson {
private $factory;
public function __construct($data, LessonFactory $factory) {
$this->factory = $factory;
// + code to intialize object with $data
}
// this is how you get series from lessons
public function getSeries() {
return $this->factory->getSeries($this->seriesId);
}
}
// somewhere in your page controller code
$factory = new LessonsFactory();
$lesson = $factory->getLesson($_GET['lessond_id']);
I'm having issues accessing the Jobinfo class from my Deliveries. The problem is I need to be able to get the value of getQty from my child class and I also need to be able to get the qty_ship method using a property from the parent. How can I do this? It doesn't seem to work and quite confused over this... I'd like to be able to use methods from Parent->Child and Child->Parent dynamically.
class jobInfo
{
public $JOB_ID;
private $deliveries; // DELIVERIES CLASS
function __construct($job_id)
{
$this->JOB_ID=$job_id;
$this->deliveries = new Deliveries();
}
public function getQty()
{
return $this->query_s('job_sheet','*', 'job_id',$this->JOB_ID, 1, 'qty');
//returns a quantity from query method
}
}
class Deliveries extends jobInfo
{
function __construct(){}
public function qty_ship()
{
$qty = 0;
$SQL = mysql_query("SELECT * FROM deliveries WHERE jID='".parent::JOB_ID."'") or die(mysql_error());
$rows = mysql_num_rows($SQL);
if($rows>0)
{
while($data = mysql_fetch_array($SQL))
{
$qty += $data['qty_shipped'];
}
}
return $qty;
}
public function getTotalBO()
{
$qty = parent::getQty();
$totalship = $this->qty_ship();
$x = $qty-$totalship;
return $x;
}
}
$A = new Jobinfo(15);
You want $this->getQty() and $this->JOB_ID, but for completeness, consider:
Removing your empty no-parm constructor, as it cannot actually be used to instantiate the class unless it calls the parent contructor with a job id, which it cannot do since you expect the job id to be defined externally.
Making JOB_ID protected. For better encapsulation you might instead make it private and provide a getJobId() method.
Working on consistent class naming - jobInfo starts with lowercase and Deliveries starts with uppercase.
Working on consistent function naming - You have underscore-seperated functions mixed with camel-case functions.
Working on consistent spacing - You mix 1-character, 2-character and 0-character spacings throughout without much rhyme or reason.
Welcome to OOP and Good Luck!
If you extend from that class, then you should be using
$this->getQty();
$this will return the current object wich already include the parent with all the public and protected variables and methods.
You should only use :: when you call a static class
for exemple :
jobInfo::getQty();
You might also want to take a look at the naming convention.
http://framework.zend.com/manual/1.12/en/coding-standard.naming-conventions.html
I'm new to oop and was surprised to see that code that worked properly in procedural programming,
<?php
$number_of_floors = 5;
$stairs_per_floor= 10;
echo $total_stairs= $number_of_floors*$stairs_per_floor;
?>
Lead to an error when included inside of a class:
<?php
// Class
class Building {
// Object variables/properties
public $number_of_floors = 5; // These buildings have 5 floors
public $stairs_per_floor= 10;
public $total_stairs= $number_of_floors*$stairs_per_floor;
private $color;
// Class constructor
public function __construct($paint) {
$this->color = $paint;
}
public function describe() {
printf('This building has %d floors. It is %s in color.',
$this->number_of_floors,
$this->color
);
}
}
// Build a building and paint it red
$bldgA = new Building('red');
// Tell us how many floors these buildings have, and their painted color
$bldgA->describe();
?>
If you remove
public $total_stairs= $number_of_floors*$stairs_per_floor;
Everything works.
Are you not allowed to write arithmetic expressions inside of a class if they are outside of a function? What type of code that interprets correctly in procedural programming will cause an error when included in a class (outside of a function)?
You can not do the operation at the time of defining them. Instead you should add this to your constructor and do:
$this->total_stairs = $this->number_of_floors * $this->stairs_per_floor;
Furthermore I advise you to use the generally accepted coding standards of PHP which would mean, not to use underscores in variable names.
public $totalStairs;
public $numberOfFloors;
public $stairsPerFloor;
Even more important is the choice of meaningful and readable variables names. So $bldgA should be:
$buildingA
you can't assign value by mathematical calculation while defining variable. Calculate value in constructor.
<?php
// Class
class Building {
// Object variables/properties
public $number_of_floors = 5; // These buildings have 5 floors
public $stairs_per_floor= 10;
public $total_stairs=0;
private $color;
// Class constructor
public function __construct($paint) {
$this->color = $paint;
$this->total_stairs = $number_of_floors*$stairs_per_floor;
}
public function describe() {
printf('This building has %d floors. It is %s in color.',
$this->number_of_floors,
$this->color
);
}
}
// Build a building and paint it red
$bldgA = new Building('red');
// Tell us how many floors these buildings have, and their painted color
$bldgA->describe();
?>
To answer your question: within an object oriented design, all code belongs inside a method; either a "special" method like the constructor, within a regular method, or (in languages other than PHP) in getter/setter methods (http://php.net/manual/en/language.oop5.properties.php has a way of implementing those in PHP).
Outside of methods, you're allowed to declare properties or attributes - but you should think of that really as a declaration, not a way of executing logic. The fact you can assign literals during the declaration is purely a convenience.
Don't assign expressions as variable. Do it in the Constructor:
$this->total_stairs = $this->number_of_floors * $this->stairs_per_floor;
or do
public $total_stairs= $this->number_of_floors * $this->stairs_per_floor;
you have to use the instance variables, without $this-> they are interpreted as local variables.
$this->total_stairs = $this->number_of_floors*$this->stairs_per_floor;
also, move those to the constructor, as they are (look like) instance specific.
You can't execute expressions when define properties, even with constants and heredocs.
Calculate them in __construct method.
Just starting out with OOP in PHP and in general. From what I have been reading so far the two seem to be synonyms. Is this the case, and if not, practically speaking when people refer to objects and classes do they generally use the terms interchangeably?
Typically one would refer to an object as an instance of a class.
So you have some class Employee.
class Employee {
var $name;
function get_name ( ) { return $this->name; }
function set_name ($new_name) { $this->name = $new_name; }
}
And you declare an instance of it like:
$assistant = new Employee();
Employee is a class. $assistant is an object, that is an instance of the Employee class.
So to answer your question - a class is not an object. You create an object when you instantiate a class.
objects and classes do they generally use the terms interchangeably?
No. As in other OOP languages, classes are like the blueprints for something, say a house. Objects are the actual house after it's built. Very different things indeed.
// blueprint
class House
{
public $color;
public function __construct($color = 'red')
{
$this->color = $color;
}
}
// make red house
$redHouse = new House();
// make blue house
$blueHouse = new House('blue');
// Now we have two different houses (objects) made from the same blueprint (class)
They're certainly not synonymous, and if you've been reading that, it's time to change the book! :-)
Classes are types, while objects are instances.
A simple example is an integer. "Integer" denotes the type, but an integer $x is an instance of that type. In PHP there isn't a strong type system, so this may not be entirely apparent, but I hope you get the idea. Similarly, array is a type, but $v = array(); creates an instance (called $v) of array type.
With classes, you cannot just say $y = MyClass(); as you do with arrays, instead, you have to use new: $y = new MyClass();.
A class is a definition of an object. An object is an instance of a class. For example:
class Parser {
public function parse() {}
}
...is a class. You might say "The Parser class can be used to parse text."
$p = new Parser;
Now, $p is an object. It is an instance of the Parser class.
This is particularly important with the static keyword. Static methods and members belong to classes, not objects.
class Parser {
public static $type;
public $text;
}
$p1 = new Parser;
$p2 = new Parser;
$p1::$type = 'php';
$p1->text = 'sometext';
$p2->text = 'someothertext';
echo $p2::$type; //echos "php"
echo $p1->text; //echos "sometext"
You can remove the in php from your question and it is still the same thing.
A class defines an Object for example
class Person {
}
is a class that defines an person object.
The distinction get more important when you start creating class methods and object methods
class Person {
function hair_color(color) {
hair_color = color;
}
}
is an object method in php you could do something like this
austin = new Person()
austin -> hair_color("brown")
now you can have something like
class Person {
total = 0;
static function total_in_class() {
total++;
}
}
now that is an class method it affects all objects of the same class
that way
austin = new Person();
austin -> total_in_class
tom = new Person();
echo tom->total
Now if my php isn't that rusty then it should echo 1. That is because all objects in the class are affected
In ruby it would look as follows
class Person
def hair_color(color)
hair_color = color;
end
def self.total_in_class()
total+=1
end
end
Similar and same concepts apply
I have a custom class object in PHP named product:
final class product
{
public $id;
public $Name;
public $ProductType;
public $Category;
public $Description;
public $ProductCode;
}
When passing an object of this class to my Data Access Layer I need to cast the object passed into a type of the product class so I can speak to the properties within that function. Since type casting in PHP works only with basic types what is the best solution to cast that passed object?
final class productDAL
{
public function GetItem($id)
{
$mySqlConnection = mysql_connect('localhost', 'username', 'password');
if (!$mySqlConnection) { trigger_error('Cannot connect to MySql Server!'); return; }
mysql_select_db('databaseName');
$rs = mysql_query("SELECT * FROM tblproduct WHERE ID='$id';");
$returnObject = mysql_fetch_object($rs, 'product');
return $returnObject;
}
public function SaveItem($objectToSave, $newProduct = false)
{
$productObject = new product();
$productObject = $objectToSave;
echo($objectToSave->Name);
$objectToSave->ID;
}
}
Right now I am creating a new object cast as a type of product and then setting it equal to the object passed to the function. Is there a better way of accomplishing this task? Am I going about the wrong way?
EDITED FOR CLARITY - ADD FULL PRODCUTDAL CLASS
You don't need to cast the object, you can just use it as if it was a product.
$name = $objectToSave->Name;
I´m not sure what you are trying to achieve, but if $objectToSave is already of class product:
You can simply call $objectToSave->SaveItem() (assuming SaveItem() is part of the product class) and access it´s properties in the function like $this->Name, etc.;
In your code $productObject and $objectToSave will hold a reference to the same object.
Type casts in PHP are done like this:
$converted = (type) $from;
Note, that this won't work if the object types are not compatible (if for example $form happens to be a string or object of mismatching type).
But usual solution (called Active Record pattern, present for example in Zend Framework) is to have a base class for a database item called Row. Individual items (for example the class product from your sample) then inherit from this class.
Typical ZF scenario:
$table = new Product_Table();
$product = $table->find($productId); // load the product with $productId from DB
$product->someProperty = $newPropertyValue;
$product->Save(); // UPDATE the database
Which is IMO much better than your solution.
EDIT:
You can't cast between two unrelated objects, it is not possible.
If you want to use the DAL like this, skip the "product" object and go for simple associative array. You can enumerate over its members with foreach, unlike object's properties (you could use reflection, but that's overkill).
My recommendation: Go for the Active Record pattern (it is easy to implement with magic methods). It will save you a lot of trouble.
Currently, you are creating a new Product, then discarding it immediately (as its reference is replaced by $objectToSave.) You will need to copy its properties one by one, I regret.
foreach (get_object_vars($objectToSave) as $key => $value)
{
$product->$key = $value;
}
(If the properties of $objectToSave are private, you will need to a expose a method to_array() that calls get_object_vars($this).)