PHP - Classes inheritance and combining with another class - php

I want to write something inheriting classes in the structure of the 3 levels. Abstract example of such.
couriers_list -> courier -> courier_name
class couriers_list {
// I'm searches courier_name of the database and reference to the class of courier_name found in database
public function show_courier() {
return $this->xxx();
}
}
class courier extends couriers_list {
// Function for the courier
// Probably it would interface or abstraction
}
class courier_name extends courier {
// Methods of accurately courier eg. Calculation associated with the delivery
// Here each courier would separate file php eg. ups.php
public function xxx() {
return 'xxx';
}
}
$k = new couriers_list();
echo $k->xxx();
I don't think I can extends courier to courier_list, because I don't have access to methods descendants. From the outside, I would like to refer only to the class couriers_list, and she has to take the info from the database and take methods resulting from courier_name.
How to put this problem to organize object?

Your class Courrier should be an interface since it used to define the way your Courrier should be used
interface CourrierInterface
{
const CLASS_NAME = __CLASS__;
public function getName();
public function getPrices();
public function XXX();
public function calculateDelivery($from, $to);
/** these are just example add here all the method that define a courier */
}
Then for each courrier you have, you should implements de CourrierInterface
class UpsCourrier implements CourrierInterface
{
private $name;
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function XXX()
{
// do something
}
}
I don't think the CourrierList should know about the database but just need to contains all the courrier object you have.
Here i used the project
https://github.com/yvoyer/collection
in order to manage the collection (TypedCollection class), it allows to control the type of class you want in your collection (here courrierInterface), but you can use an array if you want it much simple.
I create two XXX method, one for a specific courrier (since its a list) and another for apply to all the list. I don't know your need so take what you need
class CourrierList
{
/**
* TypedCollection
*/
private $courrierList;
public function __construct()
{
// i prefer to use a collection class that valid my type instead of an array, your choice!
$this->courrierList = new TypedCollection("CourrierInterface");
}
public function addCourrier(CourrierInterface $courrier)
{
$this->courrierList->add($courrier);
}
public function removeCourrier(CourrierInterface $courrier)
{
$this->courrierList->removeElement($courrier);
}
protected function getCourrierByName($courrierName)
{
$closure = function (CourrierInterface $courrier) use ($courrierName) {
return $courrier->getName() == $courrierName;
};
return $this->courrierList->filter($closure)->toArray();
}
// since its a list, you must be able to select your courrier to execute the XXX() method
public function XXXByName($name)
{
$courrier = $this->getCourrierByName($name);
$courrier->XXX();
}
// if you prefer to apply to all your courrierList just do
public function XXX()
{
foreach ($this->courrierList as $courrier) {
$courrier->XXX();
}
}
}
Since i don't think the CourrierList should know about the database, you can create a factory that does fetch the data in your db and create all the courriers. Here dbConnection is your choice, pdo etc... i just made an example, find the good way to make query.
class CourrierListFactory
{
private $dbConnection;
public function __construct(DbConnection $connection)
{
$this->dbConnection = $connection;
}
public function createCourrierList()
{
$results = $this->dbConnection->query(".....")->getResults();
$courrierList = new CourrierList();
foreach ($results as $result) {
$courrier = null;
switch ($result['name']) {
case 'ups':
$courrier = new UpsCourrier();
break;
case 'fedex':
$courrier = new FedexCourrier();
break;
/** and so on ... */
default:
// maybe throw exception if courier is not handle
}
$courrier->setName("....");
/** prepare your object here */
// add the courrier to the list
$courrierList->addCourrier($courrier);
}
return $courrierList;
}
}
Finally its how you use all of this, first create a dbCOnnection, then build your list, then you can acces
$dbConnection = new DbConnection();
// the factory allows you to separate the database from the List class and may be to generate
// your list in an other way later (using apis, etc...)
$factory = new CourrierListFactory($dbConnection);
$courrierList = $factory->createCourrierList();
$courrierList->XXX(); // apply to all
$courrierList->XXXByName("ups"); // apply just to ups courrier

Related

PHP cast object in simple ORM

