Sie sind auf Seite 1von 16

Verndicedecontenidosdellibro

2.1.ElpatrnMVC
SymfonyestbasadoenunpatrnclsicodeldiseowebconocidocomoarquitecturaMVC,
queestformadoportresniveles:
ElModelorepresentalainformacinconlaquetrabajalaaplicacin,esdecir,sulgicade
negocio.
LaVistatransformaelmodeloenunapginawebquepermitealusuariointeractuarcon
ella.
ElControladorseencargadeprocesarlasinteraccionesdelusuarioyrealizaloscambios
apropiadosenelmodelooenlavista.
LaFigura21ilustraelfuncionamientodelpatrnMVC.
LaarquitecturaMVCseparalalgicadenegocio(elmodelo)ylapresentacin(lavista)por
loqueseconsigueunmantenimientomssencillodelasaplicaciones.Siporejemplouna
mismaaplicacindebeejecutarsetantoenunnavegadorestndarcomoununnavegadorde
undispositivomvil,solamenteesnecesariocrearunavistanuevaparacadadispositivo
manteniendoelcontroladoryelmodelooriginal.Elcontroladorseencargadeaislaral
modeloyalavistadelosdetallesdelprotocoloutilizadoparalaspeticiones(HTTP,consola
decomandos,email,etc.).Elmodeloseencargadelaabstraccindelalgicarelacionada
conlosdatos,haciendoquelavistaylasaccionesseanindependientesde,porejemplo,el
tipodegestordebasesdedatosutilizadoporlaaplicacin.

Figura2.1ElpatrnMVC

2.1.1.LascapasdelaarquitecturaMVC
ParapoderentenderlasventajasdeutilizarelpatrnMVC,sevaatransformaruna
aplicacinsimplerealizadaconPHPenunaaplicacinquesiguelaarquitecturaMVC.Un
buenejemploparailustrarestaexplicacineseldemostrarunalistaconlasltimasentradas
oartculosdeunblog.
2.1.1.1.Programacinsimple
UtilizandosolamentePHPnormalycorriente,elscriptnecesarioparamostrarlosartculos
almacenadosenunabasededatossemuestraenelsiguientelistado:
Listado21Unscriptsimple

<?php

//Conectarconlabasededatosyseleccionarla
$conexion=mysql_connect('localhost','miusuario','micontrasena')
mysql_select_db('blog_db',$conexion)

//EjecutarlaconsultaSQL
$resultado=mysql_query('SELECTfecha,tituloFROMarticulo',$conexion)

?>

<html>
<head>
<title>ListadodeArtculos</title>
</head>
<body>
<h1>ListadodeArtculos</h1>
<table>
<tr><th>Fecha</th><th>Titulo</th></tr>
<?php
//MostrarlosresultadosconHTML
while($fila=mysql_fetch_array($resultado,MYSQL_ASSOC))
{
echo"\t<tr>\n"
printf("\t\t<td>%s</td>\n",$fila['fecha'])
printf("\t\t<td>%s</td>\n",$fila['titulo'])
echo"\t</tr>\n"
}
?>
</table>
</body>
</html>

<?php

//Cerrarlaconexion
mysql_close($conexion)

?>

Elscriptanterioresfcildeescribiryrpidodeejecutar,peromuydifcildemantenery
actualizar.Losprincipalesproblemasdelcdigoanteriorson:
Noexisteproteccinfrenteaerrores(quocurresifallalaconexinconlabasede
datos?).
ElcdigoHTMLyelcdigoPHPestnmezcladosenelmismoarchivoeinclusoen

algunaspartesestnentrelazados.
ElcdigosolofuncionasilabasededatosesMySQL.
2.1.1.2.Separandolapresentacin
Lasllamadasaechoyprintfdellistado21dificultanlalecturadelcdigo.Dehecho,
modificarelcdigoHTMLdelscriptanteriorparamejorarlapresentacinesunfollndebido
acmoestprogramado.Asqueelcdigovaaserdivididoendospartes.Enprimerlugar,
elcdigoPHPpurocontodalalgicadenegocioseincluyeenelscriptdelcontrolador,
comosemuestraenellistado22.
Listado22Lapartedelcontrolador,enindex.php
<?php

//Conectarconlabasededatosyseleccionarla
$conexion=mysql_connect('localhost','miusuario','micontrasena')
mysql_select_db('blog_db',$conexion)

