Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Edgard Hufelande

Friendly URL no MVC

Recommended Posts

Estou fazendo um site usando estrutura MVC, minha dúvida é com a Friendly URL é a seguinte.


Até o momento fiz um script para ler da seguinte forma.

 

www.site.com/controller/action


Até aí foi fácil, mas pretendo fazer uma página de perfil de usuários usando a seguinte url

 

www.site.com/user ou profile/login_do_usuario

 

<?php
    class System {
        
        private $_uri;
        private $_controller;
        private $_action;
        
        public function __construct() {
            $this->setURI();
            $this->setController();   
            $this->setAction();
        }
        
        public function setURI(){
            $this->_uri = explode('/', $_SERVER['REQUEST_URI']);
            $this->_uri = array_slice($this->_uri, 1);
        }
        
        public function setController() {
            if ($this->_uri[0]) {
                $this->_controller = $this->_uri[0];
            }
        }
        
        public function setAction() {
            if ($this->_uri[1]){
                $this->_action = $this->_uri[1];
            }
        }

        // Continua código...

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Basicamente terá que remover setController e setAction e criar novas implementações.

 

 

O problema nesse estilo "controller/action" é ficar limitado a dois parâmetros

 

Pense quando precisar fazer algo como "/adm/user/edit/history/search/[id]"

 

Há dois caminhos básicos

 

1. registrar os nomes dos módulos e controllers

É bem mais simples de gerenciar, porém, terá algumas limitações onde terá um pouco mais de trabalho registrando cada controlador.

Uma parte um pouco mais complicada é quando um registro for um folder ou um file.

 

2. montar o path conforme os parâmetros.

É trabalhoso e complicado, porém, torna o sistema mais flexível, não exigindo registro dos parâmetros referentes a módulos e controladores.

O ponto fraco é que realmente se torna mais trabalhoso e consome mais performance.

 

 

 

 

Parâmetro para Idioma

Para ambos os casos, imagine quando necessitar usar um parâmetro especial no início como se fosse um folder.

Um caso bem específico é um site multi-idiomas.

 

exemplo:


localhost/pt/controller/action

localhost/en/controller/action

localhost/ru/controller/action

 

e por aí vai.. e note que o SEO modifica para cada idioma.

 

vc não vai querer uma URL em russo assim:

 

localhost/ru/noticias/semanal

 

Nesse ponto, precisa ter suporte a aliases para parâmetros traduzidos para cada idioma.

 

 

Parâmetro para paginação

Imagine uma lista com paginação.

O parâmetro que representa a página, precisa estar sempre numa única posição. Aliás, todos os parâmetros de uma URL amigável precisam estar sempre numa única posição na query.

localhost/user/list/[page_number]/other_parameter

 

exemplo que geraria erro:

localhost/user/list/other_parameter/[page_number]

 

Numa URL normal, os parâmetros possuem seus respectivos índices, por isso, podem estar em qualquer posição na query

localhost/?module=user/list&other=other_parameter&page=[page_number]

 

 

Cannonical

É importante definir qual estilo de URL será canonical.

Se as páginas forem amigáveis por padrão, então todo o acesso por url não amigável deve possuir links para a sua versão "canonicalizada"

 

 

 

Enfim.. o assunto é bem complexo. Esses são os pontos mais básicos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Irei pesquisar sobre @hinom, grato pelas dicas!

 

---------------------------------------------------------------------------------------------------------------------

Editado

 

@hinom, e se eu fizesse a controller checar a primeira controller e daí tratar os dados de forma diferente?

Será que seria meio que idiotisse fazer isso?

 

exemplo:

        public function setController() {
            if ($this->_uri[0]) {
                $this->_controller = $this->_uri[0];
            } else {
                $this->_controller = 'default';
            }
        }
        
        public function setAction() {
            if ($this->_uri[1]){
                $this->_action = $this->_uri[1];
            } elseif ($this->_controller == 'user'){
                $this->_action = 'id';
            } else {
                $this->_action = 'index';
            }            
        }

 

Daí eu já daria um jeito de esquecer que a action existe na url e já comecaria a ler os parâmetros, algo assim.

 

O problema é que o sistema ficaria limitado caso eu fosse querer usar o sistema para outros projetos, mas não seria tão difícil de fazer as alterações nesse arquivo.

 

qccf.png

en9y.png

OlnCqe6.png

Compartilhar este post


Link para o post
Compartilhar em outros sites

A lógica é essa mesma.

O ponto chave é abstrair o trecho que identifica o controller ou módulo.

Identificado o controller, o restante são parâmetros.

 

Para facilitar o entendimento, veja como fica um exemplo usando a técnica na qual os nomes dos controladores são registrados

 

 

Uma técnica simples é fazer uma iteração no array $_uri

 

A idéia é ir montando o path até encontrar se o nome existe no array que contém o registro dos controladores.

 

Veja um exemplo:

 

$controllers = array( 				 'user' => array(							'name'      => 'User',							'namespace' => 'Controller',						),				 'adm/user' => array(							'name'      => 'User',							'namespace' => 'Controller\Adm',						),				 'user/profile/other' => array(							'name'      => 'Other',							'namespace' => 'Controller\User\Profile',						),			   );/*** Dummy URIs for debug purposes*///$uri_dummy   = 'users/1';//$uri_dummy   = 'user/1/2/3';//$uri_dummy   = 'user';//$uri_dummy   = 'user/';$uri_dummy   = 'user/profile/other/1/2/3';/*** Converting the parts to array*/$uri         = explode( '/', $uri_dummy );/*** This variable will handle the final result.* If nothing was found, will keep as null.*/$path        = null;if( isset( $controllers[$uri_dummy] ) ){	/**	* Found exact key	*/	$path = $controllers[$uri_dummy];	$path['parameters'] = array( $uri_dummy );}else{	if( count( $uri ) > 1 )	{		/**		* Removing the last key.		*/		array_pop( $uri );		/**		* Must be carefull with thisg because $path may return null.		* The looping is interrupted by some conditionals inside it.		*/		while( empty( $path ) )		{			/**			* Mounting the key name from array to string 			*/			$str = implode( '/', $uri );			/**			* Checking if key exists in $controllers			*/			if( isset( $controllers[$str] ) )			{				/**				* If the original URI string length is bigger than $str var, means that probably have others parameters.				*/				if( strlen( $uri_dummy ) > strlen( $str ) )				{					/**					* The extra parameters.					*/					$params    = explode( '/', substr( $uri_dummy, strlen( $str ) ) );					/**					* Assign the key name to the first key.					*/					$params[0] = $str;				}else{					/**					* Have no extra parameters.					* Assign the key name to the first key.					*/					$params = array( $str );				}				/**				* Assign the final array.				*/				$path = $controllers[$str];				$path['parameters'] = $params;				break;			}else{				/**				* Still not found.				* If array contains more then 1 key, continue interacting				*/				/**				* Removing the last key				*/				array_pop( $uri );				/**				* Nothing was found.				* Breaking the loop				*/				if( count( $uri ) <= 1 )				{					break;				}			}		}	}}//var_dump( $path );print_r( $path );

 

Note que é importante também conferir antes em algum array contendo aliases.

Os aliases devem ter precedência.

 

 

No caso na array $controller eu apenas adicionaria os que seriam por exemplo, user?

 

Eu estava pensando em fazer da seguinte forma, verificar se o controller e a action existe, caso não existir eu uso como user, colocando o código em um ___construct().

Compartilhar este post


Link para o post
Compartilhar em outros sites

particularmente aconselho a não fazer isso pois estaria criando uma gambiarra.

 

 

 

sobre o array controller do exemplo,

Não.. no array controller do exemplo acima pode-se definir qualquer outro nome

 

exemplo

 

$controllers = array( 				 'outro_nome_qualquer' => array(							'name'      => 'NomeDaClasse',							'namespace' => 'NameSpace\do\Modulo',						),			   );

acho que te confundi um pouco adicionando uns recursos...

só achei melhor não postar algo muito simplório..

mas tente entender a estrutura.. está tudo num nível bem simples.

Atente-se de que há mais detalhes para implementar.. isso é apenas um exemplo.

 

 

#6 show de bola!

Compartilhar este post


Link para o post
Compartilhar em outros sites

particularmente aconselho a não fazer isso pois estaria criando uma gambiarra.

 

 

 

sobre o array controller do exemplo,

Não.. no array controller do exemplo acima pode-se definir qualquer outro nome

 

exemplo

 

$controllers = array( 
				 'outro_nome_qualquer' => array(
							'name'      => 'NomeDaClasse',
							'namespace' => 'NameSpace\do\Modulo',
						),

			   );

 

acho que te confundi um pouco adicionando uns recursos...

só achei melhor não postar algo muito simplório..

mas tente entender a estrutura.. está tudo num nível bem simples.

Atente-se de que há mais detalhes para implementar.. isso é apenas um exemplo.

 

Como vou identificar a action se o resto são parâmetros?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Também preciso disso, mas com e array é só declarar seguindo seu exemplo no post #3

 

ficararia

 

return array(
					'user' => array(
								'name'      => 'User',
								'action'      => 'index',
								'namespace' => 'Controller',
							),
							
					'download' => array(
								'name'      => 'Download',
								'action'      => 'index',
								'namespace' => 'Controller',
							),
$uri_dummy   = 'download';

Acho que isso :pinch:

 

Vamos aguardar = hinom

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mano, to sentido falta dos parametros, que vem além do controller e do action.

 

Seria legal se você fizesse algo assim:

 

$nomeControllerDinamico = Router::getController();

$objetoController = new $nomeControllerDinamico;

$action = Router::getAction();

$params = Router::getParams();

 

call_user_func_array(array($objetoController,$action),$params);

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mano, to sentido falta dos parametros, que vem além do controller e do action.

 

Seria legal se você fizesse algo assim:

 

$nomeControllerDinamico = Router::getController();

$objetoController = new $nomeControllerDinamico;

$action = Router::getAction();

$params = Router::getParams();

 

call_user_func_array(array($objetoController,$action),$params);

É parei na parte dos parâmetros, pois preciso diferenciar de quando ele vai buscar a action ou buscar o parâmetro de quando a url for site.com.br/user/nome_do_user. rs

Compartilhar este post


Link para o post
Compartilhar em outros sites

No exemplo, temos esses esses parâmetros para teste (dummy uri)

 

/*** Dummy URIs for debug purposes*///$uri_dummy   = 'users/1';//$uri_dummy   = 'user/1/2/3';//$uri_dummy   = 'user';//$uri_dummy   = 'user/';$uri_dummy   = 'user/profile/other/1/2/3';

Vamos modificar um pouco a URI de exemplo para algo mais prático:

$uri_dummy = 'user/profile/other/edit/1';

 

Nesse exemplo, o controlador e o action são:

Controller: user/profile/other

Action: edit

 

Resultado:

Array(    [name] => Other    [namespace] => Controller\User\Profile    [parameters] => Array        (            [0] => user/profile/other            [1] => edit            [2] => 2            [3] => 3        ))

O índice 'name' é o nome do controller.

Os outros índices dentro do array 'parameters' sãos parâmetros restantes, os quais podem ser usados para quaisquer finalidades.

Caso queira um identificador para o "Action", pode usar o índice 1, por exemplo, tal como já estava sendo usado no post #1.

O índice ZERO do array 'parameters' é apenas para manter um controle do parâmetro original que chamou o controler.

 

 

Também pode adicionar o nome do action ao array definido em $controller, conforme exemplo no post #9.

Aí depende da sua política, do seu modelo de negócios.

 

Enquanto escrevia o exemplo, adicionei "namespace" para poder facilitar no momento de carregar o controlador.

Assim fica mais simples caso já esteja usando autoloading.

 

 

De forma mais práica,

Após obter o resultado, basta ler o array.

 

 

// print_r( $path );/*** Mounting the class name*/$c = '\\' . $path['namespace'] . '\\' . $path['namespace'];/*** Instantiating the class*/$clss = new $c;/*** Clearing object*/unset( $c );// daqui pra frente vc segue com o seu modelo de negócios.// por exemplo, se quer chamar o action ou qulquer outro método dentro desse controller:/*** Checking if action parameter exists*/if( isset( $path['parameters'][1] ) && method_exists( $clss, $path['parameters'][1] ) ){    /**    * Loading the action method    * ie: ClassName::ActionName()    */    $clss -> {$path['parameters'][1]}();}

Valeu hinom, seguindo seu exemplo deu tudo certo

 

load para namespace

 

spl_autoload_register(    function( $classname ) {        require_once str_replace( '\\', DIRECTORY_SEPARATOR, $classname ) . '.php';    });

code

 

$Router = new \Controller\Router;$UriDummy   = 'sac/ticket/edit';$Router->setUriDummy( $UriDummy );// Chama a função dentro do namespace$path = $Router -> router();$c = '\\' . $path['namespace'];/*** Instantiating the class*/$clss = new $c;/*** Clearing object*/unset( $c );// daqui pra frente você segue com o seu modelo de negócios.// por exemplo, se quer chamar o action ou qulquer outro método dentro desse controller:/*** Checking if action parameter exists*///$clss -> Ticket();//echo $path['parameters'][1];if( isset( $path['parameters'][1] ) && method_exists( $clss, $path['parameters'][1] ) ){    /**    * Loading the action method    * ie: ClassName::ActionName()    */	    $clss -> {$path['parameters'][1]}();}

:clap:

Compartilhar este post


Link para o post
Compartilhar em outros sites

Estou implementando no meu sistema, só que com forma diferente.

 

Uma dúvida fora do assunto, há algum problema ultrapassar as 10 linhas sugerida pelo Netbeans?

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.