KISS - Keep it simple, stupid!

Lib para lidar com arquivos Zip no CakePHP

postado por Gabriel Gilini em 26/04/2010 17:11:42

Em um projeto recente, precisei extrair arquivos zip no server, e no
processo criei uma classe para abstrair a manipulação dos arquivos.

Como no Dreamhost o PHP não vem com a zlib habilitada, não tem como
usar a ZipArchive, o que facilitaria muito as coisas. Mas o bom é que
o zlib é instalado no server, e os comandos do PHP de execução de
programas são habilitados, então criei a classe utilizando o `exec’
para chamar o `unzip’ e extrair os arquivos.

A classe está bem simples porque só implementei o necessário para meu
projeto, mas é um esqueleto para quem precisar de algo mais completo.

Evitei o uso de expressões regulares por questão de performance. Aí vai o código:

Coloquem no diretório APP/lib, e usem assim:

Tags: , , ,
Topo

1 Comentário

Validação de campos de texto com caracteres acentuados

postado por Fabrício Ferracioli em 02/04/2010 14:00:56

Recentemente tive um problema com a validação de um campo textual que me deu um pouco de dor de cabeça. Como acredito que esse pode ser um problema comum, vou compartilhar a solução aqui no blog.
Toda entrada textual deve ser representada com uma codificação de caracteres específica. Essas codificações são diversas, mas as mais conhecidas e utilizadas são ASCII, ISO 8859-1 e UTF-8. Cada uma possui diferentes capacidades e características, mas já adianto que a mais atraente delas é o UTF-8 (ou Unicode), sendo inclusive uma recomendação de utilização do W3C.

Todo programador também sabe da importância de validar uma entrada do usuário antes de realizar qualquer operação com ela, e uma regra de validação comum é a quantidade de caracteres em uma entrada de texto. O CakePHP fornece regras de validação como o minLength, maxLength e between para facilitar a vida do programador.

Agora imagine que você precisa validar um campo textual que deve ter entre 5 e 10 caracteres. Simples, defina a seguinte regra em seu modelo:

var $validate = array(
    'campo' => array(
        'rule' => array('between', 5, 10),
        'message' => 'Este campo precisa ter entre 5 e 10 caracteres.'
    )
);

Perfeito!

Calma que não é bem assim. Imagine que o campo foi preenchido com o valor ‘php é foda’, uma string de tamanho 10. Curiosamente, essa string não passa na regra de validação. Por que?

Ao observar a documentação das três regras que mencionei, vocês irão perceber que existe uma observação dizendo que o tamanho do dado é a quantidade de bytes utilizada para representá-lo. Na Web a maior parte dos textos é codificada em UTF-8, ISO-8859-1, entre outros encodings, que podem utilizar mais de um byte para representar caracteres acentuados, e esse é o motivo da regra de validação não funcionar para esse caso.

Também não adianta usar a função strlen() do PHP, porque ela possui o mesmo comportamento.

O que fazer então?

É claro que existe uma alternativa, que é a função mb_strlen. Ela recebe 2 parâmetros, sendo o 2 opcional, mas de grande importância, que é justamente a codificação utilizada para a string do primeiro parâmetro.

O código

    mb_strlen('php é foda', 'utf-8');

retorna exatamente 10, o valor que desejamos.

Desse modo, nossa função de validação seria:

function validateInputLength($input)
{
    $encoding = mb_detect_encoding($input['campo']);
    $lowerLimit = 5;
    $upperLimit = 10;
    return mb_strlen($input['campo'], $encoding) >= $lowerLimit && mb_strlen($input['campo'], $encoding) <= $upperLimit;
}

Note o uso da função mb_detect_encoding, o que torna a função capaz de manipular qualquer tipo de string, não sendo dependente de nenhuma codificação de caracteres.

Apesar de ser uma questão simples, acredito que essa solução pode ajudar bastante.
Alguém já teve problemas semelhantes? Como resolveram?

Tags: , , , ,
Topo

6 Comentários

Galeria de imagens similar à da Apple Store

postado por Gabriel Gilini em 31/03/2010 11:42:05

Semana passada o dgmike fez um desafio em seu blog. O objetivo era construir uma galeria de imagens igual à vista na Apple Store do zero, e obviamente sem consultar o fonte do original.

Decidi que era uma boa oportunidade para treinar alguns conceitos de scripting e também divulgar meu trabalho. Além disso, vinha procurando uma desculpa pra trabalhar com a My Library; melhor lib JavaScript já concebida, se você me perguntar.