//EjecutarlaconsultaSQL
$resultado=mysql_query('SELECTfecha,tituloFROMarticulo',$conexion)

//Crearelarraydeelementosparalacapadelavista
$articulos=array()
while($fila=mysql_fetch_array($resultado,MYSQL_ASSOC))
{
$articulos[]=$fila
}

//Cerrarlaconexin
mysql_close($conexion)

//Incluirlalgicadelavista
require('vista.php')

ElcdigoHTML,quecontieneciertocdigoPHPamododeplantilla,sealmacenaenel
scriptdelavista,comosemuestraenellistado23.
Listado23Lapartedelavista,envista.php

<html>
<head>
<title>ListadodeArtculos</title>
</head>
<body>
<h1>ListadodeArtculos</h1>
<table>
<tr><th>Fecha</th><th>Ttulo</th></tr>
<?phpforeach($articulosas$articulo):?>
<tr>
<td><?phpecho$articulo['fecha']?></td>
<td><?phpecho$articulo['titulo']?></td>
</tr>
<?phpendforeach?>
</table>
</body>
</html>

Unabuenareglageneralparadeterminarsilapartedelavistaestsuficientementelimpiade
cdigoesquedeberacontenerunacantidadmnimadecdigoPHP,lasuficientecomopara
queundiseadorHTMLsinconocimientosdePHPpuedaentenderla.Lasinstruccionesms
comunesenlapartedelavistasuelenserecho,if/endif,foreach/endforeachypoco
ms.Adems,nosedebenincluirinstruccionesPHPquegenerenetiquetasHTML.
Todalalgicasehacentralizadoenelscriptdelcontrolador,quesolamentecontienecdigo
PHPyningntipodeHTML.Dehecho,ycomopuedesimaginar,elmismocontroladorse
puedereutilizarparaotrostiposdepresentacionescompletamentediferentes,comopor
ejemplounarchivoPDFounaestructuradetipoXML.
2.1.1.3.Separandolamanipulacindelosdatos
Lamayorpartedelscriptdelcontroladorseencargadelamanipulacindelosdatos.Pero,
quocurresisenecesitalalistadeentradasdelblogparaotrocontrolador,porejemplouno
quesededicaagenerarelcanalRSSdelasentradasdelblog?Ysisequierencentralizar
todaslasconsultasalabasededatosenunnicositioparaevitarduplicidades?Qu
ocurresicambiaelmodelodedatosylatablaarticulopasaallamarsearticulo_blog?Y
sisequierecambiaraPostgreSQLenvezdeMySQL?Parapoderhacertodoesto,es
imprescindibleeliminardelcontroladortodoelcdigoqueseencargadelamanipulacinde
losdatosyponerloenotroscript,llamadoelmodelo,talycomosemuestraenellistado24.
Listado24Lapartedelmodelo,enmodelo.php

<?php

functiongetTodosLosArticulos()
{
//Conectarconlabasededatosyseleccionarla
$conexion=mysql_connect('localhost','miusuario','micontrasena')
mysql_select_db('blog_db',$conexion)

//EjecutarlaconsultaSQL
$resultado=mysql_query('SELECTfecha,tituloFROMarticulo',$conexion)

//Crearelarraydeelementosparalacapadelavista
$articulos=array()
while($fila=mysql_fetch_array($resultado,MYSQL_ASSOC))
{
$articulos[]=$fila
}

//Cerrarlaconexin
mysql_close($conexion)

return$articulos
}

Elcontroladormodificadosepuedeverenellistado25.
Listado25Lapartedelcontrolador,modificada,enindex.php
<?php

//Incluirlalgicadelmodelo
require_once('modelo.php')

//Obtenerlalistadeartculos
$articulos=getTodosLosArticulos()

//Incluirlalgicadelavista
require('vista.php')

Ahoraelcontroladoresmuchomsfcildeleer.Sunicatareaesladeobtenerlosdatos
delmodeloypasrselosalavista.Enlasaplicacionesmscomplejas,elcontroladorse
encargaademsdeprocesarlaspeticiones,lassesionesdelosusuarios,laautenticacin,
etc.Elusodenombresapropiadosparalasfuncionesdelmodelohacenqueseainnecesario
aadircomentariosalcdigodelcontrolador.

