Symfony: Entities and Class Implements - php

Suppose I want to have two entities in my database, a Box and an Object. I can have different boxes and within each box I can add multiple Objects.
(For simplicity, I've missed out the getters, setters, $id, etc in the code examples)
Box entity
namespace AppBundle\Entity;
class Box {
...
/**
* #ORM\OneToMany(targetEntity="Object", mappedby="box")
*/
private $objects;
}
Object entity
namespace AppBundle\Entity;
class Object {
...
/**
* #ORM\ManyToOne(targetEntity="Box", mappedby="objects")
*/
private $box;
// The following are assumptions
private $objectType;
private $objectData;
private $theObject;
}
My objects can be multiple types (I want to be able to add new types of objects without touching the database):
A Pen (not an Entity)
class Pen implements ObjectType {
public $color; // In reality this would have set/get
public function whatAmI() {
return "I am a ".$this->color." pen";
}
}
A Remote (not an Entity)
class Remote implements ObjectType {
public $numberOfButtons;
public function whatAmI() {
return "I am a remote with ".$this->numberOfButtons." buttons";
}
public function pressButton() {
...
}
}
ObjectType
interface ObjectType {
public function whatAmI();
}
My question is what is the best way to tie the two bits together? I would like to be able to do the following:
Go through all the objects in my box and call functions which they implement:
$myBox = $em->find('Box', $id);
foreach ($myBox->getObjects() as $object) {
echo $object->whatAmI();
}
Create a new object and add it to my box:
$myPen = new Pen();
$myPen->color = "red";
$myBox->addObject($myPen);
Call functions if I fetch the object directly from the DB:
$myRemote = find('Object', $id); // $id will be of a Remote class
$myRemote->pressButton();

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();
}
}

Why can't I use public properties in Doctrine entities?

