Class in een class
Ik ben bezig met een website waarbij ik aardig gebruik maak van OOP. Daarbij heb ik nu het probleem dat ik een bepaalde class in een andere class moet gebruiken. Ik wil niet opnieuw een object aanmaken omdat ik dan weer een 'leeg' object heb. Ik wil namelijk een bestaand object (die aangemaakt is buiten de class) gebruiken in een class.
Voorbeeldje om het wat te verduidelijken
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class class1 {
private $data;
function functie1($data) {
$this->date = $data;
}
}
$class1 = new class1;
class class2 {
function functie2($data) {
return $class1->functie1($data);
}
}
private $data;
function functie1($data) {
$this->date = $data;
}
}
$class1 = new class1;
class class2 {
function functie2($data) {
return $class1->functie1($data);
}
}
Ik weet nu al bijna zeker dat het op bovenstaande manier niet zal werken. Weet er toevallig iemand hoe je dit kan oplossen?
Bedankt alvast!
Toevoeging op 23/12/2012 22:33:36:
Ik heb net iets gevonden waarmee ik het zou kunnen proberen. Met de singleton patern. Iemand die weet of dat goed en veilig is?
Tevens raad ik je aan eens te kijken naar Dependency Injection: http://www.phphulp.nl/php/tutorial/classes/dependency-injection/760/
PS: Ik ben totaal geen voorstander van het Singleton pattern en dat is hier zeker niet voor nodig.
Gewijzigd op 23/12/2012 22:42:01 door Wouter J
Is dit dan echt de enige goede manier? En waarom is singleton slecht?
Quote:
Is dit dan echt de enige goede manier?
De 'enige goede' durf ik niet te zeggen, het is een van de meest recentste patternen in PHP wereld en wordt langzamerhand door alle grote libraries gebruikt.
Met een Service Container voorkom je problemen met het telkens moeten toevoegen van de $db parameter. Kijkt bijv. maar eens naar dit simpele voorbeeld:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
$container = new Pimple();
$container['database.class'] = 'Wj\Storage\MySQLiDatabaseStorage';
$container['database.settings'] = array(
'user' => 'root',
'pass' => 'foobar',
'host' => 'localhost',
...
);
$container['database'] = function ($c) {
$settings = $c['database.settings'];
return new $c['database.class']($settings['host'], $settings['user'], ...);
};
$container['user.mapper.class'] = 'Wj\User\UserMapper';
$container['user.mapper'] = $container->share(function ($c) {
return $c['user.mapper.class']($c['database']);
});
// in gebruik
$user = $container['user.mapper']->findById(2);
$user1 = $container['user.mapper']->findByName('Henk');
?>
$container = new Pimple();
$container['database.class'] = 'Wj\Storage\MySQLiDatabaseStorage';
$container['database.settings'] = array(
'user' => 'root',
'pass' => 'foobar',
'host' => 'localhost',
...
);
$container['database'] = function ($c) {
$settings = $c['database.settings'];
return new $c['database.class']($settings['host'], $settings['user'], ...);
};
$container['user.mapper.class'] = 'Wj\User\UserMapper';
$container['user.mapper'] = $container->share(function ($c) {
return $c['user.mapper.class']($c['database']);
});
// in gebruik
$user = $container['user.mapper']->findById(2);
$user1 = $container['user.mapper']->findByName('Henk');
?>
Quote:
En waarom is singleton slecht?
Doordat je maar 1 instance per request kan hebben. Dat houdt elke vorm van Test Driven Development en elke vorm van uitbreiding tegen.
Gewijzigd op 23/12/2012 23:05:28 door Wouter J
->setClass1($class)
->getClass1()
Maar zoals Wouter zegt is DI een betere methode.
Raoul, setters is ook een vorm van Injection. Je hebt 3 vormen: Constructor injection, Property injection en setter injection. Property injection wordt zelden gebruikt en als een klasse iets per se nodig heeft gebruik je constructor en anders setter.
Ah zo, weer iets bijgeleerd :))
Is singleton daarvoor toch geen goed idee?
Ik denk wel dat DI voor een simpele applicatie en een beginner als jouw een beetje uitgebreid is... dus daarom toon ik even mijn getter en setter voorbeeldje:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
class EersteClass
{
}
class TweedeClass
{
private $_eersteClass;
public function setEersteClass(EersteClass $eerst)
{
$this->_eersteClass = $eerst;
}
public function getEersteClass()
{
return $this->_eersteClass;
}
public function voorbeeldje()
{
$class = $this->getEersteClass();
// doe iets met $class
}
}
$eersteClass = new EersteClass();
$tweedeClass = new TweedeClass();
$tweedeClass->setEersteClass($eersteClass);
?>
class EersteClass
{
}
class TweedeClass
{
private $_eersteClass;
public function setEersteClass(EersteClass $eerst)
{
$this->_eersteClass = $eerst;
}
public function getEersteClass()
{
return $this->_eersteClass;
}
public function voorbeeldje()
{
$class = $this->getEersteClass();
// doe iets met $class
}
}
$eersteClass = new EersteClass();
$tweedeClass = new TweedeClass();
$tweedeClass->setEersteClass($eersteClass);
?>
Als je van plan bent om meerdere classen te hebben die ook EersteClass gebruiken kan je misschien ook werken met Aware classen?
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
abstract class EersteClassAware
{
private $_eersteClass;
public function setEersteClass(EersteClass $eerst)
{
$this->_eersteClass = $eerst;
}
public function getEersteClass()
{
return $this->_eersteClass;
}
}
class EersteClass {}
class TweedeClass extends EersteClassAware {}
class DerdeClass extends EersteClassAware {}
$eersteClass = new EersteClass();
$tweede = new TweedeClass();
$derde = new DerdeClass();
$tweede->setEersteClass($eersteClass);
$derde->setEersteClass($eersteClass);
?>
abstract class EersteClassAware
{
private $_eersteClass;
public function setEersteClass(EersteClass $eerst)
{
$this->_eersteClass = $eerst;
}
public function getEersteClass()
{
return $this->_eersteClass;
}
}
class EersteClass {}
class TweedeClass extends EersteClassAware {}
class DerdeClass extends EersteClassAware {}
$eersteClass = new EersteClass();
$tweede = new TweedeClass();
$derde = new DerdeClass();
$tweede->setEersteClass($eersteClass);
$derde->setEersteClass($eersteClass);
?>
Gewijzigd op 24/12/2012 13:47:21 door - Raoul -
Ik heb dus een database class aangemaakt. Dan maak ik bovenaan in mijn index file het database-object aan door volgende code uit te voeren:
Het probleem is nu dat ik het $db object ook in een andere class (user) wil gebruiken.
Code (php)
1
2
3
4
5
2
3
4
5
class user {
__constructor() {
$this->db = $db; // Hoe verkrijg ik hier de reeds aangemaakte $db object?
}
}
__constructor() {
$this->db = $db; // Hoe verkrijg ik hier de reeds aangemaakte $db object?
}
}
Wat ik nu heb is als volgt:
Code (php)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
class user {
__constructor($db) {
$this->db = $db; // Hoe verkrijg ik hier de reeds aangemaakte $db object?
}
}
$db = new mysql($host, $user, $pass, $database);
$user = new user($db);
__constructor($db) {
$this->db = $db; // Hoe verkrijg ik hier de reeds aangemaakte $db object?
}
}
$db = new mysql($host, $user, $pass, $database);
$user = new user($db);
Ik wil nu juist net niet die $db meesturen naar de constructor van de user class. Dit lijkt mij nogal vreemd om te doen. Net omdat ik dan bij iedere class waar ik die database nodig heb dat moet bijvoegen.
Je code is wel niet echt net...
Hieronder verbeterd:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
class User
{
public $db = null;
public $id = null;
public function __construct($db, $id)
{
$this->db = $db;
$this->id = $id;
}
public function getUsername()
{
$user = $this->db->query('...');
return $user->fetch()['username'];
}
}
$db = new Db(...);
$user = new User(1, $db);
echo $user->getUsername();
?>
class User
{
public $db = null;
public $id = null;
public function __construct($db, $id)
{
$this->db = $db;
$this->id = $id;
}
public function getUsername()
{
$user = $this->db->query('...');
return $user->fetch()['username'];
}
}
$db = new Db(...);
$user = new User(1, $db);
echo $user->getUsername();
?>
Toevoeging op 24/12/2012 15:05:21:
Lees anders even deze OOP tutorial: http://phptuts.nl/view/45/ ?
Gewijzigd op 24/12/2012 15:06:37 door - Raoul -
- Raoul - op 24/12/2012 15:04:12:
Wat je hierboven doet is gewoon normale OO en is gewoon correct, hoe vreemd je dat ook vind.
Je code is wel niet helemaal net, je zet geen 'public function' voor je methods.
Hieronder verbetert:
Toevoeging op 24/12/2012 15:05:21:
Lees anders even deze OOP tutorial: http://phptuts.nl/view/45/ ?
Je code is wel niet helemaal net, je zet geen 'public function' voor je methods.
Hieronder verbetert:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
class User
{
public $db = null;
public $id = null;
public function __construct($db, $id)
{
$this->db = $db;
$this->id = $id;
}
public function getUsername()
{
$user = $this->db->query('...');
return $user->fetch()['username'];
}
}
$db = new Db(...);
$user = new User(1, $db);
echo $user->getUsername();
?>
class User
{
public $db = null;
public $id = null;
public function __construct($db, $id)
{
$this->db = $db;
$this->id = $id;
}
public function getUsername()
{
$user = $this->db->query('...');
return $user->fetch()['username'];
}
}
$db = new Db(...);
$user = new User(1, $db);
echo $user->getUsername();
?>
Toevoeging op 24/12/2012 15:05:21:
Lees anders even deze OOP tutorial: http://phptuts.nl/view/45/ ?
Bedankt! Ik zal het dan op deze manier verder doen. Ik zet wel private en public voor mijn functies. Maar het voorbeeld was even 'snel' getypt hier. Geen exacte kopie van wat ik heb in mijn bestanden.
Heel erg bedankt iedereen!
De meeste makkelijke oplossing is volgens mij op elke class waarin je een database connectie willen gebruiken te extenden met de database class.
Je krijgt dan dit:
Code (php)
De grotere frameworks doen het ook op deze manier omdat het gewoon snel en overzichtelijk werkt.
Rick van Riel op 24/12/2012 15:44:08:
Ruben,
De meeste makkelijke oplossing is volgens mij op elke class waarin je een database connectie willen gebruiken te extenden met de database class.
Je krijgt dan dit:
De grotere frameworks doen het ook op deze manier omdat het gewoon snel en overzichtelijk werkt.
De meeste makkelijke oplossing is volgens mij op elke class waarin je een database connectie willen gebruiken te extenden met de database class.
Je krijgt dan dit:
Code (php)
De grotere frameworks doen het ook op deze manier omdat het gewoon snel en overzichtelijk werkt.
Bedankt, maar wat als je dan meerdere classes wil 'includen' in een andere class. Ik heb b.v. een class database, error en user. Ik wil in user de class database en error kunnen aanroepen. Kan dit wel met extend?
Rick van Riel op 24/12/2012 15:44:08:
Ruben,
De meeste makkelijke oplossing is volgens mij op elke class waarin je een database connectie willen gebruiken te extenden met de database class.
Je krijgt dan dit:
De grotere frameworks doen het ook op deze manier omdat het gewoon snel en overzichtelijk werkt.
De meeste makkelijke oplossing is volgens mij op elke class waarin je een database connectie willen gebruiken te extenden met de database class.
Je krijgt dan dit:
Code (php)
De grotere frameworks doen het ook op deze manier omdat het gewoon snel en overzichtelijk werkt.
Nee, de meeste frameworks doen het niet zo en gebruiken DI.
dat je nog mag extenden om deze rede!
En je mag mij een framework laten zien die dat op deze manier doet. Tevens zijn frameworks nooit gemaakt om 'snel' te werken, maar juist om 'goed en mooi volgens de standaarden' te werken. Als laatst is dit niet overzichtelijk, aangezien je op deze manier niet meerdere 'dependencies' kan hebben.
Neeneeneeneeneeeeee, niet doen Rick! Met Extenden maak je een IS_EEN relatie, je mag alleen extenden als je een klasse uitbreid. Bijv. Admin extends User, want Admin IS_EEN User of Cat extends Animal, want Cat IS_EEN Animal. Maar een User is geen vorm of uitbreiding van Database, User IS_GEEN Database maar User HEEFT_EEN database. Al is dat officieel niet goed en zul je een UserMapper hebben die een database HEEFT, maar goed dat laten we buiten beschouwing. Het hoofddoel is En je mag mij een framework laten zien die dat op deze manier doet. Tevens zijn frameworks nooit gemaakt om 'snel' te werken, maar juist om 'goed en mooi volgens de standaarden' te werken. Als laatst is dit niet overzichtelijk, aangezien je op deze manier niet meerdere 'dependencies' kan hebben.
Gewijzigd op 24/12/2012 16:29:18 door Wouter J
http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static
Voorbeeldje:
Ik vind het zelf ook redelijk vervelend om steeds een database object via de constructor mee te geven, en dan is dit een oplossing waarmee alles wat soepeler gaat. Ik ben benieuwd wat de rest van zoiets ^ vind
Wat ook een optie is, is werken met een static variable: Voorbeeldje:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
class Database {
static $instance;
public static function getInstance() {
if (null === self::$instance) {
self::$instance = new PDO(...);
}
return self::$instance;
}
}
class Model {
protected $db;
public function __construct() {
$this->db = Database::getInstance();
}
}
class User extends Model {
public function getUsername($id) {
// je kan hier $this->db aanroepen
}
}
?>
class Database {
static $instance;
public static function getInstance() {
if (null === self::$instance) {
self::$instance = new PDO(...);
}
return self::$instance;
}
}
class Model {
protected $db;
public function __construct() {
$this->db = Database::getInstance();
}
}
class User extends Model {
public function getUsername($id) {
// je kan hier $this->db aanroepen
}
}
?>
Ik vind het zelf ook redelijk vervelend om steeds een database object via de constructor mee te geven, en dan is dit een oplossing waarmee alles wat soepeler gaat. Ik ben benieuwd wat de rest van zoiets ^ vind
Gewijzigd op 24/12/2012 18:00:52 door Moose -
En denk nu eens over het gebruik van deze applicatie op een host zonder database? Dan moet je bijv. een FileDatabase klasse gaan gebruiken en mag je al je models aanpassen.
Tevens vind ik User geen model, UserModel of UserMapper daarintegen wel.
Ga nu alsjeblieft niet werken met singletons en beginners de verkeerde manier aanleren.
Singleton is een oplossing, en het is/was populair voor een reden. Het is niet een verkeerde manier, het is een manier.
Ik denk dat jullie alles een beetje aan het overdenken zijn, zeker aangezien de vragen die de OP stelt. Als hij nooit singleton gaat gebruiken, zal hij ook nooit achter de sterke/zwakke punten komen :)
Om Wouters voorbeeld van een user aan te halen, wat ga je doen als je 6 verschillende type users hebt, ga je dan voor elke type user de user class extenden? Nee toch?