Elscriptdelmodelosolamenteseencargadelaccesoalosdatosypuedeserreorganizadoa
talefecto.Todoslosparmetrosquenodependendelacapadedatos(comoporejemplolos
parmetrosdelapeticindelusuario)sedebenobteneratravsdelcontroladoryportanto,
nosepuedeaccederaellosdirectamentedesdeelmodelo.Lasfuncionesdelmodelose
puedenreutilizarfcilmenteenotroscontroladores.
2.1.2.SeparacinencapasmsalldelMVC
ElprincipiomsimportantedelaarquitecturaMVCeslaseparacindelcdigodelprograma
entrescapas,dependiendodesunaturaleza.Lalgicarelacionadaconlosdatosseincluye
enelmodelo,elcdigodelapresentacinenlavistaylalgicadelaaplicacinenel
controlador.
Laprogramacinsepuedesimplificarsiseutilizanotrospatronesdediseo.Deestaforma,
lascapasdelmodelo,lavistayelcontroladorsepuedensubidividirenmscapas.
2.1.2.1.Abstraccindelabasededatos
Lacapadelmodelosepuededividirenlacapadeaccesoalosdatosyenlacapade
abstraccindelabasededatos.Deestaforma,lasfuncionesqueaccedenalosdatosno
utilizansentenciasniconsultasquedependendeunabasededatos,sinoqueutilizanotras
funcionespararealizarlasconsultas.As,sisecambiadesistemagestordebasesdedatos,
solamenteesnecesarioactualizarlacapadeabstraccindelabasededatos.
Ellistado26muestraunejemplodecapadeabstraccindelabasededatosyellistado27
muestraunacapadeaccesoadatosespecficaparaMySQL.
Listado26Lapartedelmodelocorrespondientealaabstraccindelabasededatos

<?php

functioncrear_conexion($servidor,$usuario,$contrasena)
{
returnmysql_connect($servidor,$usuario,$contrasena)
}

functioncerrar_conexion($conexion)
{
mysql_close($conexion)
}

functionconsulta_base_de_datos($consulta,$base_datos,$conexion)
{
mysql_select_db($base_datos,$conexion)

returnmysql_query($consulta,$conexion)
}

functionobtener_resultados($resultado)
{
returnmysql_fetch_array($resultado,MYSQL_ASSOC)
}