Say I have a very simple CRUD system in PHP to manage a database. Say it holds a list of products. Using Doctrine ORM, I'd like to query the database and view/edit/add records. According to the Getting Started manual,
When creating entity classes, all of the fields should be protected or
private (not public), with getter and setter methods for each one
(except $id). The use of mutators allows Doctrine to hook into calls
which manipulate the entities in ways that it could not if you just
directly set the values with entity#field = foo;
This is the sample provided:
// src/Product.php
class Product
{
/**
* #var int
*/
protected $id;
/**
* #var string
*/
protected $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
}
// Recording a new title
$product->setName("My new name");
$db->persist($product);
$db->flush();
// Echoing the title
echo $product->getName();
However, this seems overly complicated. Say I don't have a need to hook into calls to manipulate entities, as explained in the manual. I can make this code much shorter and do this:
// src/Product.php
class Product
{
/**
* #var int
*/
public $id;
/**
* #var string
*/
public $name;
}
This would allow things like this:
$product = $db->getRepository('Product')->find(1);
// Recording a new name
$product->name = "My new title";
$db->persist($product);
$db->flush();
// Echoing the title
echo $product->name;
The advantages are:
Always using the exact same name
No extra setters and getters in the entity class
What is the disadvantage of using this? Am I taking certain risks here?
Say I don't have a need to hook into calls to manipulate entities, as explained in the manual.
You do not need to hook into these calls, but Doctrine does. Doctrine internally overrides these getters and setters to implement an ORM as transparently as possible.
Yes, it is overly complicated, but that is the PHP language at fault and not Doctrine. There is nothing inherently wrong with using public properties, it is just that the PHP language does not allow creating custom getters/setters the way some other languages do (such as Kotlin, C#).
What does Doctrine do exactly? It generates so-called Proxy objects to implement lazy loading. These objects extend your class and override certain methods.
For your code:
// src/Product.php
class Product
{
/**
* #var int
*/
protected $id;
/**
* #var string
*/
protected $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
}
Doctrine might generate:
class Product extends \YourNamespace\Product implements \Doctrine\ORM\Proxy\Proxy
{
// ... Lots of generated methods ...
public function getId(): int
{
if ($this->__isInitialized__ === false) {
return (int) parent::getId();
}
$this->__initializer__ && $this->__initializer__->__invoke($this, 'getId', []);
return parent::getId();
}
public function getName(): string
{
$this->__initializer__ && $this->__initializer__->__invoke($this, 'getName', []);
return parent::getName();
}
// ... More generated methods ...
}
(What this does exactly is not the point, the point is that Doctrine MUST be able to override getters and setters to implement an ORM)
To see for yourself, inspect the generated Proxy objects in the proxy directory of Doctrine. See the Advanced Configuration section for more info on configuring the proxy directory. Also see Working with Objects for more info on Proxy objects and how Doctrine utilizes them.
Why not use public entity classes in Doctrine?
Actually what you are asking is:
Where does this getters/setters come from?
It comes from the Encapsulation in OOP.
so, what is encapsulation?
It’s the way we define the visibility of our properties and methods. When you’re creating classes, you have to ask yourself what properties and methods can be accessed outside of the class.
Let’s say we had a property named id. If a class extends your class, is it allowed to manipulate and access id directly? What if someone creates an instances of your class? Are they allowed to manipulate and access id?
NO! They should not be able to reach the id attribute directly.
Why not?
Example: What happens if you only accept Invoice Ids which start with "IX" and other classes have access to your class and they set the invoice_id directly?
What happens if they accidentally set the invoice_id to "XX12345"?
of course, you cannot do anything (like validating the input), because you are using public properties and no setters.
but if you had a setter method in your class, you could just do
private $invoiceId;
public function setInvoiceId($invoiceId)
{
if (is_null($invoiceId) || preg_match("#^IX(.*)$#i", $invoiceId) === 0)
return false;
$this->invoiceId = $invoiceId;
return $this;
}
I hope it was clear enought. I will try to extend the answer

PHP - Classes inheritance and combining with another class

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

Redefining PHP class functions on the fly?

I am trying to figure out how to import a large number of PHP class functions on the fly. For example...
class Entity
{
public function __construct($type)
{
require_once $type."_functions.php"
}
// ...
}
$person = new Entity("human");
$person->sayhi();
$cow = new Entity("cow");
$cow->sayhi();
human_functions.php:
class Entity redefines Entity
{
public function sayhi()
{
echo "Hello world!";
}
}
cow_functions.php:
class Entity redefines Entity
{
public function sayhi()
{
echo "Moo!";
}
}
I have found a few possibilities like classkit_method_redefine() and runkit_method_redefine() (which are "experimental", and they cannot modify the currently running class anyway). I am on PHP 5.3.3 right now, so I can't use Traits (Not sure if that is what I am looking for anyways). I have had success redefining the handler variable like this:
// Example 2:
class OtherEntity { /* Code Here */ }
class Entity
{
public function __construct($type)
{
global $foo;
unset($foo);
$foo = new OtherEntity();
}
}
$foo = new Entity();
But, this feels like a very hacky method. More importantly, if I don't name every instance of the class $foo, then it will not work. Are there any workarounds for what I am trying to do?
Note: I am aware that I can extend a class, but in my case when the Entity class is initiated, there is no safe way to know in advance what subclass it would need to be initiated with. Perhaps there is a method I could write, such as:
public function changeClass
{
this->class = OtherEntity;
}
Thanks for your help!
Here's an idea of a possible solution you could try. Let the Cow and Human classes extend the Entity class. However, the Entity class would use a factory to instantiate the objects based on if the value was safe. Let's look at this in more detail:
/*
* Class Entity should not be able to be instantiated.
* It should contain a factory to instantiate the
* appropriate entity and an abstract function declaring
* the method that each entity will need to implement.
*/
abstract class Entity {
public static function factory($type) {
return (is_subclass_of($type, "Entity")) ? new $type() : FALSE;
}
abstract public function sayHi();
}
/*
* Human class extends Entity and implements the
* abstract method from Entity.
*/
class Human extends Entity {
public function sayHi() {
echo "Hello World!";
}
}
/*
* Cow class extends Entity and implements the
* abstract method from Entity.
*/
class Cow extends Entity {
public function sayHi() {
echo "Moo!";
}
}
Now to use this method, call the factory method and if all works well, it'll instantiate the proper class which will extend Entity.
$person = Entity::factory("Human");
$person->sayHi();
$cow = Entity::factory("Cow");
$cow->sayHi();
Using, is_subclass_of() will keep you safe because if the passed in value is not a class that extends Entity, you'll be returned a value of FALSE.
If you'd like to see the above code in action, copy the above php code and test it out on phpfiddle.org.
One thing you can do is create Human and Cow as subclasses of Entity. When you do new Entity("Human"), you can store a newly created Human object inside the Entity instance.
Then you can use __call to redirect method calls to the "child element".
class Entity{
private $child;
public function __construct($type){
$this->child = new $type;
}
public function __call($func, $params=array()){
$method = method_exists($this, $func)
? [$this, $func] : [$this->child, $func];
return call_user_func_array($method, $params);
}
}
class Human extends Entity{
public function __construct(){}
public function sayhi(){
echo "Hello world!";
}
}
class Cow extends Entity{
public function __construct(){}
public function sayhi(){
echo "Moo!";
}
}
$person = new Entity("Human");
$person->sayhi();
$cow = new Entity("Cow");
$cow->sayhi();
The only downside is that $person and $cow are both Entity objects.

Derived Classes in Php Phalcon

I am fairly new to php phalcon and the language itself. I am making a website that involves an abstract class and derived classes.
The Abstract Class:
<?php
abstract class UsersAbstract extends \Phalcon\Mvc\Model {
private $Full_Name; //
private $Mobile_Number; //
private $Email_ID; //
private $City; //
private $Country; //
private $DoB; //
private $Gender; //
private $Age; //
private $Availability_Flag; /*value of flag is 1 for active and 0 for inactive*/
/********setters and getters for all data members which I have not written here*******/
public function getSource()
{
return 'users_abstract';
}
}
The derived class
<?php
class UserPatients extends UsersAbstract
{
private $Patient_ID;
private $Guardian_Name;
private $Doctors = array();
public function getSource()
{
return 'user_patients';
}
/***************setter and getter for patient id*******************/
public function getPatientID()
{
return $this->Patient_ID;
}
public function setPatientID($value)
{
$this->Patient_ID = $value;
}
/****************setter and getter for guardian name******************/
public function getGuardianName()
{
return $this->Guardian_Name;
}
public function setGuardianName($value)
{
$this->Guardian_Name = $value;
}
/****************getter and setter for doctor array*********************/
public function getDoctors()
{
return $this->Doctors;
}
public function setDoctors($value)
{
$this->Doctors = $value;
}}
In my controller, I am reading data from a form in a view. An object of the class UserPatients is declared and its data members are set. Here is the action method that I am calling in the controller PatientSignInController:
public function SaveAction()
{
if (!empty($_POST)){
$patient_data = new UserPatients();
$patient_data->setFull_Name($_POST['Full_Name']);
$patient_data->setGuardianName($_POST['Guardian_Name']);
$patient_data->setDoB($_POST['DoB']);
$patient_data->setAge($_POST['Age']);
$patient_data->setGender($_POST['gender']);
$patient_data->setMobile_Number($_POST['Contact_No']);
$patient_data->setEmail_Address($_POST['Email_ID']);
$patient_data->setCity($_POST['City']);
$patient_data->setCountry($_POST['Country']);
$patient_data->setPatientID($_POST['Patients_ID']);
$patient_data->setFlag(1);
$doctorsArray = array("ACD","ABC"); //some hard coded values for the time being
$patient_data->setDoctors($doctorsArray);
$patient_medical_info = new PatientInfo();
$patient_medical_info->setBP($_POST['BP']);
$patient_medical_info->setTemperature($_POST['Temperature']);
$patient_medical_info->setInformation($_POST['Info']);
$patient_medical_info->setPatientID($_POST['Patients_ID']);
$patient_medical_info->setDateOfMeeting(date("d/m/y"));
$patient_data->save();
$patient_medical_info->save();
$this->response->redirect("../../../../WebMCare/PatientSignIn/Index");
}
}
The structure of my database is the same as these classes - I have a table Users_Abstract with all the attributes of the abstract class and I have a table User_Patients with all the attributes of the child class.
When I hit the save button on the form in the view and check the database tables, I discover that the new entries are added in the patient_info table. Which means that the $patient_medical_info->save() is working.
However, the table users_abstract does not contain any new data for the corresponding object, while the table user_patients does get the associated data. I checked messages for errors in save method, but it returned nothing.
I can't seem to find anything online.
Please help me.
I have tried storing data in a single table by associating one table, with all 12 attributes, to the classes UsersAbstract and User_Patients. Even that does not work.

Categories