I would like to make a simple ORM in PHP for standard CRUD interaction with my db, I also want make it work in php5 for legacy compatibility.
I've written some classes to do this and it works, but not completely as I would.
This is the idea. I have an abstrac class called ModelBase which has a property (tableName) and some metods like select, insert, update and delete, plus has an abstract method, getData, that will be implemented by the classes that will be implement ModelBase and should return object of correct type.
So, for example, I could have a class Users which implements ModelBase and one another class UserData which is the model with the property.
Here is the code:
abstract class ModelBase{
private $tableName;
public function __construct($tableName) {
$this->tableName = $tableName;
}
public function select{
// make select query to db and retreive data
// ...
$resData = [];
while($dataRow = mysqli_fetch_array($res, MYSQLI_ASSOC)) {
$resData[] = $this->getObjectData($dataRow); // implemented in child class
}
return $resData;
}
public function insert(){ /* ... */}
public function update(){ /* ... */}
public function delete(){ /* ... */}
abstract function getObjectData($data); // maps the results
}
class UserData {
public $id;
public $name;
public $surname;
public $email;
// other fields
public function __construct() {}
}
class User implements ModelBase {
private $tableName = 'users';
public function __construct() {
parent::__construct($this->tableName);
}
public function getObjectData($dataRow) {
$o = new UserData ();
// mapping dataRow to object fields
$o->id = $dataRow['ID'];
// ....
return $o;
}
}
So I use my classes in this way:
$users = new Users();
$u = users->select();
$firstUser = $u[0]; // I get my user if exists
In $firstUser I'll get my object with property and correct data but I would like to have that also my IDE (vsCode in this case) would recognize the object type in order to suggest the correct properties. So if I write $firstUser-> I would like to see field suggestions (id, name, surname, ...) from UserData and for other xyzData classes as well.
What I should do to improve my classes in order to see property suggestions when I use my objects, also in php5?
Solution for PHP 8, tested on PHPStorm.
<?php
class Base {
/**
* #return static[]
*/
public function select() : array {
return [new self];
}
public function selectFirst() : static {
return $this->select()[0];
}
}
class User extends Base {
public ?string $userName = null;
}
#detects the current class via () : static
(new User)->selectFirst()->userName;
#detects the current class via #return static[]
(new User)->select()[0]->userName;
In line solution for PHP 5, define the variable directly with this comment
/** #var $a User */
$a->userName;
There is no benefit of supporting old PHP 5. You lose so mutch clean code and modern approach when supporting old php versions.
But when you have to, then go with the inline solution.
Not tested and not so clean for PHP 5:
class User extends Base {
public ?string $userName = null;
/**
* #return User[]
*/
public function select() : array {
return parent::select();
}
}

PHP understanding OOP Concepts

I'm just new to PHP and i cant understand why the code bellow doesn't works.
If i do the same code but not using class things works just fine.
<?php
class DAL
{
public $Conn;
public function __construct()
{
global $Conn;
$Conn = new PDO("mysql:host=localhost;dbname=football", "root","");
$Conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
}
}
class BLL
{
public function GetSeason()
{
$conn = new DAL();
$result = $conn->Conn->query("Select * from season");
print_r($result->fetchAll());
}
}
?>
If I understand right, DAL is Data access layer and BLL is Business logic layer.
Both usually represented not by one class or object, but rather contain objects that represent different levels of abstractions in your application.
Data access layer consists of Data access objects and Business logic layer consists of Business Objects. So you should have one Data access object for each Business Object. In general Data access objects should share one interface (i.e. should extend same Data access object abstract class):
abstract class DataAccessObject
{
protected $conn;
public function __construct($conn)
{
$this->conn = $conn;
}
abstract public function all();
abstract public function get($id);
abstract public function save($object);
abstract public function delete($object);
}
So with your example, the code can look like the following. Here is your season DAO:
final class SeasonDataAccessObject extends DataAccessObject
{
public function all()
{
$query = $this->conn->query('SELECT * FROM `season`');
$seasons = [];
foreach ($query->fetchAll() as $result) {
$seasons[] = $this->hydrate(new Season, $result);
}
return $seasons;
}
public function get($id)
{
// Execute select statement and hydrate result into
// Season Business Object.
// Should return instancs of Season.
}
public function save($season)
{
return null === $season->id
? $this->insert($season)
: $this->update($season);
}
public function delete($season)
{
// Execute delete statement.
}
private function hydrate(Season $season, $result)
{
// Fill $season attributes with values from $result.
}
private function update(Season $season)
{
// Execute update statement.
}
private function insert(Season $season) {
// Execute insert statement.
}
}
And here goes your season Business object:
abstract class BusinessObject
{
private $id;
public function setId($id)
{
$this->id = $id;
return $this;
}
public function getId()
{
return $this->id;
}
}
final class Season extends BusinessObject
{
// Some properties of Season and setters and getters for them.
}
And finally, you can use it all in your program:
$conn = new PDO('mysql:host=localhost;dbname=football', 'root', '');
$conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
$seasonDAO = new SeasonDataAccessObject($conn);
$seasons = $seasonDAO->all();
print_r($seasons);
I guess you got the lingo from "Microsoft Application Architecture Guide" or somewhere else in the Microsoft world (your attributes naming also speaks for it:)). In the modern world, I guess Data access object is Repository and the Business object is Entity.
This is pretty advanced stuff that concerns not even OOP, but Object-oriented design. So you must understand OOP first, because as it was mentioned in the comments, now your code is procedural. Wrapping code in class doesn't make code object-oriented (at the end of the day, this is Object-oriented programming, not Class-oriented programming;))
So, please start with some tutorial on the subject of OOP in PHP (i.e. this one), and gradually you will get better in this.
Done!!!!!
Thank you guys so much!!!
class DAL
{
public $Conn;
public function __construct()
{
$this->Conn = new PDO("mysql:host=localhost;dbname=football",
"root","");
$this->Conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
}
}
class BLL
{
public function GetSeason()
{
$conn = new DAL();
$result = $conn->Conn->query("Select * from season");
print_r($result->fetchAll());
}
}