Listado27Lapartedelmodelocorrespondientealaccesoalosdatos
functiongetTodosLosArticulos()
{
//Conectarconlabasededatos
$conexion=crear_conexion('localhost','miusuario','micontrasena')

//EjecutarlaconsultaSQL
$resultado=consulta_base_de_datos('SELECTfecha,tituloFROMarticulo','b
log_db',$conexion)

//Crearelarraydeelementosparalacapadelavista
$articulos=array()
while($fila=obtener_resultados($resultado))
{
$articulos[]=$fila
}

//Cerrarlaconexin
cerrar_conexion($conexion)

return$articulos
}

Comosepuedecomprobar,lacapadeaccesoadatosnocontienefuncionesdependientes
deningnsistemagestordebasesdedatos,porloqueesindependientedelabasede
datosutilizada.Adems,lasfuncionescreadasenlacapadeabstraccindelabasede
datossepuedenreutilizarenotrasfuncionesdelmodeloquenecesitenaccederalabasede
datos.
Losejemplosdeloslistados26y27nosoncompletos,ytodavahacefaltaaadir
algodecdigoparatenerunacompletaabstraccindelabasededatos(abstraerel
cdigoSQLmedianteunconstructordeconsultasindependientedelabasededatos,
aadirtodaslasfuncionesaunaclase,etc.)Elpropsitodeestelibronoesmostrarcmo
sepuedeescribirtodoesecdigo,yaqueenelcaptulo8semuestracmoSymfony
realizadeformaautomticatodalaabstraccin.
NOTA

2.1.2.2.Loselementosdelavista
Lacapadelavistatambinpuedeaprovecharlaseparacindecdigo.Laspginasweb
suelencontenerelementosquesemuestrandeformaidnticaalolargodetodala
aplicacin:cabecerasdelapgina,ellayoutgenrico,elpiedepginaylanavegacin
global.Normalmenteslocambiaelinteriordelapgina.Porestemotivo,lavistasesepara
enunlayoutyenunaplantilla.Normalmente,ellayoutesglobalentodalaaplicacinoal
menosenungrupodepginas.Laplantillasloseencargadevisualizarlasvariables
definidasenelcontrolador.Paraqueestoscomponentesinteraccionenentres
correctamente,esnecesarioaadirciertocdigo.Siguiendoestosprincipios,lapartedela
vistadellistado23sepuedesepararentrespartes,comosemuestraenloslistados28,2
9y210.
Listado28Lapartedelaplantilladelavista,enmiplantilla.php
<h1>ListadodeArtculos</h1>
<table>
<tr><th>Fecha</th><th>Ttulo</th></tr>
<?phpforeach($articulosas$articulo):?>
<tr>
<td><?phpecho$articulo['fecha']?></td>
<td><?phpecho$articulo['titulo']?></td>
</tr>
<?phpendforeach?>
</table>

Listado29Lapartedelalgicadelavista
<?php

$titulo='ListadodeArtculos'
$articulos=getTodosLosArticulos()

Listado210Lapartedellayoutdelavista
<html>
<head>
<title><?phpecho$titulo?></title>
</head>
<body>
<?phpinclude('miplantilla.php')?>
</body>
</html>

2.1.2.3.Accionesycontroladorfrontal
Enelejemploanterior,elcontroladornoseencargabaderealizarmuchastareas,peroenlas
aplicacioneswebrealeselcontroladorsueletenermuchotrabajo.Unaparteimportantedesu
trabajoescomnatodosloscontroladoresdelaaplicacin.Entrelastareascomunesse
encuentranelmanejodelaspeticionesdelusuario,elmanejodelaseguridad,cargarla
configuracindelaaplicacinyotrastareassimilares.Porestemotivo,elcontrolador
normalmentesedivideenuncontroladorfrontal,queesnicoparacadaaplicacin,ylas
acciones,queincluyenelcdigoespecficodelcontroladordecadapgina.
Unadelasprincipalesventajasdeutilizaruncontroladorfrontalesqueofreceunpuntode
entradanicoparatodalaaplicacin.As,encasodequeseanecesarioimpedirelaccesoa
laaplicacin,solamenteesnecesarioeditarelscriptcorrespondientealcontroladorfrontal.Si
laaplicacinnodisponedecontroladorfrontal,sedeberamodificarcadaunodelos
controladores.
2.1.2.4.Orientacinaobjetos
Losejemplosanterioresutilizanlaprogramacinprocedimental.Lasposibilidadesque
ofrecenloslenguajesdeprogramacinmodernosparatrabajarconobjetospermiten
simplificarlaprogramacin,yaquelosobjetospuedenencapsularlalgica,puedenheredar
mtodosyatributosentrediferentesobjetosyproporcionanunaseriedeconvencionesclaras
sobrelaformadenombraralosobjetos.
LaimplementacindeunaarquitecturaMVCenunlenguajedeprogramacinquenoest
orientadoaobjetospuedeencontrarseconproblemasdenamespacesycdigoduplicado,
dificultandolalecturadelcdigodelaaplicacin.
Laorientacinaobjetospermitealosdesarrolladorestrabajarconobjetosdelavista,objetos
delcontroladoryclasesdelmodelo,transformandolasfuncionesdelosejemplosanteriores
enmtodos.SetratadeunrequisitoobligatorioparalasarquitecturasdetipoMVC.
Siquieresprofundizareneltemadelospatronesdediseoparalasaplicaciones
webenelcontextodelaorientacinaobjetos,puedesleer"PatternsofEnterprise
ApplicationArchitecture"deMartinFowler(AddisonWesley,ISBN:0321127420).El
NOTA

cdigodeejemplodellibrodeFowlerestescritoenJavayenC#,peroesbastantefcil
deleerparalosprogramadoresdePHP.
2.1.3.LaimplementacindelMVCquerealizaSymfony
Piensaporunmomentocuntoscomponentessenecesitaranpararealizarunapgina
sencillaquemuestreunlistadodelasentradasoartculosdeunblog.Comosemuestraen
lafigura22,sonnecesarioslossiguientescomponentes:
LacapadelModelo
Abstraccindelabasededatos
Accesoalosdatos
LacapadelaVista
Vista
Plantilla
Layout
LacapadelControlador
Controladorfrontal
Accin
Entotalsonsietescripts,loqueparecenmuchosarchivosparaabrirymodificarcadavez
quesecreaunapgina.Afortunadamente,Symfonysimplificaesteproceso.Symfonytoma
lomejordelaarquitecturaMVCylaimplementadeformaqueeldesarrollodeaplicaciones
searpidoysencillo.
Enprimerlugar,elcontroladorfrontalyellayoutsoncomunesparatodaslasaccionesdela
aplicacin.Sepuedentenervarioscontroladoresyvarioslayouts,perosolamentees
obligatoriotenerunodecada.Elcontroladorfrontalesuncomponentequeslotienecdigo
relativoalMVC,porloquenoesnecesariocrearuno,yaqueSymfonylogeneradeforma
automtica.
Laotrabuenanoticiaesquelasclasesdelacapadelmodelotambinsegeneran
automticamente,enfuncindelaestructuradedatosdelaaplicacin.ElORMseencarga
decrearelesqueletooestructurabsicadelasclasesygeneraautomticamentetodoel
cdigonecesario.CuandoelORMencuentrarestriccionesdeclavesforneas(oexternas)o
cuandoencuentradatosdetipofecha,creamtodosespecialesparaaccederymodificar
esosdatos,porloquelamanipulacindedatosseconvierteenunjuegodenios.La
abstraccindelabasededatosescompletamentetransparenteparaelprogramador,yaque

serealizadeformanativamediantePDOPHPDataObjects).As,sisecambiaelsistema
gestordebasesdedatosencualquiermomento,nosedebereescribirniunalneade
cdigo,yaquetansloesnecesariomodificarunparmetroenunarchivodeconfiguracin.
Porltimo,lalgicadelavistasepuedetransformarenunarchivodeconfiguracinsencillo,
sinnecesidaddeprogramarla.

