I want to load 2 objects from my DB. The first object is the parent and second inherit of the first (in PHP and DB).
I've created 2 class :
(it's only a sample not the real code so don't try to correct this ;-) )
class A{
...
public static function get($id){
$query = "SELECT id,field1,field2 FROM table_A WHERE id = $id";
$result = request($query);
return load_object_A_instance($result);
}
...
}
class B extends A{
...
public static function get($id){
$query = "SELECT id,field3,field4 FROM table_B WHERE id = $id";
$result = request($query);
return load_object_B_instance($result);
}
...
}
I would instantiate object B with its own properties and with properties of object A in the "same" action. How can I do this ?
I've some ideas but I don't see how to implement them :
class B extends A{
...
public static function get($id){
$query = "SELECT id, field3, field4 FROM table_B WHERE id = $id";
$result = request($query);
$B = load_object_B_instance($result);
if($B != empty/null){
$B = merge(A::get($B->id),$B); // <== that's the part I don't know how to implement
}
return $B;
}
...
}
Edit :
I found a first solution (it's not clean but ...)
echo $obj->name; //show: carlos
echo $obj2->lastname; //show: montalvo here
$obj_merged = (object) array_merge((array) $obj, (array) $obj2);
$obj_merged->name; //show: carlos
$obj_merged->lastname; //show: montalvo here;
Solution found here: How do I merge two objects?
Just because you use classes, does not make it OOP. If you want to merge data from two data source, you retrieve information from them and then a 3rd partt to combined then:
$foo = new A( $connection );
$bar = new B( $connection );
$data = $foo->get( 42 ) + $bar->get( 1 );
Also you should look into DataMapper pattern, and watch some video on the subject of proper OOP:
Inheritance, Polymorphism, & Testing
Advanced OO Patterns (slides)
Unit Testing
Global State and Singletons
Don't Look For Things!
You can do that with single-table inheritance:http://www.jacopobeschi.com/post/php-laravel-single-table-inheritance
Related
I was wondering how fetchAll of PDO is actually implemented to get an Idea how to map the result from the database including a GROUP_CONCAT() comma separated list string to an array property.
Having a sql like
$query = "Select a.id, GROUP_CONCAT(b.name) AS referencingNames FROM a JOIN b on (a.id = b.id_a)"
Will return something like
id (int)
referencingNames (srting)
1
Mark, Mona, Sam
2
Jim, Tom, Sara, Mike
3
...
My Object to map to looks like this
class someObject {
public int $id;
public array $referencingNames;
}
When I call my php code then:
$pdo = new PDO(....)
$statement = $pdo->prepare($query);
$statement->execute();
$objects = $statement->fetchAll(PDO::FETCH_CLASS, someObject::class);
I am running into a type error, as referencingNames is obviously a string.
What I then tried was to set $referencingNames private and use the magic function __set() as it says in the php docs the following
is run when writing data to inaccessible (protected or private) or non-existing properties
class someObject {
public int $id;
private string $referencingNames;
public ?array $refNamesList;
public function __set($name, $value)
{
if($name == "referencingNames") {
$this->referencingNames = $value;
$this->refNamesList = explode(",", $value);
} else {
$this->$name = $value;
}
}
}
The bad news: this didn't work out. I get back an object where refNamesList stays null. Logging the call of __set() did not give me any output, so I assume, it does not get called.
Has anybody an Idea how I can map GROUP_CONCAT to an array with PDOs fetchAll() without building my own solution?
I mean, fetching all, and iterating the whole thing is still an option, but I was wondering if I can do this more elegantly anyhow.
As the name of the column you are loading is part of the class, it's setting that value anyway without having to call the __set method. So one way (seems to work) is to add a column alias which doesn't exist in the class - nameList in this example...
$query = "Select a.id, GROUP_CONCAT(b.name) AS nameList
FROM a
JOIN b on (a.id = b.id_a)"
this should then call the __set method and you can process when you get nameList...
class someObject {
public int $id;
public array $referencingNames;
public function __set($name, $value)
{
if($name == "nameList") {
$this->referencingNames = explode(",", $value);
} else {
$this->$name = $value;
}
}
}
In ZF2, suppose I have a query result from the database like this:
Name | Value
-----+-------
a | 1
a | 2
a | 3
b | 6
b | 1
b | 5
...
There is a class:
class SomeClass
{
protected $values;
// + getter and setter for $values
}
I want to hydrate SomeClass so that I have the values property as an array, like [1, 2, 3].
How to do this?
PS: I know the hydration is done with the HydratingResultSet(), but AFAIK, HydratingResultSet() hydrates one object per table row, whereas here I need to hydrate 1 object for several rows.
EDIT: after remarks from #newage, understood that the question wasn't well described.
I need to have the objects, instantiated from SomeClass, like a = new SomeClass() and b = new SomeClass() that will have the values variables filled with [1, 2, 3] for a, and [6, 1, 5] for b -- exactly what corresponds to a and b from the database query result.
HydratingResultSet will return a ResultSet. ResultSet implements Iterator interface. It return array of results.
If you need other collection class, you can write it.
For example:
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter'); // Get an adapter
$hydrator = new ClassMethods; // Create a hydrator.
$entity = Entity\User::class; // Entity class name
$prototype = new EntityCollection($hydrator, $entity);
$table = new TableGateway('table_name', $dbAdapter, null, $prototype);
If your entity with methods set... and get..., you need use ClassMethods hydrator. If entity with properties, need use ObjectProperty hydrator.
For creating a collection of entities. Need create a collection class, it need implement Iterator and ResultSetInterface
class EntityCollection implements Iterator, ResultSetInterface
{
protected $entities = [];
protected $hydrator;
protected $entityName;
public function __construct($hydrator, $entityName)
{
$this->hydrator = $hydrator;
$this->entityName = $entityName;
}
public function initialize($dataSource)
{
foreach ($dataSource as $dataRow) {
$this->append($this->hydrator->hydrate((array)$dataRow, new $this->entityName()));
}
}
public function append($entity)
{
array_push($this->entities, $entity);
return $this;
}
/* Need create all methods from Iterator */
...
}
UPDATE: after remark from #dima-dz.
You can read all data from DB and use foreach.
Like this. Example
$someObjectA = new SomeClass();
$someObjectB = new SomeClass();
$result = $table->select([]);
foreach ($result as $row) {
switch ($row->Name) {
case 'a':
$someObjectA->add($row->Value);
break;
case 'b':
$someObjectB->add($row->Value);
break;
}
}
#newage, posted a great solution, but I found out there's another possibility to do what I wanted, maybe a bit simpler. There's a php function parse_str() that can parse a string from Mysql field and create an array for the values variable if you properly format the content of the mysql field. An example of a mysql join with the ZF2 that creates the needed string for parsing is like this
->join(
['t' => 'Table',
't.Id = t1.Id',
[
'Values' =>
new Expression("GROUP_CONCAT(
DISTINCT CONCAT_WS('=', t1.Id, t1.Name) SEPARATOR '&')"
),
]
)
i wanna store some configuration data in some objects, and i've a problem...
class B {
const attr1 = 'something1';
const attr2 = 'something2';
const attr3 = 'something3';
}
class A {
const attr1 = 'somethingA1';
const attr2 = 'somethingA2';
const attr3 = 'somethingA3';
const b = <---- i wanna put here a reference of B, but B as a class (not an object...), i'm crazy?
}
I'm not sure if my example is clear... from A, i want to access to attr1 of B, like A::B::attr1, or something like this, there's another way to do this? Or i'm wrong?
There is no way to assign reference to a Class, nor is there a way to assign class constants at runtime. So your entire approach is pretty much impossible. What you can do is
const b = 'B'
and as of PHP 5.3.0 you could then do
$b = A::b;
echo $b::attr1;
but you cannot do A::b::attr1. This will raise a T_PAAMAYIM_NEKUDOTAYIM error. It's a parser limitation. PHP cannot do this as of this writing.
Because the class B contains a group of data of A, i want to store in b (of A) a complex data, i want to do like this because i wanna keep the code clean
You can solve this easily by making B a composite of A, e.g. you either inject B into A when you create A
class A
{
private $b;
public function __construct(B $b)
{
$this->b = $b;
}
}
$a = new A(new B);
or create B inside A, e.g.
class A
{
private $b;
public function __construct()
{
$this->b = new B;
}
}
Because B is just the data parts of A, you tunnel all public access through A instead of getting hold of B and then using B's methods. So any code using A does not need to know there is some B inside A. This will allow you to change B easily without needing to worry about code that consumes A, e.g. to get attr1 you add a getter to A:
public function getAttr1()
{
$b = $this->b;
return $b::attr1;
}
You can mitigate the clumsy need for assignment when using properties instead of constants in B (constants are stupid in PHP anyway as you have to treat them as public API), e.g.
class B
{
private $attr1;
public function getAttr1()
{
return $this->attr1;
}
}
And then you can do in A:
public function getAttr1()
{
return $this->b->getAttr1();
}
Even better would be not to expose the internals of B through A altogether though and only add public methods that do something with A. This will make your API much smaller and your code more OO.
You cannot reference to a class like that, because the only way you will ever use the class is through an object, so there is no point trying to do that either.
User this:
class A {
const attr1 = 'somethingA1';
const attr2 = 'somethingA2';
const attr3 = 'somethingA3';
public $b;
function __construct() {
$this -> b = new b();
}
You can simply say b = "B" ..
and if you want to create object of B , You can do new b or access the properties
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 several classes that are basically interfaces to database rows. Since the class assumes that a row already exists ( __construct expects a field value ), there is a public static function that allows creation of the row and returns an instance of the class.
Here's a pseudo-code ( so there are mistakes and missing improvements in this ) example :
class fruit {
public $id;
public function __construct( $id ) {
if ( ! is_numeric($id) ) {
throw new Exception("Id is not numeric.");
}
$this->id = $id;
$sql = "SELECT * FROM Fruits WHERE id = $id";
...
$this->arrFieldValues[$field] = $row[$value];
}
public function __get( $var ) {
return $this->arrFieldValues[$var];
}
public function __set( $var, $val ) {
$sql = "UPDATE fruits SET $var = " . mysql_real_escape_string($val) . " WHERE id = $this->id";
}
public static function create( $fruit ) {
$sql = "INSERT INTO Fruits ( fruit_name ) VALUE ( '" mysql_real_escape_string($fruit) . "' )";
$id = mysql_insert_id();
$fruit = & new fruit($id);
return $fruit;
}
}
$obj1 = fruit::create( "apple" );
$obj2 = & new fruit( 12 );
What is this pattern called?
Edit: I changed the example to one that has more database-interface functionality. For most of the time, this kind of class would be instantiated normally, through __construct(). But sometimes when you need to create a new row first, you would call create().
I think it's the Factory method pattern.
The factory method pattern is an object-oriented design pattern to implement the concept of factories.
Like other creational patterns, it deals with the problem of creating objects (products) without specifying the exact class of object that will be created. The factory method design pattern handles this problem by defining a separate method for creating the objects, which subclasses can then override to specify the derived type of product that will be created.
Outside the scope of design patterns, the term factory method can also refer to a method of a factory whose main purpose is creation of objects.
As this is related to databases, I think this is close to something that may be called Data Mapper.
If you are looking for something like this in PHP, move to Propel ORM. Look at the first example : it's almost your code !