Class locaties
Vandaag weer een OO topic van mijn kant! Ik zit namelijk met het probleem dat ik vastloop op een toch wel vrij belangrijk punt in het OO-wereldje.
Ik heb de laatste tijd op m'n stage heel wat geleerd over namespaces en dit is mij nu wel zo goed als duidelijk. Een namespace is een bepaalde ruimte waarin classes zich bevinden, weliswaar virtueel als ik het goed heb.
Nu hangt dit een beetje samen met hetgeen waar ik tegenaan loop: waar plaats ik classes in een mappenstructuur? Stel dat ik een simpel gebruikerssysteem heb met de volgende classes: Storable, User, UserMapper, MapperInterface, Mapper en Database en MySQL.
In welke mappen zouden jullie deze classes onderbrengen? Je kunt natuurlijk alles in de map /classes gooien en een autoloader instellen en eventueel met namespaces wat doen, maar echt netjes en overzichtelijk is dat natuurlijk ook niet!
Ik kijk uit naar jullie reacties!
Groetjes,
Roel
Zoals je al zei, alles in een map gooien. Dat doe ik zelf ook altijd, ik vind het persoonlijk overzichtelijker en handiger als ik alle classes op een plek heb staan. Dat scheelt mij een hoop zoeken, vooral als ik er een tijd niet aan heb gewerkt.
Als ik kijk naar projecten zoals forum's, zetten hun de classes grotendeels ook op een plek.
Het is volgens mij maar wat je zelf handiger vind, en ik vind alle classes op een plek persoonlijk niet niet netjes eruit zien zeg maar.
Groetjes,
Rick
Ahh, eindelijk weer een OO topic, lang op gewacht!
Je moet natuurlijk met namespaces werken, waarbij ik de build-in namespace systeem van PHP adviseer (boven de PEAR namespace die ZF1 gebruikt Zend_Framework_Foo i.p.v. Zend\Framework\Foo).
Voor hoe je dit doet met namespaces en mappenstructuur/autoloading heeft de PHP usergroup een standaard geschreven, de PSR-0 standaard. Hierin is ook een standaard autoloader inbegrepen. Mocht je nou met composer gaan werken, wat een goed idee is, dan zul je zien dat de composer autoloader ook deze standards gebruikt.
Een korte samenvatting van deze standaard:
- Een namespace is opgebouwd uit ProjectNaam\NamespaceNaam. Dus als ik een project RoelUserSystem heb (laten we zeggen dat we die de RUS namespace geven) dan staan alle klassen in de RUS namespace. Nu hebben we alles wat te maken heeft met Users in de User namespace staan. Je klassen zien er dus zo uit:
Code (php)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
RUS\User\User;
RUS\User\UserMapper;
RUS\User\Type\Admin;
RUS\Mapper\MapperInterface;
RUS\Mapper\Mapper;
RUS\DI\ServiceContainer;
RUS\DI\ContainerAwareInterface;
RUS\DI\ContainerAware;
RUS\User\UserMapper;
RUS\User\Type\Admin;
RUS\Mapper\MapperInterface;
RUS\Mapper\Mapper;
RUS\DI\ServiceContainer;
RUS\DI\ContainerAwareInterface;
RUS\DI\ContainerAware;
Deze staan in devolgende mappen:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
RUS/
User/
Type/
Admin.php
User.php
UserMapper.php
Mapper/
MapperInterface.php
Mapper.php
DI/
ServiceContainer.php
ContainerAwareInterface.php
ContainerAware.php
User/
Type/
Admin.php
User.php
UserMapper.php
Mapper/
MapperInterface.php
Mapper.php
DI/
ServiceContainer.php
ContainerAwareInterface.php
ContainerAware.php
Om het project duidelijk te maken wordt deze map vaak weer in een src/vendor/lib map gestopt, zodat je de core code scheid van de project code:
Zo ziet je mappenstructuur eruit bij een hedendaagse applicatie in PHP.
Wanneer ga ik over op een andere namespace?
Ik gooi altijd dingen die bij elkaar horen in 1 namespace. Alles wat te maken heeft met de Users gaat in de User namespace, Mapper dingen in de Mapper namespace, DI dingen in de DI namespace.
Namespaces kunnen elkaar ook overlappen. De RUS\User\UserMapper klasse erft bijv. over van de RUS\Mapper\Mapper klasse. En misschien is de RUS\Mapper\Mapper wel ContainerAware en erft die weer over van de RUS\DI\ContainerAware klasse.
Is dit dan ook gewoon toe te passen in een eigen (als ik het al zo mag noemen!) framework?
Je hebt het namelijk over ZF, maar daar ben ik denk ik nog niet aan toe!
Gebruik gewoon de autoload functie die gegeven is door PSR-0 en zet die eventueel om in een spl-autoloader. Of gebruik de autoloader die Symfony2.0 gebruikt: http://symfony.com/doc/2.0/components/classloader.php (geen kennis van Symfony benodigd, gewoon alleen klassen downloaden en doen wat er in de docs staat over dat component).
Of gebruik mijn classloader: https://github.com/WouterJ/snakeMvc/tree/master/lib/snakeMvc/Loader Mocht je dat willen leg ik je dan wel even uit hoe je die moet gebruiken. Maar het komt op dit neer:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
<?php
use snakeMvc\Framework\Loader\ClassLoader;
require_once '/Loader/ClassLoader.php';
$classloader = new ClassLoader();
// ... eventuele settings
$classloader->register();
?>
use snakeMvc\Framework\Loader\ClassLoader;
require_once '/Loader/ClassLoader.php';
$classloader = new ClassLoader();
// ... eventuele settings
$classloader->register();
?>
Ik houd het toch bij die ene methode van de website die je me gaf:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
function autoload($className)
{
$className = ltrim($className, '\\');
$fileName = '';
$namespace = '';
if ($lastNsPos = strripos($className, '\\')) {
$namespace = substr($className, 0, $lastNsPos);
$className = substr($className, $lastNsPos + 1);
$fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
}
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
require $fileName;
}
?>
function autoload($className)
{
$className = ltrim($className, '\\');
$fileName = '';
$namespace = '';
if ($lastNsPos = strripos($className, '\\')) {
$namespace = substr($className, 0, $lastNsPos);
$className = substr($className, $lastNsPos + 1);
$fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
}
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
require $fileName;
}
?>
Ziet er voor mij duidelijk uit.
Goed, ik heb even een namespace structuur gemaakt voor een user systeempje, met jouw structuur als voorbeeld. Zit ik een beetje goed?
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- RUS
- User
- Type
- User
- Administrator
- User
- UserMapper
- Mapper
- MapperInterface
- Mapper
- Database
- Adapter
- MySQL
- PDO
- MySQL
- Oracle
- MSSQL
- User
- Type
- User
- Administrator
- User
- UserMapper
- Mapper
- MapperInterface
- Mapper
- Database
- Adapter
- MySQL
- PDO
- MySQL
- Oracle
- MSSQL
Van de ene kant vind ik het goed, maar van de andere kant ook weer een raar zicht.
Ik kijk uit naar jullie reactie!
Bump.
Bumperdebump.
Ik heb een structuur opgezet in mappen, maar ik heb totaal geen idee of het goed is.
Heb een 7z-bestand geupload naar Dropbox, zou iemand het willen controleren?
http://dl.dropbox.com/u/5839420/namespacing.7z
En om aan te tonen dat het geen troep is, maar ik geen andere manier kon bedenken om het te controleren:
https://www.virustotal.com/file/b6ca3770dedf2c1fc9238b94568ece26cce40a4f140228d8ad9f6cf551ff53da/analysis/1350504892/
Al vind ik PDOInterface niet echt geslaagd. Ik zou een algemene DatabaseInterface maken en dan een abstracte PDODatabase klasse. Je hebt namelijk methods die in alle PDO klassen hetzelfde zijn, eigenlijk bijna allemaal.
Hoe vind je hem er zo uitzien?
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
namespacing
index.php
RUS
Database
Adapter
Database.php
Normal
MySQL.php
NormalDatabase.php
PDO
MSSQL.php
MySQL.php
Oracle.php
PDODatabase.php
DatabaseInterface.php
Mapper
Mapper.php
MapperInterface.php
Storable
Storable.php
User
Type
Administrator.php
User.php
User.php
UserMapper.php
index.php
RUS
Database
Adapter
Database.php
Normal
MySQL.php
NormalDatabase.php
PDO
MSSQL.php
MySQL.php
Oracle.php
PDODatabase.php
DatabaseInterface.php
Mapper
Mapper.php
MapperInterface.php
Storable
Storable.php
User
Type
Administrator.php
User.php
User.php
UserMapper.php
Is het overigens niet verstandig om van de mappers en de Database class een singleton te maken?
Gewijzigd op 19/10/2012 14:27:39 door Roel -
Bump
2 hele belangrijke redenen:
- Wat als je straks 2 database verbindingen hebt? Dan moet je de klasse weer singleton-af maken. Met singletons verpest je eigenlijk een groot deel van je mogelijke uitbreiding;
- Met Test Driven Development wil je per test een nieuwe klasse hebben, je moet niet telkens met dezelfde singleton gaan werken.
Eigenlijk moet je nooit singletons gebruiken, is mijn mening.
ik zou het meer gebruiken als je met zn 2e aan het project werkt
bijvoorbeeld
namespace piet en namepace klaas
zodat je weet wie wat heeft gemaakt maar tog zal ik eerder gaan voor interfaces