Figura2.2ElflujodetrabajodeSymfony
Considerandotodoloanterior,elejemplodelapginaquemuestraunlistadocontodaslas
entradasdelblogsolamenterequieredetresarchivosenSymfony,quesemuestranenlos
listados211,212y213.
Listado211Accinlistado,en
miproyecto/apps/miaplicacion/modules/weblog/actions/actions.class.php
<?php

classweblogActionsextendssfActions
{
publicfunctionexecuteListado()
{
$this>articulos=ArticuloPeer::doSelect(newCriteria())
}
}

Listado212Plantillalistado,en
miproyecto/apps/miaplicacion/modules/weblog/templates/listadoSuccess.php
<?phpslot('titulo','ListadodeArtculos')?>

<h1>ListadodeArtculos</h1>
<table>
<tr><th>Fecha</th><th>Ttulo</th></tr>
<?phpforeach($articulosas$articulo):?>
<tr>
<td><?phpecho$articulo>getFecha()?></td>
<td><?phpecho$articulo>getTitulo()?></td>
</tr>
<?phpendforeach?>
</table>

Ademsesnecesariocrearunlayoutcomoeldellistado213.Afortunadamente,elmismo
layoutsepuedereutilizarmuchasveces.
Listado213Layout,enmiproyecto/apps/miaplicacion/templates/layout.php
<html>
<head>
<title><?phpinclude_slot('titulo')?></title>
</head>
<body>
<?phpecho$sf_content?>
</body>
</html>

Estosscriptssontodoloquenecesitalaaplicacindelejemplo.Elcdigomostradoesel
necesarioparacrearlamismapginaquegenerabaelscriptsimpledellistado21.Symfony
seencargadelrestodetareas,comohacerqueloscomponentesinteractuenentres.Sise
consideraelnmerodelneasdecdigo,ellistadodeentradasdeblogcreadosegnla
arquitecturaMVCnorequieremslneasnimstiempodeprogramacinqueunscript
simple.Sinembargo,laarquitecturaMVCproporcionagrandesventajas,comola
organizacindelcdigo,lareutilizacin,laflexibilidadyunaprogramacinmuchoms
entretenida.Porsifuerapoco,crearlaaplicacinconSymfonypermitecrearpginasXHTML
vlidas,depurarfcilmentelasaplicaciones,crearunaconfiguracinsencilla,abstraccinde
labasededatosutilizada,enrutamientoconURLlimpias,variosentornosdedesarrolloy
muchasotrasutilidadesparaeldesarrollodeaplicaciones.
2.1.4.LasclasesqueformanelncleodeSymfony
LaimplementacinquerealizaSymfonydelaarquitecturaMVCincluyevariasclasesquese
mencionanunayotravezalolargodellibro:

