Précédent   Forum des professionnels en informatique > PHP > Langage
Langage Forum sur le langage PHP, la POO, les conventions, la sécurité, etc. Avant de poster : FAQ Langage, toutes les FAQ PHP, cours langage et sources PHP
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 21/12/2010, 03h29   #1
Invité de passage
 
Étudiant
Inscription : avril 2009
Messages : 11
Détails du profil
Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : avril 2009
Messages : 11
Points : 0
Points : 0
Par défaut Composite view (MVC)

Bonjour, à tous j'ai suivi le tutoriel suivant pour développer son propre Framework MVC.

J'essaye actuellement de changer le pattern Template view par un composite view mais je ne comprend pas comment lier mon ActionController à celui-ci.

Pour cela je dispose donc des classes CompositeView et SimpleView qui extends toute les deux une classe AbstractView.


Merci beaucoup de votre aide
NearZero est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/12/2010, 10h09   #2
Membre éclairé
 
Inscription : août 2007
Messages : 360
Détails du profil
Informations forums :
Inscription : août 2007
Messages : 360
Points : 334
Points : 334
Bonjour,

Ne faudrait-il pas ajouter du code ???

Cdt,

Mathieu
mathieu44800 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/12/2010, 12h03   #3
Modérateur
 
Avatar de Benjamin Delespierre
 
Benjamin Delespierre
Développeur Web
Inscription : février 2010
Messages : 2 984
Détails du profil
Informations personnelles :
Nom : Benjamin Delespierre
Âge : 24
Localisation : France

Informations professionnelles :
Activité : Développeur Web
Secteur : High Tech - Opérateur de télécommunications

Informations forums :
Inscription : février 2010
Messages : 2 984
Points : 5 015
Points : 5 015
Personnellement, j'ai créé un Singleton ViewManager capable d'appeler les vues, ça ressemble à ça :

Code :
1
2
3
4
5
6
7
8
9
 
class FooController extends BaseController
{
    public function index ()
    {
         $this->registry->addVar('foos', FooManager::getFoos());
         ViewManager::setRegistry($this->registry)->load('/path/template');
    }
}
__________________
A la recherche d'un framework MVC facile a prendre en main ? Essayez Axiom
Nouveau: la référence d'Axiom est disponible sur GitHub (je la peaufine en ce moment même).

Un problème correctement identifié est à moitié résolu, évitez de poster l'intégralité de votre code avec pour seule explication "ça ne marche pas...".
Pour identifier correctement vos problèmes PHP, utilisez la gestion des erreurs et xdebug.

Les boutons et existent, servez-vous en
Benjamin Delespierre est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/12/2010, 12h44   #4
Membre éclairé
 
Inscription : août 2007
Messages : 360
Détails du profil
Informations forums :
Inscription : août 2007
Messages : 360
Points : 334
Points : 334
Tu es encore un peu avare en code source...
mathieu44800 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/12/2010, 13h58   #5
Invité de passage
 
Étudiant
Inscription : avril 2009
Messages : 11
Détails du profil
Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : avril 2009
Messages : 11
Points : 0
Points : 0
Toute mes excuses j'ai posté tard hier et j'ai oublié de donner les liens ^^

Alors j'ai suivi ce tuto pour le framework MVC

http://julien-pauli.developpez.com/t...vc-controleur/

Pour mon composite pattern je me suis très fortement basé sur
http://www.devshed.com/c/a/PHP/PHP-C...esign-Pattern/

