Ir para conteúdo

Arquivado

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

Andrey Knupp Vital

Organização dos componentes

Recommended Posts

Bom gente, estou montando uma API de integração com o Twitter, na verdade a API só é auxiliar, pra não ter que ficar fazendo cURL com os headers emitidos pela classe que gera a assinatura OAuth válida pro twitter, enfim .. minha dúvida ainda não é essa

 

É mais pelo seguinte, cada serviço que eles oferecem, uns requerem autenticação, outros não .. até aí tudo bem, temos uma classe que faz a transição da autenticação que será usada nos objetos que fornecem métodos que precisam de autenticação no webservice, beleza .. então daí temos

 

Twitter.png

 

Ou seja ..

 

Teremos a entidade TwitterAuthentication, que será enviada para TwitterOAuthentication, que irá executar os métodos getters, colhendo as informações da aplicação no twitter previamente setadas e fornece o(s) métodos que são responsáveis por gerar a assinatura ( cabeçalho ) que será adicionado no Header quando a requisição for enviada para a API do Twitter .. sem problemas ..

 

Agora, a classe Twitter NÃO é dependente da OAuthentication, muito menos da TwitterAuthentication, pois em alguns dos serviços, como já falei, não é necessário a autenticação .. beleza também, pra isso tem o método setter, quando formos usar algo que requer autenticação, setamos o OAuthentication ..

 

Agora, essa classe Twitter deve ser uma classe abstrata pelo fato de fornecer vários métodos para uso das APIs ( ou seja ) getTimeLine , getDirectMessages .. sendo que cada getter é responsável por retornar uma classe responsável por trabalhar com esses serviços .. daí tem vários componentes .. beleza

 

O problema: os serviços fornecidos por cada API tem parâmetros que fazem a mesma coisa, mas a informação de entrada ( input ) é diferente, exemplo:

Twitter::getTimeLine()->getRetweetedByUser ( [ id ou nome de usuário ] ) 

 

Como outros parâmetros que podem ser adicionados, são vaaarios, como limite de resultados trazidos, incluir entidades ... etc , a pergunta é a seguinte, como deveria ser passado esses parâmetros ? uma pasta para cada API sendo Twitter/API/TimeLine/TimeLineParams.php, TimeLine.php .. etc

e/ou propriedades na classe TimeLine, o que eu acho feio ..

 

Também tenho dúvidas quanto a classe Twitter ser abstrata ou não ..

Compartilhar este post


Link para o post
Compartilhar em outros sites

Já tive problema semelhante, cheguei a usar array, mas não gostei porque pode confundir o programador e não define uma assinatura apropriada ao metódo, creio que isso vai ser útil: http://c2.com/cgi/wiki?ParameterObject

 

 

Pois é, eu acho que é uma boa idéia , afinal, é feio passar 'parâmetros' através de array, se tivermos um objeto que representa todos os parâmetros que podem ser passados para um determinado serviço ..

 

Mas ainda o ponto não é esse, é que os actions de um serviço tem parâmetros diferentes .. ou seja, alguns poderiam vir com valor nulos .. mas aí seria o caso de transformar todos os parâmetros em array e remover os vazios, ou criar vários ifs e ir montando a query string ( o que é um saco ) ?

Compartilhar este post


Link para o post
Compartilhar em outros sites
Também tenho dúvidas quanto a classe Twitter ser abstrata ou não ..

Abstrata por quê?

Veja, se você não terá filhas concretas dela, terá que se utilizar de métodos estáticos sempre.

 

Qual o problema disso?

E se houver a necessidade de você ter mais de um objeto Twitter na sua aplicação, como por exemplo, twittar automaticamente em 2 contas diferentes?

Você teria que fazer [simplificadamente]:

foreach($accounts as $each){
Twitter::setOAuthentication(new TwitterOAuthentication($each));
Twitter::doSomething();
}

 

Não sendo abstrata, você pode acabar com a natureza estática dela, fazendo algo assim:

$accounts = array(new Twitter(), new Twitter(), new Twitter());
foreach($accounts as $each){
$each->doSomething();
}

 

Além disso, tudo que é estático, é global. Tudo o que é global dificulta nossa vida na hora de debugar a aplicação...

Compartilhar este post


Link para o post
Compartilhar em outros sites

Abstrata por quê?

Veja, se você não terá filhas concretas dela, terá que se utilizar de métodos estáticos sempre.

 

Porque a twitter vai fornecer os métodos que recuperam os objetos da API, como:

 

Twitter::getTimeLine()->getHomeTimeLine()

 

Mas ainda tenho dúvidas quanto a isso .. pois é como você mesmo disse, terá de usar métodos estáticos, se houvesse uma maneira do OAuthentication ser um 'espelho' que reflete para todas as API's, não seria problema, mas de um jeito ou de outro, eu teria que re-passar a autenticação para os serviços que precisam ..

 

E se houver a necessidade de você ter mais de um objeto Twitter na sua aplicação, como por exemplo, twittar automaticamente em 2 contas diferentes?

 

Esse é outro problema , da mesma forma que eu vou iterar para pegar ambas autenticações, vou iterar para fazer ambas autenticações, a diferença só vai ser na criação do objeto ..

 

Tive uma idéia, trabalhar com um registry que permite adicionar inúmeras autenticações, daí na hora de emitir o header, basta verificar se o objeto recebido por parâmetro é uma instância de TwitterCredentialsStorage .. e então, iterar nesse objeto !

 