sfControllereslaclasedelcontrolador.Seencargadedecodificarlapeticiny

transferirlaalaaccincorrespondiente.
sfRequestalmacenatodosloselementosqueformanlapeticin(parmetros,cookies,

cabeceras,etc.)
sfResponsecontienelascabecerasdelarespuestayloscontenidos.Elcontenidodeeste

objetosetransformaenlarespuestaHTMLqueseenvaalusuario.
Elcontexto(queseobtienemediantesfContext::getInstance())almacenauna
referenciaatodoslosobjetosqueformanelncleodeSymfonyypuedeseraccedido
desdecualquierpuntodelaaplicacin.
Elcaptulo6explicaendetalletodosestosobjetos.
Comosehavisto,todaslasclasesdeSymfonyutilizanelprefijosf,comotambinhacen
todaslasvariablesprincipalesdeSymfonyenlasplantillas.Deestaforma,seevitanlas
colisionesenlosnombresdeclasesyvariablesdeSymfonyylosnombresdetuspropias
clasesyvariables,ademsdequelasclasesdelframeworksonmsfcilesdereconocer.
EntrelasnormasseguidasporelcdigodeSymfony,seencuentraelestndar
"UpperCamelCase"paraelnombredelasclasesyvariables.Solamenteexistendos
excepciones:lasclasesdelncleodeSymfonyempiezanporsf(portantoenminsculas)
ylasvariablesutilizadasenlasplantillasqueutilizanlasintaxisdesepararlaspalabras
conguionesbajos.
NOTA

NotadeltraductorLanotacin"CamelCase"consisteenescribirfrasesopalabras
compuestaseliminandolosespaciosintermediosyponiendoenmaysculalaprimeraletra
decadapalabra.Lavariante"UpperCamelCase"tambinponeenmaysculalaprimera
letradetodas.

Anterior
Captulo2.ExplorandoelinteriordeSymfony

(../capitulo_2.html)

Siguiente
2.2.Organizacindelcdigo

(../capitulo_2/organizacion_del_codigo.html)

COMPARTIR

Twitter(https://twitter.com/share?text={{item_social_title}}&lang=es)
Facebook(http://www.facebook.com/sharer.php?
u=http%3A%2F%2Flibrosweb.es%2Flibro%2Fsymfony_1_4%2Fcapitulo_2%2Fel_patron_mvc.html&t={{
item_social_title}})
Google+(https://plus.google.com/share?
url=http%3A%2F%2Flibrosweb.es%2Flibro%2Fsymfony_1_4%2Fcapitulo_2%2Fel_patron_mvc.html&hl=es)

INDICEDECONTENIDOS

1.IntroduccinaSymfony(../capitulo_1.html)
Captulo2.ExplorandoelinteriordeSymfony(../capitulo_2.html)
2.1.ElpatrnMVC(../capitulo_2/el_patron_mvc.html)
2.2.Organizacindelcdigo(../capitulo_2/organizacion_del_codigo.html)
2.3.Herramientascomunes(../capitulo_2/herramientas_comunes.html)
2.4.Resumen(../capitulo_2/resumen_2.html)
3.EjecutaraplicacionesSymfony(../capitulo_3.html)
4.Introduccinalacreacindepginas(../capitulo_4.html)
5.ConfigurarSymfony(../capitulo_5.html)
6.ElControlador(../capitulo_6.html)
7.LaVista(../capitulo_7.html)
8.Elmodelo(Doctrine)(../capitulo_8.html)
9.Enlacesysistemadeenrutamiento(../capitulo_9.html)
10.Formularios(../capitulo_10.html)
11.Emails(../capitulo_11.html)
12.Usodelacache(../capitulo_12.html)
13.Internacionalizacinylocalizacin(../capitulo_13.html)
14.Generadordelapartedeadministracin(../capitulo_14.html)
15.Pruebasunitariasyfuncionales(../capitulo_15.html)
16.Herramientasparalaadministracindeaplicaciones(../capitulo_16.html)
17.PersonalizarSymfony(../capitulo_17.html)
18.Rendimiento(../capitulo_18.html)
19.Configuracinavanzada(../capitulo_19.html)

2015LibrosWeb.es
3.263dasonline

Das könnte Ihnen auch gefallen