ma classe AbstractView
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<?php
/* 
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
 
abstract class AbstractView
{
    protected $_template = 'default';
    protected $_properties = array();
 
    public function __construct($template = '', array $data = array())
    {
        if ($template !== '') {
            $this->setTemplate($template);
        }
        if (!empty($data)) {
            foreach ($data as $name => $value) {
                $this->$name = $value;
            }
        }
    }
    public function setTemplate($template)
    {
        $template = $template . '.php';
        if (!file_exists($template)) {
            throw new ViewException('The specified view template does not exist.');
        }
        $this->_template = $template;
    }
    public function getTemplate()
    {
        return $this->_template;
    }
 
    public function __set($name, $value)
    {
        $this->_properties[$name] = $value;
    }
 
    // get the specified property from the view
    public function __get($name)
    {
        if (!isset($this->_properties[$name])) {
            throw new ViewException('The requested property is not valid for this view.');
        }
        return $this->_properties[$name];
    }
 
    // remove the specified property from the view
    public function __unset($name)
    {
        if (isset($this->_properties[$name])) {
            unset($this->_properties[$name]);
        }
    }
 
    // add a new view (implemented by view subclasses)
    abstract public function addView(AbstractView $view);
 
    // remove a view (implemented by view subclasses)
    abstract public function removeView(AbstractView $view);
 
    // render the view template
    public function render()
    {
        if ($this->_template !== '') {
           extract($this->_properties);
           ob_start();
           require($this->_template);
           $str = ob_get_contents();
           ob_get_clean();
           return $str;
        }
    }
}
?>
Ma classe CompositeView
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php
 
class CompositeView extends AbstractView
{
    protected $_views = array();
 
    // factory method (chainable)
    public static function factory($view, array $data = array())
    {
        return new self($viewfile, $data);
    }
 
    // add a new view object
    public function addView(AbstractView $view)
    {
        if (!in_array($view, $this->_views, TRUE)) {
            $this->_views[] = $view;
        }
        return $this;
    }
 
    // remove an existing view object
    public function removeView(AbstractView $view)
    {
        if (in_array($view, $this->_views, TRUE)) {
            $views = array();
            foreach ($this->_views as $_view) {
                if ($_view !== $view) {
                    $views[] = $_view;
                }
            }
            $this->_views = $views;
        }
        return $this;
    }
 
    // render each partial view (leaf) and optionally the composite view
    public function render()
    {
        $innerView = '';
        if (!empty($this->_views)) {
            foreach ($this->_views as $view) {
                $innerView .= $view->render();
            }
            $this->content = $innerView;
        }
        $compositeView = parent::render();
        return !empty($compositeView) ? $compositeView : $innerView;
    }
}
?>

Ma classe SimpleView

Code php :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
/* 
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
 
class SimpleView
{
 
    public function addView(AbstractView $view)
    {
        throw new ViewException('A simple view cannot add another view.');
    }
 
    public function removeView(AbstractView $view)
    {
        throw new ViewException('A simple view cannot remove another view.');
    }
 
}
?>

Dan le tuto MVC il s'agit d'un Template Pattern et la classe Response s'occupe des variables de la vue( __set(), __get() ), hors avec le composite View chaque vue s'occupe elle même de ses variables. Comment lier le ActionControlller au composite ? Car j'ai une CompositeView qui correspond à mon Layout principal puis les vues qui le compose.

Merci beaucoup
NearZero est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/12/2010, 14h38   #6
Membre éclairé
 
Inscription : août 2007
Messages : 360
Détails du profil
Informations forums :
Inscription : août 2007
Messages : 360
Points : 334
Points : 334
Tu ne donnes toujours pas le code de ton BaseController ni de ta classe Response, j'ai vraiment du mal à cerner ton problème...
mathieu44800 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/12/2010, 15h02   #7
Invité de passage
 
Étudiant
Inscription : avril 2009
Messages : 11
Détails du profil
Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : avril 2009
Messages : 11
Points : 0
Points : 0
Voici mon ActionController:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<?php
/* 
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
 
class ActionController
{
    protected $_request;
    protected $_response;
    protected $_redirected;
 
    private $_filters = array();
 
    public function __construct(Request $request, Response $response)
    {
        $this->_request  = $request;
        $this->_response = $response;
        $this->_redirected = false;
 
    }
 
    public static function process(Request $request, Response $response)
    {               
        $parsed = $request->route();
 
        if (!file_exists($path = APP_DIR.'controllers'.DS.$parsed['module'].'Controller.php')){
            throw new ControllerNotFoundException ('Controller Not Found!');
        }
 
        require_once($path);
 
        $class = $parsed['module']. 'Controller';
        $controller = new $class($request, $response);
        return $controller->launch();
    }
 
    public function launch()
    {
        $uri = $this->_request->getParsedURI();
        $action = $uri['action'];
 
        if (!$this->_actionExists($action)){
 
            throw new ActionNotFoundException('Action Not Found!');
        }
        // prefiltering
 
        $this->$action();
 
        // postfiltering
 
        if (!$this->_redirected){
            $this->_render($action);
        }
        return $this->_response;
    }
 
    public static function processException(Request $request, Response $response, Exception $e)
    {
        $controller = new self($request, $response);
        return $controller->launchException($e);
    }
 
    private function _actionExists($action)
    {
        try{
 
            $method = new ReflectionMethod(get_class($this),$action);
            return ($method->isPublic() && !$method->isConstructor());
 
        }catch (Exception $e){
            return false;
        }
    }
 
    public function addFilter(Filterable $filter)
    {
        $this->_filters[] = $filter;
    }
 
    public function redirect($url)
    {
        if ($this->_redirected == true){
            throw new Exception('Une redirection a déja été demandée');
        }
        $this->_response->redirect($url);
        $this->_redirected = true;
    }
 
    private function _render($file)
    {
            /* Composite view*/
    }
 
    public function __get($param)
    {
        return $this->_response->getVar($param);
    }
 
    public function __set($name,$param)
    {
        $this->_response->addVar($name, $param);
    }
 
    public function launchException(Exception $e)
    {
        if ($e instanceof MVCException){
            $this->_render('404.phtml');
        }else{
            $this->_render('500.php');
        }
        return $this->_response;
    }
}
?>

