I have simple parent class named fruits and several childs, like this:
Parent:
class fruits
{
public function __construct()
{
$this->render();
}
protected function render()
{
}
}
Child (banana):
class banana extends fruits
{
public $identity = 'banana';
protected function render()
{
echo 'This text will describe all about banana';
}
}
Child (orange):
class orange extends fruits
{
public $identity = 'orange';
protected function render()
{
echo 'This text will describe all about orange';
}
}
The question is, how to load/display child content from parent based on child variable ($identity) ? I want to use the following code:
$fruits = new fruits( 'banana' );
and will get the child content that generated from render function, for example will get like this:
This text will describe all about banana etc...
Thank you and really appreciate for any help...
--- EDITED ----------------------------
As suggested by @quack, how about this code?
Parent:
<?php
class Fruit
{
public function __construct( $identity = '' )
{
$this->load( $identity );
}
private function load( $identity )
{
foreach ( $this->getChild() as $child ) {
$reflectionClass = new ReflectionClass( $child );
$childData = $reflectionClass->getDefaultProperties();
if ( isset( $childData['identity'] ) && ( $childData['identity'] === $identity ) ) {
$component = new $child();
if ( method_exists( $component, 'render' ) ) {
$component->render();
}
break;
}
}
}
private function getChild()
{
$children = array();
foreach ( get_declared_classes() as $class ) {
if ( is_subclass_of( $class, 'Fruit' ) ) {
$children[] = $class;
}
}
return $children;
}
}
Child
<?php
class Banana extends Fruit
{
public $identity = 'banana';
protected function render()
{
print "Hi, I'am $this->identity";
}
}
And will load with this:
new Fruit('banana');
CodePudding user response:
Maybe you need a static function to create the instance like this:
class fruits
{
public static function getInstance($bananaOrange)
{
if ($bananaOrange == 'banana')
{
return new banana();
}
elseif ($bananaOrange == 'orange')
{
return new orange();
}
}
public function __construct()
{
$this->render();
}
protected function render()
{
}
}
$fruits = fruits::getInstance('banana');
CodePudding user response:
I was quite interested by your question and not satisfied with the factories using some if, else or switch tests so I finally got this solution, which also handles parameters to give to the constructors of each fruit. Each fruit has some different properties so I used an array but you could also use variable length of constructor parameters. I prefered the array solution because the order isn't important. You could improve it to check the parameter names and rise an error if they don't exist for that fruit.
<?php
class FruitException extends Exception { }
abstract class Fruit {
protected $fruitName = '';
static public function create($fruitName, $fruitProperties)
{
// The class name may not be written correctly.
$fruitName = ucwords($fruitName);
if (!class_exists($fruitName) || get_parent_class($fruitName) !== self::class) {
throw new FruitException("The fruit type '$fruitName' does not exist!");
}
return new $fruitName($fruitProperties);
}
abstract protected function display();
}
class Banana extends Fruit {
public const DEFAULT_LENGTH = 15; // cm
public const MAX_LENGTH = 30; // cm
private $length = self::DEFAULT_LENGTH;
public function __construct($properties)
{
$this->fruitName = 'Banana';
extract($properties); // Create variables from array keys and values.
if (isset($length)) {
if (!is_numeric($length) || $length < 0 || $length > self::MAX_LENGTH) {
throw new FruitException('The given banana length is not valid!');
}
$this->length = $length;
}
}
public function display()
{
print "I'm a banana and my length is $this->length cm\n";
}
}
class Orange extends Fruit {
public const DEFAULT_DIAMETER = 9; // cm
public const MAX_DIAMETER = 15; // cm
private $diameter = 9; //
public function __construct($properties)
{
$this->fruitName = 'Orange';
extract($properties); // Create variables from array keys and values.
if (isset($diameter)) {
if (!is_numeric($diameter) || $diameter < 0 || $diameter > self::MAX_DIAMETER) {
throw new FruitException('The given orange diameter is not valid!');
}
$this->diameter = $diameter;
}
}
public function display()
{
print "I'm an orange and my diameter is $this->diameter cm\n";
}
}
class Car {
// This is not a kind of fruit!
}
$fruits = [
'Orange' => ['diameter' => 8],
'banana' => ['length' => 17], // missing capital first letter.
'car' => ['color' => 'blue', 'manufacturer' => 'Ferrari'], // Existing class definition but not a fruit.
'Apple' => ['color' => 'green', 'diameter' => 7.5], // Inexisting fruit type.
];
foreach ($fruits as $fruitName => $fruitProperties) {
try {
$fruitInstance = Fruit::create($fruitName, $fruitProperties);
$fruitInstance->display();
}
catch (FruitException $e) {
fprintf(STDERR, "ERROR: %s\n", $e->getMessage());
}
}
This will output the following:
I'm an orange and my diameter is 8 cm
I'm a banana and my length is 17 cm
and this in the standard error stream:
ERROR: The fruit type 'Car' does not exist!
ERROR: The fruit type 'Apple' does not exist!