Ou algo nesse sentido, eu detesto trabalhar com 'array de parâmetros', é feio .. sabe ? quando é necessário, estritamente necessário, e que uma entidade é dispensável, aí beleza ..

 

<?php 
         class TwitterCredentialsStorage implements Countable , IteratorAggregate { 
                // ... 
         }
         $CredentialsStorage = TwitterCredentialsStorage::getInstance();
         $CredentialsStorage->append( new Twitter\TwitterAuthentication() ) ;
         $CredentialsStorage->append( new Twitter\TwitterAuthentication() ) ;

 

O que acham ?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Andrey, eu pensei em usar Reflection para conferir os parâmetros obrigatórios dos métodos.

 

Após ler muito, cheguei a um consenso sobre a utilização do __call e como anotar os parâmetros:

 

Por exemplo, o método GET geo/id/:place_id da API, onde place_id é obrigatório:

private function getGeoPlace( $placeId ) {}
private function _getGeoPlace() {

// este código será executado somente depois da validação

}

No caso, a função sem o underline contém o parâmetro obrigatório. O problema será quando um ou outro parâmetro são obrigatórios, como screenName e ID. Deixando isso de lado, continuemos:

 

O __call, que será executado depois de qualquer chamada de método não-estático da classe:

public function __call( $method, $args ) {

$errors = 0;

$methodArgs = Array(); // valores dos argumentos que serao passados para o real metodo depois da verificacao usando call_user_func_array
$requiredParameters = Array(); // parametros que serao impressos se der erro

$methodReflection = new ReflectionMethod( 'Twitter', $method );
$parameters = $methodReflection->getParameters(); // retorna um objeto com o tipo e nome dos parametros	

     	if ( method_exists( $this, '_' . $method ) ) {

	// verifica se os valores dos parametros existem como atributos da classe (e sao diferente de vazio)

	for ( $i = -1, $il = count( $parameters ); ++$i < $il;) {

		$name = $parameters[$i]->name;
		$requiredParameters[] = $name;

		if ( !empty( $this->$name ) ) {

			$methodArgs[] = $this->$name;

		} else {

			$errors++;

		}

	}

	if ( $errors > 0 ) {

		array_push( $this->errors, 'Confira os parâmetros: ' . $method . '( ' . implode( ', ', $requiredParameters ) . ' )' );

	} else {

		call_user_func_array( Array( 'Twitter', '_' . $method ), $methodArgs );

	}


     	} else {

	array_push( $this->errors, 'Método inexistente na API' );

}

}

O que ele faz é verificar se os valores dos atributos da classe, que foram os parâmetros anotados, existem e são diferentes de vazio, então podemos chamar o método privado com underline, que fará a magia.

 

Não sei se essa é a melhor forma de fazer isso, mas me parece melhor que um Array monstruoso para validar cada método da API.

 

Link: http://ideone.com/X5KpH

 

** Parece que no PHP o Reflection não é lento como eu pensava. O PC rodou um milhão de vezes o __call com dois métodos-teste (que davam erro) em 14s.

Compartilhar este post


Link para o post
Compartilhar em outros sites

De qualquer forma, vai ter que passar esses parâmetros pro método, isso que eu não quero .. acho que o melhor vai ser o bendito do array mesmo .. daí é só criar um método que valida os parâmetros requeridos de X método no array recebido ..

 

Acho que não há solução melhor, pelo menos eu não vejo .. seguindo a idéia do parameter object

 

<?php

         abstract class AbstractParameters implements Countable {

                protected $parameters = array ( ) ;

                public function accept ( ) {
                       if ( func_num_args() > 0 ) {
                              forEach ( func_get_args() as $param ) {
                                     if ( ! array_key_exists( $param , $this->parameters ) ) 
                                            throw new Exception ( '...' ) ;
                              }
                       }
                }

                public function  count ( ) {
                       return count ( $this->parameters ) ;
                }

                public abstract function getParameters ( ) ;

         }

         class TwitterHomeTimeLineParameters extends AbstractParameters { 

                public function setUserName ( $userName ) {
                       $this->parameters [ 'screen_name' ] = $userName ;
                }

                public function includeEntities ( ) {
                       $this->parameters [ 'include_entities' ] = true ;
                }

                public function getParameters ( ) {
                       forEach ( $this->parameters as $param => $val ) 
                              $p [ ] = sprintf ( "%s=%s" , $param , $val ) ;
                       return implode ( '&' , $p ) ;
                }

         }

         class TwitterTimeline {

                public function getHomeTimeLine ( AbstractParameters $parameters = null ) {
                       $parameters->accept( 'screen_name' ) ; 
                       printf ( 'http://api.twitter.com/.../homeTimeline.json?%s' , $parameters->getParameters() ) ;
                }

         }

         class Twitter {

                public static function getTimeLine ( ) {
                       return new TwitterTimeline ( ) ;
                }

         }

         $parameters = new TwitterHomeTimeLineParameters();
         $parameters->setUserName('andreyknupp');
         Twitter::getTimeLine()->getHomeTimeLine( $parameters ) ;

 

A saída será:

http://api.twitter.com/.../homeTimeline.json?screen_name=andreyknupp

 

O accept é só uma demonstração, claro que nessa classe eu teria parâmetros required , e optionals .. como posso utilizar screen_name , user_id .. nesse caso, o método 'optional', validaria um ou outro ..

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.