Ma classe Response
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
35
36
37
38
39
40
41
42
43
44
45
46
<?php
class Response
{
    private $_vars = array();
    private $_headers = array();
    private $_body;
 
    public function addVar($key, $value)
    {
        $this->_vars[$key] = $value;
    }
 
    public function getVar($key)
    {
        return $this->_vars[$key];
    }
 
    public function getVars()
    {
        return $this->_vars;
    }
 
    public function setBody($value)
    {
        $this->_body = $value;
    }
 
    public function redirect($url, $permanent = false)
    {
        if ($permanent){
            $this->_headers['Status'] = '301 Moved Permanently';
        }else{
            $this->_headers['Status'] = '302 Found';
        }
        $this->_headers['location'] = $url;
    }
 
    public function printOut()
    {
        foreach ($this->_headers as $key => $value) {
            header($key. ':' . $value);
        }
        echo $this->_body;
    }
}
?>

Comment implémenter le fait que j'ai plusieurs vues pour un seul Layout dans mon ActionController ? Utilisation d'un ViewManager? je n'ai aucune idée de comment l'implémenter.

Avec un TemplateView je faisait par exemple
Code :
1
2
3
4
5
6
7
8
9
10
 
<?php
class FooController extends ActionController
{
        public function index()
        {
            $this->msg = "Hello World";
        }
}
?>
Ça ajoutais directement ma variable msg à ma classe Response via __set() de la classe ActionController

Mais maintenant j'ai plusieurs vues et la est mon problème. J'aimerai éviter de faire quelque chose du type.

Code php :
1
2
3
4
5
6
7
8
9
10
11
12
13
 
<?php
class FooController extends ActionController
{
        public function index()
        {
            $partiel = new SimpleView();
            $partiel->titre ="Un nouveau message";
            $this->CompositeView->addView($partiel);
            $this->msg = "Hello World";
        }
}
?>

Je ne sais pas trop si je suis clair, je m'en excuse

Encore merci de l’intérêt que vous portez à mon problème
NearZero est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/12/2010, 15h19   #8
Membre éclairé
 
Inscription : août 2007
Messages : 360
Détails du profil
Informations forums :
Inscription : août 2007
Messages : 360
Points : 334
Points : 334
As tu un seul layout pour chaques vues ou des layouts différents ?
mathieu44800 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/12/2010, 15h30   #9
Invité de passage
 
Étudiant
Inscription : avril 2009
Messages : 11
Détails du profil
Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : avril 2009
Messages : 11
Points : 0
Points : 0
Alors si ne me trompe pas, j'ai un layout par défaut qui contient header, footer, ensuite la dedans j'inclue mon template qui correspond à un CompositeView et qui contient donc plusieurs SimpleView.

Je vais essayer de mieux expliquer mon besoin.
Imaginons que je prenne pour exemple la page profile de Facebook elle est composé du wall personnel, liste d'amis, information personnel.

Maintenant supposons avec mon framework je reçois une adresse de type
http://mon_site_web/profile&id=1

