KISS - Keep it simple, stupid!

Inserindo índices automaticamente nos relacionamentos

postado por Lucas Gameiro em 07/06/2011 11:27:01

Durante o desenvolvimento de uma aplicação legada, percebi que muitos gastos do banco de dados era devido a falta de índices nas tabelas do banco de dados.

Para não criar todos os índices na mão resolvi desenvolver um shell para o cakephp que os insira sempre que um relacionamento for detectado.

O script pode ser encontrado aqui no meu github.

Segue o código para quem não trabalha com git:

<?php
App::import('Model');
class InsertIndexShell extends Shell {
    public function main() {
        $models = $this->getModels();
        $this->insertIndexes($models);
    }
 
    private function getModels()
    {
        return App::objects('model');
    }
 
    private function insertIndexes($models)
    {
        foreach($models as $model)
        {
            App::import('Model',$model);
            $this->{$model} = new $model();
            $this->{$model}->recursive = -1;
            $this->fieldsToIndex($this->{$model});
        }
    }
 
    private function fieldsToIndex($modelObj)
    {
        foreach($modelObj->belongsTo as $relacionamento)
        {
            $field = $modelObj->_schema[$relacionamento['foreignKey']];
            if(!empty($field) && !array_key_exists('key',$field))
            {
                if($modelObj->query("ALTER TABLE `{$modelObj->tablePrefix}{$modelObj->table}` ADD INDEX (`{$relacionamento['foreignKey']}`)"))
                {
                    $this->out("Indice {$relacionamento['foreignKey']} adicionado em {$modelObj->table}");
                }
                else
                {
                    $this->out("impossivel adicionar indice {$relacionamento['foreignKey']} em {$modelObj->table}");
                }
            }
        }
    }
}
?>

Obs1.: Está específico para o MySQL.
Obs2.: Na próxima versão verificarei as colunas que estão sendo utilizadas nos conditions.

Tags: , , , , ,
Topo

Sem comentários

Logando queries no Shell do CakePHP

postado por Gabriel Gilini em 06/06/2011 14:59:53

Se você é como eu, adora criar shells em projetos CakePHP. São fáceis, herdam pouca estrutura e te dão acesso a quase tudo que precisa para tarefas de manutenção.

Apesar de todos os pontos positivos, passei por um problema sério recentemente: a incapacidade de se extender sua classe super. Todo shell deve herdar a classe Shell, que é declarada no core do CakePHP (cake/console/libs/shell.php). Este pequeno fato torna inviável qualquer mudança na classe Shell, já que alterar código terceiro em um projeto não é algo que eu considero inteligente.

A solução foi criar uma classe intermediária, assim como temos o AppController e o AppModel, podemos ter o AppShell.

Crie um arquivo app.php no caminho app/vendors/shells/app.php e crie sua classe AppShell:

<?php
class AppShell extends Shell
{
}

Ok, temos uma classe intermediária que podemos extender à vontade, é só fazer com que nossos shells extendam AppShell, com um pequeno porém: o Cake não vai incluir por padrão o arquivo de sua base class. E agora?

Simples! Vamos incluir o arquivo usando o método canivete suíço App::import.

<?php
App::import('Shell', 'App');
class SomeApplicationSpecificShell extends AppShell
{
    // Call some parent methods
}

E é só isso!

Mas o que você vai colocar na sua super classe? Eu tenho uma sugestão.

Sinto muita falta do element sql_dump, que apresenta um log de todas as transações ocorridas nos datasources durante o load da página. Abri o código do element e portei para nossa classe de shell:

<?php
class AppShell extends Shell
{
    public function getDatabaseLogs()
    {
        if (!class_exists('ConnectionManager') || Configure::read('debug') < 2) {
            return false;
        }
 
        $sources = ConnectionManager::sourceList();
        if (!isset($logs))
        {
            $logs = array();
            foreach ($sources as $source)
            {
                $db =& ConnectionManager::getDataSource($source);
                if (!$db->isInterfaceSupported('getLog'))
                {
                    continue;
                }
                $logs[$source] = $db->getLog();
            }
        }
 
        $out = array();
        $out[] = "Nr\tQuery\tError\tAffected\tNum. rows\tTook (ms)";
        foreach ($logs as $source => $logInfo)
        {
            $text = $logInfo['count'] > 1 ? 'queries' : 'query';
 
            $tmp = array();
            foreach ($logInfo['log'] as $k => $i)
            {
                $tmp[] = ($k + 1) . "\t" . h($i['query']) . "\t{$i['error']}\t{$i['affected']}\t{$i['numRows']}\t{$i['took']}";
            }
 
            $out[] = array(
                sprintf('(%s) %s %s took %s ms', $source, $logInfo['count'], $text, $logInfo['time']),
                $tmp
            );
        }
 
        return $out;
    }
}

Como vocês podem ver, simplesmente peguei o conteúdo do element sql_dump, removi a marcação, inseri tabulações para facilitar a visualização, joguei tudo num array e simplesmente retornei. Fica a cargo do desenvolvedor do projeto tratar e exibir a saída.

E você, que método é imprescindível em seus scripts shell?

Tags: , ,
Topo

Sem comentários

CakePHP Component para o Google URL Shortener

postado por Fabrício Ferracioli em 19/04/2011 22:57:06

Fala Galera!

Hoje comecei um Component para o CakePHP que utiliza as funções básicas do Google URL Shortener, ou goo.gl.
Essas duas funções já estão prontas, e futuramente pretendo adicionar a função de estatísticas de cliques, também disponível na API.

O projeto se encontra nesse repositório do GitHub. Espero que seja útil para vocês, e caso alguém tenha alguma opinião ou dúvida, utilize os comentários.

Update: um exemplo de utilização sempre cai bem, não é?

<?php
class ExamplesController extends AppController
{
    var $name = 'Examples';
    //sua chave gerada pelo google pode ser fornecida aqui
    var $components = array('GoogleUrlShortener' => array('apiKey' => 'sua-chave-da-api-aqui'));
 
    function action()
    {
        //essa é outra maneira de fornecer sua chave da api
        $this->GoogleUrlShortener->apiKey = 'sua-chave-da-api';
        $results = $this->GoogleUrlShortener->generateShortUrl($this->data['longUrl']);
        $original = $this->GoogleUrlShortener->getOriginalUrl($results['id']);
    }
}
?>

Agora sim, fica simples de usar!

Tags: , , , ,
Topo

Sem comentários

Oficina de personalização de Wordpress

postado por Fabrício Ferracioli em 11/11/2010 07:35:40

Fala galera, tudo certo?

Esse blog anda um pouco inativo mas ainda funciona!

Talvez seja porque os autores estão muito ocupados em suas atividades atuais, mas não esquecemos de vocês.

Esse post vai ser bem rápido, e direcionado para um pessoal que normalmente não entraria nesse blog. Nos dias 8 e 9 desse mês, eu e o Rennan Martini ministramos uma oficina de personalização para Wordpress a pedido do professor Dhiego Bicudo para a turma do último período de Artes Visuais da Unopar. Durante a apresentação prometi para eles disponibilizar o arquivo que utilizei, então aqui está ele.

Agradeço a oportunidade dada pelo Dhiego e a atenção de todos. Como tenho certeza que vocês estão cheio de dúvidas, utilizem os comentários que tento ajudar.

Tags: , ,
Topo

2 Comentários

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

2 Comentários

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

5 Comentários

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