Meti a mão na massa no fim de semana, e depois de quebrar a cabeça pra deixar algumas coisinhas redondas, consegui terminar uma primeira versão do script. Não testei extensivamente, mas sei que funciona em IE7, IE8, Opera8-10, Chrome e Firefox 2+.

Dê uma conferida no meu trabalho, e diga o que achou aí nos comentários. Se encontrar qualquer bug ou tiver alguma sugestão, vou ficar feliz em ouvi-lo.

Update:
David Mark was nice enough to link my gallery at MyLib’s website!
Thanks, David!

Tags: , , ,
Topo

4 Comentários

Redirecionamento de Erros: descubra os 404 e diminua a insatisfação do usuário

postado por Lucas Gameiro em 22/03/2010 16:55:43

Uma das coisas mais frustrantes da navegação na internet é quando você vê um link que te interessa e quando clica é redirecionado pra um erro.

O 404 é o mais comum deles, isso porque ele é muito fácil de acontecer. Se alguém citou uma página do seu site que não existe mais, ou algum erro no script constroi uma url errada dinamicamente o 404 é invevitável você não vai percebê-lo rapidamente.

O CakePHP através do método link do helper Html já previne alguns problemas porém ainda não é possível administrar os erros de maneira fácil.
Pensando nisso, eu desenvolvi um sistema para informar quando os 404 acontecem e criar redirecionamentos para que eles não se repitam. Isto não é difícil de ser feito já que o CakePHP tem boas maneiras de controlar este erro.

A primeira coisa a se fazer é criar a tabela onde ficaram armazenados estes erros.