profile correspond au nom de mon module qui est associé à un template profile qui correspond à CompositeView lui même composé de SimpleView ou d'autre CompositeView qui sont liste d'amis, information personnel... qui sont accessible via http://mon_site_web/listeamis par exemple.

Du coup il faudrait que je dispose d'un View manager chargé d'identifier les SimpleView et d'appeler les actions correspondantes

Désolé j’avoue avoir un peu de mal à expliquer mon problème de façon simple ^^
NearZero est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/12/2010, 18h42   #10
Membre éclairé
 
Inscription : août 2007
Messages : 360
Détails du profil
Informations forums :
Inscription : août 2007
Messages : 360
Points : 334
Points : 334
Je ne pense pas qu'une "view" doivent connaitre le controller, ça va a l'encontre du modèle vue controlleur...

C'est très abstrait ce que tu veux réaliser. J'ai toujours du mal à comprendre ou tu veux en venir !!!

J'essaye tout du moins de t'aider le plus que je peux...

Cordialement,

Mathieu
mathieu44800 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/12/2010, 19h16   #11
Invité de passage
 
Étudiant
Inscription : avril 2009
Messages : 11
Détails du profil
Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : avril 2009
Messages : 11
Points : 0
Points : 0
Bon je vais essayer de reprendre alors:

Avec un template pattern imaginons 2 classes
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
class HelloController extends ActionController
{
       public function index()
       {
           $this->msg ="HelloWorld";
       }
}
class BonjourController extends ActionController
{
       public function index()
       {
           $this->msg ="Bonjour le monde!";
       }
}
Respectivement chaque action est rattachée aux vues bonjour.html et hello.html

Lorsque j'appelle l'adresse http://monsite/bonjour/index en résumé ma classe FrontController crée un objet Request, Response,ActionController

Mon uri est dispatchée par mon objet Request, mon objet ActionController crée le controller correspondant et appel la fonction Index qui elle initialise ma variable $msg pour ma vue.Puis mon controller fait le rendu de celle-ci.

Bon c'est le fonctionnement de MVC.

Maintenant avec le pattern composite je veux créer une classe
Code :
1
2
3
4
5
6
7
8
 
class IndexController extends ActionController
{
        public function index()
        {
             $this->msg ="Tous les messages";
        }
}
Et ici j'aimerai que mon action soit rattachée à une vue index.html qui contienne bonjour.html et hello.html sauf que ici je n'est pas initialisé leurs variables $msg tous simplement car les actions index() des classes BonjourController et HelloController s'en charge.

Du coup mon problème est, en utilisant le pattern composite view comment implémenter une ViewManager utilisé dans une classe dérivant ActionController qui se chargerais de récupérer les vues composite et d'initialiser leurs variables en appelant les actions correspondants.

Bon je me rend compte que c'est toujours flou, peut être que je me prend la tête pour quelque chose de simple que n'arrive pas à expliquer.^^
NearZero est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/12/2010, 19h30   #12
Membre éclairé
 
Inscription : août 2007
Messages : 360
Détails du profil
Informations forums :
Inscription : août 2007
Messages : 360
Points : 334
Points : 334
Je ne comprends plus rien du tout, est-ce normal ??

Bases toi sur un framework existant (cake, symfony ou Zend)...

A+

Mathieu
mathieu44800 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 27/12/2010, 18h50   #13
Invité de passage
 
Étudiant
Inscription : avril 2009
Messages : 11
Détails du profil
Informations professionnelles :
Activité : Étudiant

Informations forums :
Inscription : avril 2009
Messages : 11
Points : 0
Points : 0
J'ai finalement réussi à faire quelque chose qui pourrait vous permettre de comprendre se que je désire.

J'implémente donc un pattern de composite view

Dans mon exemple j'ai trois vues: index.php, hello.php, bonjour.php.
Ma vue index.php est composée de hello et de bonjour

Mon controller
Code :
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
 
<?php
class HelloworldController extends ActionController
{
    public function index()
    {
        $partial_view = $this->getRessource('helloworld','helloworld','bonjour');
        $this->eng_msg = $partial_view;
 
        $partial_view = $this->getRessource('helloworld','helloworld','hello');
        $this->fr_msg = $partial_view;
 
        $this->msg = "Index function";
 
    }
    public function bonjour()
    {
        $this->msg = "Bonjour le monde!";
    }
 