Detect changes to Array in PHP

I've been using __set magic method with protected properties to monitor changes so that my classes know if they have something to save. Is there any way to monitor an array type property for changes? I understand that normally you access the array via a reference and functions like array_push won't trigger the __set method, they'll use a reference to the array.
What I want is basically this:
class Skill{ public $Player, $Name, $Level;}
class Player {
protected $Name, /*Other properties*/, $Skills /*Array*/
}
I then do tracking on all of the properties in Player to tell me if the persistence needs updated. (Skill would also have this function, but this shows the basic example). Also, I want to force them to remain synchronized (it's a bidirectional relationship).
Is there any way to do this that allows it to behave like an array (don't want to go through making a class just to synchronize those if I don't have to).
You could extend ArrayObject and proxy append:
class Skills extends ArrayObject
{
public function append($value)
{
// track changes
parent::append($value);
}
}
You could look into something like runkit_function_redifine(), but is it really too cumbersome to make helper methods for what you want? e.g.
class Player
{
private $skills = array();
protected function addSkill($skill)
{
// Do something.
//
$this->skills[] = $skill;
}
}
Or even a wrapper for an array to make it cleaner:
class FancyArray
{
private $content = array();
public function add($value)
{
// Do something.
//
$this->content[] = $value;
}
public function remove($value){ /* blah */ }
public function getContent(){ return $this->content; }
}
class Player
{
protected $skills;
public function __construct()
{
$this->skills = new FancyArray();
$this->skills->add("Ninjitsu");
}
}

Changing object right before PHP serialises it

I have the following class tree:
class A /* Base class */
{
private/protected/public $state
}
class B extends A /* Auto generated class, not to be modified */
{
private $v
public function getV() { return $this->v; }
public function setV($val) { $this->v = $val; }
}
class C extends B { /* Custom code */ }
There is only one class A. There are multiple classes like class B, and all of those classes will have a subclass like C. Class B gets auto-generated and should not be modified.
I am storing objects of type(s) C in the session. What I want to do is to store some state information in every instance, just before PHP gets it serialised, and that will do something with it when it's unserialised. I want all this to be implemented in class A.
Considering, I need to use either __sleep() or Serializable interface. Using __sleep is out of the question, because of what the PHP manual says:
It is not possible for __sleep() to return names of private properties in parent classes. Doing this will result in an E_NOTICE level error. Instead you may use the Serializable interface.
Meaning that if I sleep an instance of class C, I'll loose the private variables declared in B. So I want to use Serializable, but for some reason, I simply can't get it to do what I want.
In essence, I would like the object to be serialised just as if I didn't implement any serialisation stuff myself, I just want to add information to $state right before it happens. I've tried covering all data with ReflectionObject->getProperties(), but I can't seem to find the right way to fetch and set the private values in class B to be serialised and unserialised.
How do I do this?
You can do this using the Reflection classes. You'll have to get the properties of the class itself and each of it's parent classes. Getting and setting the property values can be done using ReflectionProperty's getValue and setValue methods, combined with setAccessible to get access to private and protected properties. Combining those, I came up with the following code:
<?php
class A implements Serializable /* Base class */
{
protected $state;
public function serialize()
{
$this->state = "something";
return serialize($this->_getState());
}
public function unserialize($data)
{
$this->_setState(unserialize($data));
}
protected function _getState()
{
$reflClass = new ReflectionClass(get_class($this));
$values = array();
while ($reflClass != null)
{
foreach ($reflClass->getProperties() as $property)
{
if ($property->getDeclaringClass() == $reflClass)
{
$property->setAccessible(true);
$values[] = array($reflClass->getName(), $property->getName(), $property->getValue($this));
}
}
$reflClass = $reflClass->getParentClass();
}
return $values;
}
protected function _setState($values)
{
foreach ($values as $_)
{
list($className, $propertyName, $propertyValue) = $_;
$property = new ReflectionProperty($className, $propertyName);
$property->setAccessible(true);
$property->setValue($this, $propertyValue);
}
}
}
class B extends A /* Auto generated class, not to be modified */
{
private $v;
public function getV() { return $this->v; }
public function setV($val) { $this->v = $val; }
}
class C extends B { /* Custom code */ }
$instance = new C();
$instance->setV("value");
$s = serialize($instance);
$instance2 = unserialize($s);
var_dump($instance, $instance2);
Which seems to do what you want.

Wrap/Reuse/Clone a PHP object (code optimization)

Imagine two classes which share almost the same exact methods and properties, both extending a parent class, but the differences are minimal.
class fields {
public function __construct() {
global $id;
$this->id = $id++;
}
}
class input extends fields {
public function __construct() {
parent::__construct();
}
public function draw() {
echo '<input>';
}
}
class textarea extends fields {
public function __construct() {
parent::__construct();
}
public function draw() {
echo '<textarea>';
}
}
I'm thinking it would be more efficient to rewrite the textarea class in this psuedo-code fashion:
class textarea extends fields {
public function __construct() {
$this = new input(); // <<------
}
public function draw() {
echo '<textarea>';
}
}
Basically, I'm unsure how this would best be done so that the class acts like the class from the first example.
In essence, I would like to do the following using OOP, but be able to use the object as it can be in the first example above (be able to call the possibly overloaded methods, have different properties, etc.):
function a() {echo '123';}
function b() {a();}
I have just copied the entire class and modify a few lines, but I feel it is wasteful.
Final Answer
Thanks to those people, here is the combined answer with example calls:
abstract class fields {
private static $masterid = 0;
public function __construct() {
$this->id = self::$masterid++;
}
}
class input extends fields {
public $data;
public function __construct($new = '') {
parent::__construct();
if ($new) $this->data = $new;
else $this->data = 'Hello';
}
public function draw() {
echo '<input>'.$this->export().'</input>';
}
public function export() {
return 'ID '.$this->id.' = '.$this->data;
}
}
class textarea extends input {
public function __construct($new = '') {
parent::__construct($new);
}
public function draw() {
echo '<textarea>'.$this->export().'</textarea>';
}
}
$a = new textarea();
$a->draw();
$a = new textarea('World');
$a->draw();
$a = new input('!');
$a->draw();
//Outputs:
// <textarea>ID 0 = Hello</textarea>
// <textarea>ID 1 = World</textarea>
// <input>ID 2 = !</input>
Make the fields class an abstract class, and like Darren suggested, make the 'draw' method a function of the fields class.
Now heres the trick, you want the input class to extend fields, but override the draw method. This will allow you to customize the functionality of that method, and you can still call the parent variation from within it.
Finally, since the textarea class is going to have many similarities to the input class, make textarea extend input. Thereby inheriting the properties and methods of both fields and input.
Make the "fields" class have a draw method:
public function draw($msg) {
echo $msg;
}
Then in the textarea or input class put:
parent::draw("<input>");
This cuts down on the number of methods you have, and can call one method for both types of field.
Also in your "fields" class, change the id code to be like this:
public $id
public function __construct($id) {
$this->id = $id;
}
Then in the subclass:
parent::__construct(1); //Or whatever ID you want
The way you have it, ID is the same value every time you set it, which will result in every subclass of fields having the same id. This way each subclass will have a seperate ID.
Also because I'm nice, here's it all put together:
public class field {
$id;
public __construct($id) {
$this->id = $id;
}
public function draw($msg) {
echo $msg;
}
}
public class input extends field {
public __construct() {
parent::__construct(1);
parent::draw("<input>");
}
}
public class textarea extends field {
public __construct() {
parent::__construct(2);
parent::draw("<textarea>");
}
}
That's how I'd put it together from what you've said. I may have mistaken what you were asking for though. Can you tell I'm primarily a Java programmer from that?
It's not exactly clear what you want to do. For the example you've given, I think the structure is OK, but you should make a few changes, particularly with the constructor. I think the constructor should be abstract, with an abstract method draw().
abstract class fields {
// Use a static member to keep track of id's instead of making it global
private static $id = 0;
// Use an instance variable to keep track of a particular instance's id
private $myId;
public function __construct() {
// Increment the static ID & assign it to the instance id.
$this->myId = self::$id++;
}
// Provide a public getter, so that the ID can't be changed
// externally to this class
public function getId() {
return $this->myId;
}
public abstract draw(); // Make sure all sub classes implement a draw() method.
}
class input extends fields {
// Don't need to call the parent constructor if you're not adding anything
// else. It will be called automatically.
public function draw() {
echo '<input>';
}
}
class textarea extends fields {
public function draw() {
echo '<textarea>';
}
}

Categories