CREATE TABLE `redirects` (
    `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `num_errors` int(10) UNSIGNED NOT NULL,
    `page_from` varchar(255) NOT NULL,
    `page_to` varchar(255),
    `num_redirects` int(10) UNSIGNED,
    PRIMARY KEY (`id`)
)

Após a tabela criamos o modelo com métodos para contar e manipular os erros e os redirecionamentos: (/models/redirect.php)

<?php
class Redirect extends AppModel {
    var $name = 'Redirect';  
    public function manageError($url){
        $redirects = $this->find(
            'first',
            array(
                'conditions' => array('Redirect.page_from' => $url)
            )
        );
        if(!$redirects){
            $redirects['Redirect']['num_errors'] = 0;
            $redirects['Redirect']['page_from'] = $url;
            $redirects['Redirect']['num_redirects'] = 0;
            $redirects['Redirect']['page_to'] = null;
        }
        $redirects['Redirect']['num_errors']++;
        $this->set(
            $redirects
        );
        return $this->save();
    }
    public function manageRedirect($urlfrom, $urlTo){
        $redirect = $this->find(
            'first',
            array(
                'conditions' => array(
                    'Redirect.page_from' => $urlfrom
                )
            )
        );
        $redirect['Redirect']['num_redirects']++;
        $this->set($redirect);
        return $this->save();
    }
}
?>

Com isso, já podemos criar o armazenamento, redirecionamento e contagem dos erros. Esta será feita escrevendo a classe AppError que é feita exatamente para manipular estes erros. (/app_error.php)

<?php
    class AppError extends ErrorHandler{
        var $Error;
        public function error404($params){
            $this->Redirect = ClassRegistry::init('Redirect');
            $page = $this->Redirect->find(
                'first',
                array(
                    'conditions' => array(
                        'page_from' => $params['url']
                    )
                )
            );
            if($page && $page['Redirect']['page_to']){
                $this->Redirect->manageRedirect(
                    $params['url'],
                    $page['Redirect']['page_to']
                );
                $Dispatcher = new Dispatcher();
                $Dispatcher->dispatch($page['Redirect']['page_to']);
            }
            else{
                $this->Redirect->manageError($params['url']);
                $this->controller->set('url', $params['url']);
                $this->_outputMessage('error404', $params);
            }
        }
    }
?>

Agora os erros já estão sendo contados e redirecionados porém sua administração tem que ser feita diretamente no banco de dados. É interessante portanto também criar métodos para gerenciar estes erros e redirecionamentos. ficamos então com o controller: (/controllers/redirects_controller.php)

<?php
class RedirectsController extends AppController {
	var $name = 'Redirects';
	var $helpers = array('Html', 'Form');
    var $paginate = array(
        'limit' => 25,
        'order' => array(
            'num_errors' => 'desc'
        )
    );
	function index() {
		$this->Redirect->recursive = 0;
		$this->set('redirects', $this->paginate());
	}
	function create_redirect($id = null) {
		if (!$id && empty($this->data)) {
			$this->Session->setFlash(__('Redirecionamento Inválido', true));
			$this->redirect(array('action'=>'index'));
		}
		if (!empty($this->data)) {
			if ($this->Redirect->save($this->data)) {
				$this->Session->setFlash(__('O Redirecionamento foi salvo.', true));
				$this->redirect(array('action'=>'index'));
			} else {
				$this->Session->setFlash(__('O Redirecionamento não pode ser salvo. Por favor, tente novamente.', true));
			}
		}
		if (empty($this->data)) {
			$this->data = $this->Redirect->read(null, $id);
		}
	}
	function delete($id = null) {
		if (!$id) {
			$this->Session->setFlash(__('Redirecionamento com id inválido', true));
			$this->redirect(array('action'=>'index'));
		}
		if ($this->Redirect->del($id)) {
			$this->Session->setFlash(__('Redirecionamento excluído', true));
			$this->redirect(array('action'=>'index'));
		}
	}
}
?>

Observem que este controller não está utilizando nenhum método de autenticação. Fica em aberto o método a ser utilizado sendo que todas as páginas devem ficar invisíveis ao usuário comum. As views necessárias para este controller são:
create_redirect (/views/redirects/create_redirect.ctp):

<div class="redirects form">
<?php echo $form->create('Redirect', array('action' => 'create_redirect'));?>
	<fieldset>
 		<legend><?php __('Criar Redirecionamento');?></legend>
	<?php
		echo $form->input('id');
		echo $form->input('page_from', array('label'=>'URL do 404', 'disabled' => true));
		echo $form->input('page_to', array('label' => 'Redirecionar para'));
	?>
	</fieldset>
<?php echo $form->end('Criar');?>
</div>
<div class="actions">
	<ul>
		<li>
			<?php
				echo $html->link(
					__('Excluir', true),
					array(
						'action' => 'delete',
						$form->value('Redirect.id')
					),
					null,
					sprintf(
						__('Você tem certeza que deseja excluir # %s?', true),
						$form->value('Redirect.id')
					)
				);
			?>
		</li>
		<li>
			<?php
				echo $html->link(
					__('Listar 404s', true),
					array('action' => 'index')
				);
			?>
		</li>
	</ul>
</div>

e index (/views/redirects/index.ctp):

<div class="redirects index">
<h2><?php __('Redirecionamentos');?></h2>
<table cellpadding="0" cellspacing="0">
<tr>
    <th><?php echo $paginator->sort('URL do 404', 'page_from');?></th>
	<th><?php echo $paginator->sort('número de erros', 'num_errors');?></th>
	<th><?php echo $paginator->sort('URL a redirecionar', 'page_to');?></th>
	<th><?php echo $paginator->sort('número de redirecionamentos', 'num_redirects');?></th>
	<th class="actions"><?php __('Ações');?></th>
</tr>
<?php
foreach ($redirects as $redirect):
?>
	<tr>
		<td>
			<?php echo $redirect['Redirect']['page_from']; ?>
		</td>
		<td>
			<?php echo $redirect['Redirect']['num_errors']; ?>
		</td>
		<td>
			<?php echo $redirect['Redirect']['page_to']; ?>
		</td>
		<td>
			<?php echo $redirect['Redirect']['num_redirects']; ?>
		</td>
		<td class="actions">
			<?php
				echo $html->link(
					__('Criar Redirecionamento', true),
					array(
						'action' => 'create_redirect',
						$redirect['Redirect']['id']
					)
				);
			?>
            <?php
				echo $html->link(
					__('Excluir', true),
					array(
						'action' => 'delete',
						$redirect['Redirect']['id']
					)
				);
			?>
		</td>
	</tr>
<?php endforeach; ?>
</table>
</div>
<div class="paging">
	<?php echo $paginator->prev('<< '.__('anterior', true), array(), null, array('class'=>'disabled'));?>
 | 	<?php echo $paginator->numbers();?>
	<?php echo $paginator->next(__('próxima', true).' >>', array(), null, array('class' => 'disabled'));?>
</div>

Pronto, agora temos uma ferramenta para gerenciar os erros 404 do site. Tomara que algum dia a frustração de pensar que um link solucionará meus problemas e tomar um 404 na lata diminua

Criei um projeto no github pra facilitar a baixar os arquivos. Está tudo neste link

Tags: , , , , ,
Topo

4 Comentários

A importância do software livre e padrões abertos na Web

postado por Fabrício Ferracioli em 20/03/2010 11:48:08

Ano passado um aluno de jornalismo da UEL me procurou para falar um pouco sobre minha “experiência” com software livre. Ele desejava saber minhas opiniões tanto no papel de usuário quanto de desenvolvedor, tanto para o mercado quanto para a área científica. Uma parte do resultado da pesquisa do Lucas de Godoy pode ser visto nesse post do blog dele.

Há algum tempo o João José postou aqui no blog sobre práticas para se tornar um melhor desenvolvedor CakePHP e entre elas muitas estão relacionadas ao software livre ou a padrões abertos. Mas acredito que essas práticas não ajudam somente o desenvolvedor CakePHP, mas qualquer desenvolvedor.

Por que?
Imagine a Web dependente da vontade de players de mercado com suas soluções proprietária e obscuras? Difícil? Então vamos voltar um pouco no tempo e lembrar da época da guerra dos browsers. Netscape e Microsoft disputavam quem tinha o melhor browser e para isso também disputavam a preferência dos desenvolvedores Web. Pouca importância era dada aos padrões do W3C e era muito comum implementar duas versões de um mesmo aplicativo Web.

Até hoje sofremos um pouco com os resultados dessa época negra da Web, mas o cenário mudou. Hoje os browsers brigam entre si para ver quem é o mais rápido em dar suporte total a HTML 5, CSS 3, EcmaScript o que é muito positivo e na minha opinião o caminho certo. Não sou só eu que penso assim, Tim Berners-Lee, o criador da nossa tão querida WWW é um dos maiores incentivadores de padrões abertos. Vantagens, você só precisa aprender uma vez, pois todos seguem o mesmo padrão o que torna a Web muito mais acessível. O usuário também ganha pois, por mais que não saiba, tem a liberdade de escolher o browser de sua preferência e visualizar a aplicação Web da mesma maneira em todos eles.

Falando um pouco sobre o server side, não tem como negar que o software livre é praticamente a base da Web atual. O servidor Web mais utilizado é o Apache e a linguagem de programação o PHP. Ambos são disponibilizados com licenças abertas, o que facilita a sua adoção. Também possuem uma ampla comunidade que está sempre disposta a ajudar e uma boa documentação que ajuda tanto iniciantes quanto desenvolvedores mais experientes. Com nosso framework preferido também não é diferente.

Tudo isso ajuda a fazer um desenvolvedor melhor. Ter a oportunidade de ver o código do que você utiliza para desenvolver suas soluções dá uma compreensão melhor de como as coisas funcionam, te ajuda a entender melhor a lógica por trás da mágica, te faz entrar em contato com outras metodologias de programação, etc. Participar de comunidades de software livre, lendo threads, postando perguntas e ajudando com respostas também tem um peso muito positivo, ajuda a entender necessidades de outros desenvolvedores, compreender códigos diferentes dos seus, solucionar problemas que não são seus, entrar em contato com outros profissionais com mais ou menos experiência, etc.
Se você for um pouco mais hardcore pode até colaborar no desenvolvimento, e o convite sempre está aberto para quem deseja ajudar. Se você não é um programador fabuloso, ainda pode ajudar na documentação, tradução ou simplesmente sendo ativo na comunidade.

E o software proprietário? Não sou contra de maneira alguma, mas algo tão abrangente e revolucionário como a Web não pode ser baseado em licenças proprietárias.
E você, o que acha sobre a importância do software livre e dos padrões abertos para o desenvolvimento Web?

Tags: , ,
Topo

2 Comentários

Migrando para o CakePHP 1.3

postado por Fabrício Ferracioli em 22/02/2010 13:39:06
Tags:

A maioria já deve saber que o CakePHP está com a versão 1.3 do framework em estado beta. Me recordo que a versão 1.2 do framework já era bem agradável quando estava em fase beta, e portanto decidi testar o que o CakePHP 1.3 tinha de novo. Fiz o download e fui direto para a página que contém a descrição das principais mudanças da versão 1.2 para a 1.3. Quando vi que ela tinha um tamanho um pouco grande decidi testar logo alguma aplicação que eu tinha funcionando na versão 1.2.

Para minha surpresa ela não exibiu nem a página inicial, e ao invés disso várias mensagens de erro foram mostradas. Então vi que era realmente necessário ler a extensa página. Durante a leitura percebi que diversas mudanças importantes ocorreram, e logo vi o porque da minha aplicação sequer funcionar. Como a leitura é um pouco extensa, decidi resumir as principais mudanças aqui. As novidades vão ficar para depois, porque também são várias.

Principais mudanças do CakePHP 1.3

  • Adicionadas configurações específicas do config/core.php para quem utiliza o PHP 5.3
  • O arquivo webroot/index.php foi alterado, e deve ser substituído em sua aplicação
  • Recomenda-se que todos os métodos e classes deprecados não sejam mais utilizados
  • Admin routes foram removidas por uma configuração mais geral, chamada routes prefix. O route prefix admin pode ser definido com a linha
    Configure::write('Routing.prefixes', array('admin'));
  • O método de remoção no modelo agora é único,
    Model::delete()
  • O Model teve os métodos métodos findAll(), findCount() e findNeighbours() removidos
  • Os diretórios css, js e img foram removidos dos diretórios app/vendors e plugin/vendors e substituídos com os diretórios plugin e theme no webroot
  • Somente a variável
    $title_for_layout

    poderá ser definida para o título da página, tanto no Controller quanto na View

  • Deve-se selecionar o ponto da aplicação desejado para dump de sql quando o debug está definido para 2, utilizando a linha de código
    echo $this->element('sql_dump');

    em qualquer ponto da aplicação

  • SessionHelper e SessionComponent não são mais carregados por padrão. Agora devem ser declarados como qualquer outro helper ou component. Para manter o comportamento antigo, adicione a declaração de helpers e components em seu AppController
  • A função
    SessionComponent::setFlash()

    teve seu segundo parâmetro alterado para usar um element e não um layout. Para alterar sua aplicação primeiro mova seus layouts para a pasta de elements e renomeie a variável

    $content_for_layout

    para

    $message
  • Não existe mais o nível 3 de debug
  • PaginatorHelper agora produz a saída dos métodos
    prev(); next(); first(); last();

    englobada por

    <span>

    e não

    <div>

    para tornar a estilização mais fácil

  • Os métodos
    dateTime(); year(); month(); day(); minute(); meridian(); select();

    do FormHelper não possuem mais o parâmetro

    $showEmpty

    , e agora utilizam

    $attributes['empty']
  • FormHelper::submit()

    agora pode criar outros inputs além de type=submit. Para isso utilize a option type

  • FormHelper::button()

    agora cria elementos button ao invés de inputs reset. Para criar inputs de reset, utilize

    FormHelper::submit()

    com o parâmetro option definindo ‘type’ => ‘reset’

  • O método
    FormHelper::create()

    não cria mais elementos fieldset escondidos, agora substituídos por divs escondidas, o que ajuda na validação de HTML 4

  • No HtmlHelper os métodos
    link(); para(); div(); tag()

    não pussuem mais o parâmetro

    $escape

    , que foi substituído por

    $options['escape']

    . Similarmente os métodos

    meta(); css()

    tiveram o parâmetro

    $inline

    substituído por

    $options['inline']
  • Agora as chamadas a
    $session->flash()

    não são mais auto-exibidas, sendo necessário adicionar

    echo

    antes da chamada a função, como em qualquer método de helper

  • JavascriptHelper e AjaxHelper estão deprecados, e agora deve-se utilizar o JsHelper em conjunto com o HtmlHelper.
    $javascript->link()

    agora é

    $javascript->codeBlock()

    é

    $html->scriptBlock()

Já deu para perceber que vai dar trabalho mudar algumas coisas. E essas são apenas as alterações que considerei mais importantes, na página de migração do 1.2 para o 1.3 está a lista completa. Verifique nessa se alguma mudança afeta as suas aplicações.

Pelas alterações deu para perceber que o framework está mais preocupado com a padronização do seu comportamento, de código, utilização de Web Standards e desempenho, questões que são sempre importantes.
Bom, é isso, espero ter ajudado. Na próxima vamos dar uma olhada nas novidades do Cake 1.3.

Tags:
Topo

3 Comentários

JavaScript: buscar a posição de um elemento em uma matriz

postado por Gabriel Gilini em 30/12/2009 16:00:03
Não há tags para este post

Recebi uma pergunta curiosa no Aardvark há pouco, perguntando se eu não conhecia algum código JavaScript que buscasse a posição exata de um elemento dentro de um array multidimensional — matriz — qualquer.

Não é novidade pra ninguém que eu me divirto escrevendo código nessa linguagem, então resolvi bolar alguma coisa pra resolver o problema do rapaz em necessidade. Aí vai o código (gist):

Exemplo de uso:

No tags for this post.
Topo

1 Comentário

Creative Commons License
Sou Ágil: KISS em http://kiss.souagil.com.br está licenciado sobre
Creative Commons Attribution-Share Alike 2.5 Brazil License.

souÁgil