    public function hello()
    {
        $this->msg = "Hello World!";
    }
 
}
?>
mon action controller
Code :
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
 
<?php
 
class ActionController extends Controller
{
    protected $_request;
    protected $_response;
    protected $_redirected;
 
    protected $_layoutManager;
 
    protected $_view;
    protected $_model;
 
    protected $_filters = array();
 
    public function __construct(Request $request, Response $response)
    {
        $this->_request  = $request;
        $this->_response = $response;
 
        $this->_redirected = false;
 
        $this->_layoutManager = new LayoutManager();
 
        $this->_model = null;
        $this->initView();
    }
 
    public static function process(Request $request, Response $response)
    {               
        $parsed = $request->route();
        if (!file_exists($path = CONTROLLER_DIR.DS.$parsed['module'].DS.$parsed['controller'].'.php')){
            throw new ControllerNotFoundException ('Controller Not Found!');
        }
        require_once($path);
 
        $class = ucfirst($parsed['controller']). 'Controller';
        $controller = new $class($request, $response);
        return $controller->launch();
    }
 
    public static function processException(Request $request, Response $response, Exception $e)
    {
        $controller = new self($request, $response);
        return $controller->launchException($e);
    }
    public function launch()
    {
        $action =  $this->_request->getActionName();
        if (!$this->_actionExists($action)){
 
            throw new ActionNotFoundException('Action Not Found!');
        }
        // prefiltering
        $this->$action();
 
        // postfiltering
 
        if (!$this->_redirected){
 
            $this->_render(null);
        }
        return $this->_response;
    }
    public function launchException(Exception $e)
    {
        if ($e instanceof MVCException){
            $this->_render('404.phtml');
        }else{
            $this->_render('500.php');
        }
        return $this->_response;
    }
    private function _actionExists($action)
    {
        try{
 
            $method = new ReflectionMethod(get_class($this),$action);
            return ($method->isPublic() && !$method->isConstructor());
 
        }catch (Exception $e){
            return false;
        }
    }
 
    public function addFilter(Filterable $filter)
    {
        $this->_filters[] = $filter;
    }
    private function _render($file)
    {
        $this->_response->appendBody(
            $this->_view->render()
        );
    }
 
    public function __get($param)
    {                
        return $this->_view->$param;
    }
 
    public function __set($tpl_var,$value)
    {
        if(is_a($value, "AbstractView"))
        {
           $this->_view->addView($tpl_var,$value);
        }else{
           $this->_view->assign($tpl_var, $value);
        }
    }
    public function initView()
    {
        $module  = $this->_request->getModuleName();
        $controller = $this->_request->getControllerName();
        $action = $this->_request->getActionName();
 
        $baseDir = VIEW_DIR.DS.$module;
 
        if (!file_exists($baseDir) || !is_dir($baseDir)) {
            throw new ControllerException('Missing base view directory ("' . $baseDir . '")');
        }
        $this->_view = new CompositeView(array('basePath'=>$baseDir,'name'=>$controller,'action'=>$action));
        return $this->_view;
    }
    protected function _setParam($paramName, $value)
    {
        $this->_request->setParam($paramName, $value);
 
        return $this;
    }
    protected function _hasParam($paramName)
    {
        return null !== $this->_request->getParam($paramName);
    }
    protected function _getAllParams()
    {
        return $this->_request->getParams();
    }
    public function redirect($url)
    {
        if ($this->_redirected == true){
            throw new Exception('Une redirection a déja été demandée');
        }
        $this->_response->redirect($url);
        $this->_redirected = true;
    }
    public function getRessource($module,$controller,$action)
    {
        $this->_request->setModuleName($module);
        $this->_request->setActionName($action);
        $this->_request->getControllerName($controller);
        $classname = $controller.'Controller';
        $class = new $classname($this->_request,$this->_response);
        $class->$action();
        return $class->_view;
    }
}
?>
Maintenant j'aimerais savoir comment améliorer mon système, ma méthode getRessource n'est pas vrément très propre, elle modifie l'objet requête de mon controller courrant et cela est plutôt problèmatique.


Merci de votre aide.
NearZero est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 06h19.


 
 
 
 
Partenaires

Hébergement Web