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

Plugin Manager – Gerenciando os plugins de sua aplicação de forma ágil

postado por Marcus Vinicius em 06/04/2009 15:38:16

Já tinha visto muitas vezes o João José reclamando que não tinha um gerenciador de plugins feito o do rails… Inclusive, havia até postado aqui no KISS uma idéia que tinha encontrado no blog @TheKeyBoard

A idéia é bem interessante, porém existe apenas um repositório centralizado, que o próprio desenvolvedor mantém.

A partir disso, surgiram nossas primeiras divagações:

  • Por que nao possibilitar que cada usuário possa manter seu próprio repositório?
  • Podíamos possibilitar a criação de um script de instalação/desinstalação?
  • E se existisse um jeito de especificar as dependencias, e elas serem automaticamente resolvidas?
  • Pouts! Mas se eu não quizer manter um repositório só pra poder guardar um link pra um plugin?

Assim surgiu o Plugin Manager, que ainda está em fase de implementação… Mas creio eu que está completo o suficiente para ser usado, testado, e também para que novas idéias surjam, não só nossas, mas de todos aqueles que se interessarem pelo projeto.

Botando pra Quebrar

Dentro da pasta APP/plugins/ execute:

git clone git://github.com/lucius/plugin_manager.git

pronto!

cake/console/cake -app SUA_APLICACAO plugin

Agora é só seguir as instruções

Plugin Manager em funcionamento

É sempre bom lembrar que o projeto está hospedado no github e que qualquer pessoa que deseja fazer modificações pode ficar a vontade para tal…

Tags: , , , ,
Topo

3 Comentários

Improved Cake Shell: feedbacks coloridos no console

postado por Marcus Vinicius em 13/03/2009 14:19:22

Eu e o Lucas tinhamos um trabalho bem divertido: criar um shell do cake. Ate aí, nada de muito complicado, pois a tarefa que seria executada era bem simples. De fato, realizamos o trabalho rapidamente. Comecei a pensar e vi que o log que seria gerado, poderia ser estupidamente grande, e tinhamos um feedback ‘preto-e-branco’ e pouco intuitivo…

Sempre fui o “cara da interface gráfica, das coisas gays e bonitinhas” como diria o Rafael (o homem modo-texto), e não seria numa hora dessas que deixaria isso de lado. xD

Comecei a conversar com o João, comentando esse fato, esperando que ele tivesse uma ideia (mesmo que copiada de alguém =D). Para minha surpresa, ali estava: cake-db-migrations.

Tá bom… mas e daí? Muito interessante o que joelmoss tinha feito… Mas para formatar uma string grande, além de ser obrigado a criar um estilo, ainda tinha que ficar concatenando varias strings com e sem formatação.

$this->out('Teste de formatacao simples '.$this->_colorize('funcionando',NOME_DO_ESTILO).'!');

Mas poderia ser mais fácil… algo como TAGS seria ótimo! Bom, depois de algum tempo de meditação e trabalho, agora elas funcionam… e de forma bem simples!

$this->formattedOut('Teste de formatacao simples [fg=green]funcionando[/fg]!');

E a saída no shell:

Teste de formatacao simples funcionando!

Bom, vamos ao que realmente interessa.

COMO USAR?

Primeiro: entre a pasta ‘plugins’ da app e

git clone git://github.com/lucius/improved_cake_shell.git

Depois, na hora de criar o seu shell em APP/vendors/shells/, apenas importe o plugin e extenda sua classe da ImprovedCakeShell (ao invés do Shell normal). O resto funciona da mesma maneira:

<?php
App::import('Vendor', 'ImprovedCakeShell.ImprovedCakeShell');
 
class TestShell extends ImprovedCakeShell {
    function main() {
        $this->addStyle( array(
            'TEST' => array(
                'bg' => 'green',
                'fg' => 'black',
                's' => true
            )
        ));
 
        $this->formattedOut('[b]Teste[/b] de formatacao [TEST]funcionando[/TEST]!');
    }
}
?>

Vamos com calma! Posso criar estilos completos sem ter que ficar abrindo e fechando milhões de tags?
SIM! Basta apenas utilizar o método addStyle, como feito ali em cima.

E como prometido:

exemplo

Para obter informações mais detalhadas a respeito do uso é só consultar o README, que segue junto com o plugin.

Pronto! Agora seus logs de console trazem feedback de maneira mais eficiente, visualizável e BONITA!

Tags: , , , ,
Topo

2 Comentários

Migrations, uma breve explicação

postado por João José Pedrini em 28/02/2009 15:38:21

Uma das grandes dificuldades dentro de um time de desenvolvimento WEB, sem dúvida, é manter o Banco de Dados atualizado entre todos ambientes de trabalho. Volta e meia um desenvolvedor faz alguma alteração no banco e não comunica o restante do grupo. Isto sempre causa retrabalho, bugs ou um programador arrancando o próprio cabelo.

Uma das causas deste problema é que em nenhum momento rastreamos as modificações do banco em nossos projetos. Para o código temos o SCM (source code management), para controlar as tarefas temos os gerenciadores de projetos, para os documentos temos o Google Docs, mas para o banco de dados? Foi pensando nesses problemas que se criou o técnica de Migrations. O Migrations é uma forma de manter organizadas as modificações na estrutura do banco de dados, permitindo, dentre algumas facilidades, construir seu banco incrementalmente, retroceder alteração que não sejam mais adequadas ou trabalhar em ambientes com diversos programadores.

O conceito básico de Migrations consiste em armazenar as modificações do banco em pequenos arquivos numa ordem cronológica. Esses arquivos devem conter informações que consigam construir ou destruir seu banco de uma versão para qualquer outra. Como se faz isto? Simples, indique no seu arquivo como proceder numa determinada atualização (UP) e como retroceder (DOWN), assim, quando quer ir para uma versão mais nova, execute todos os UPs dos arquivos mais recentes, e quando quer retroceder, execute os DOWNs até a versão mais antiga. Com ajuda de um programa auxiliar, que geralmente é um automatizador, você consegue executar facilmente tarefas como (re)construção de banco, atualização de alterações ou rollback’s de algo que não saiu como se devia.

Em algumas implementações de Migrations é possível fazer manipulação de dados. Esta é uma facilidade muito interessante e os DBA’s devem adorar. Imagine que você, em um determinado momento, queira fazer uma modificação no campo “nome_completo” quebrando-o em dois: “nome” e “sobrenome”. Com a manipulação você consegue fazer o tratamento dos dados e migrar, não só a estrutura do banco, mas também os dados que por ventura podem estar armazenados. Esta funcionalidade dá um pouco mais de poder ao Migrations, mas nem sempre esta presente em todas as implementações.

Como alguns podem estar acompanhando, eu e Juan Basso estamos criando uma implementação de Migrations para CakePHP. Numa iniciativa de não tentar reinventar a roda, estamos fazendo alguns estudos sobre implementações de Migrations em PHP. No momento temos três implementações que acreditamos ser interessantes: CakeSchema, Migrations do JoelMoss para Cake e o Ruckusing. Cada um possui qualidades e defeitos, por isso tivemos a idéia de convidar a comunidade para discutir a necessidade de se criar uma nova implementação. Quem puder nos ajudar testando estas implementações e nos falando seus pontos pró e contras, ou mesmo apontando outras implementações, agradecemos.

Bem, está ai uma breve explicação de Migrations, espero que você tenham entendido um pouco e estamos esperando a participação de todos na discussão.

Tags: , , ,
Topo

1 Comentário

CakePHP AutoTest – Testes Contínuos

postado por João José Pedrini em 12/02/2009 20:24:10

Certo dia, lá no canal do IRC fiquei sabendo de um projeto muito interessante, o CakeAutoTest do Rodrigo. Se trata de um plugin de CakePHP para realizar Auto-Testes. Não sei se comentei aqui no blog mas sempre acreditei que teste automatizado é a melhor ferramenta de feedback que podemos ter num projeto, ele nos dá confiança para prosseguirmos, coragem para refatorar nossos códigos, simplicidade e motivação para não pararmos de programar. E se feedback é tão bom, por que não termos esse feedback a todo momento!? É esta a idéia do AutoTest.

CakeAutoTest em Ação!

CakeAutoTest em Ação!

Depois que soube que ele tinha iniciado este projeto, fiquei muito empolgado para poder utilizar. Infelizmente acabei tendo alguns problemas e não tive muito tempo para explorar o plugin. Bem, a espera terminou, fiz um fork no projeto e arregacei as mangas para solucionar alguns dos problemas que estava enfrentando, de quebra, acabei fazendo um pouco mais e adicionei suporte a uma ferramenta de notificação para o Linux (libnotify).  As minhas modificações já foram adicionadas no repositório principal do Rodrigo. Para instalar o Libnotify no Linux é fácil, com o ’slapt-get’ do Slackware é só fazer o download dos pacotes libnotify-0.4.*as e notification-daemon-0.4.*as (em outras distros eu não faço idéia como instalar).

Após fazerem clone no projeto do Rodrigo ou no meu na pasta de plugins vocês devem configurar o arquivo .autotest do seu projeto (crie um arquivo .autotest na raiz da pasta /app). Neste arquivo a idéia é armazenar as informações singulares de cada projeto, como as pastas que serão ignoradas e/ou o sistema de notificação que irá utilizar. Para vocês terem uma idéia, este arquivo será carregado dentro do __construct() do AutoTestShell, portanto vocês poderão setar/utilizar qualquer variável da classe. Como exemplo, vocês podem olhar o .autotest que utilizo:

<?php 
$this->ignore_files[] = '/autotest/';
// Adicionando a notificação do LibNotify
include( $this->paths['libs'].'libnotify.php');
?>

Para executar é simples, chame a Task autotest no Shell do CakePHP e deixa rolar. A cada modificação em qualquer arquivo do projeto, o plugin irá rodar os testes. Se você quebrou ou concertou algum teste, ele avisará. Simples e super útil! Agora, tudo que você fizer no projeto você terá o feedback se está melhorando ou estragando tudo!

Está ai a apresentação desta nova ferramenta para CakePHP, parabéns ao Rodrigo e qualquer dúvida que vocês tiverem é só comentar no post que terei o prazer de ajudar!

Tags: , , , , ,
Topo

1 Comentário

Criação de projetos customizáveis

postado por João José Pedrini em 30/01/2009 13:55:32
Tags: ,

Vendo o post do MouseCaneta sobre traduzir o template do Bake fiquei um pouco intrigado, ele estava fazendo modificações dentro do /cake/console/libs/templates/skel para que nos próximos projetos ele não tenha re-trabalho. Vendo por este lado, é um esforço justificável, seria interessante eu poder modificar alguma View, ou mesmo já fazer uma seleção de extensões que normalmente uso, etc. e sempre que eu começar um projeto eu já ter todas essas modificações em mãos. O problema está em mexer na pasta /cake. Eu nunca mexo na pasta Cake (enquanto eu ainda não sou um commiter do CoreTeam =D) e o motivo disto é que se por algum acaso eu venha atualizar minha versão do framework, eu não fico dependente. Por isso, li um pouco o código do BakeShell e descobri a solução.

joaojose@bisorrao:~/projetos/cake$ cp -r cake/console/libs/templates/skel/ 
                                   vendors/shells/templates/skel
joaojose@bisorrao:~/projetos/cake$ ls vendors/shells/templates/skel/
total 16
-rw-r--r-- 1 joaojose users 1302 2009-01-30 14:39 app_controller.php
-rw-r--r-- 1 joaojose users 1357 2009-01-30 14:39 app_helper.php
-rw-r--r-- 1 joaojose users 1273 2009-01-30 14:39 app_model.php
drwxr-xr-x 3 joaojose users  135 2009-01-30 14:39 config
drwxr-xr-x 3 joaojose users   50 2009-01-30 14:39 controllers
-rw-r--r-- 1 joaojose users  963 2009-01-30 14:39 index.php
drwxr-xr-x 3 joaojose users   16 2009-01-30 14:39 locale
drwxr-xr-x 4 joaojose users   40 2009-01-30 14:39 models
drwxr-xr-x 2 joaojose users   18 2009-01-30 14:39 plugins
drwxr-xr-x 5 joaojose users   46 2009-01-30 14:39 tests
drwxr-xr-x 6 joaojose users   56 2009-01-30 14:39 tmp
drwxr-xr-x 3 joaojose users   19 2009-01-30 14:39 vendors
drwxr-xr-x 8 joaojose users   90 2009-01-30 14:39 views
drwxr-xr-x 6 joaojose users  126 2009-01-30 14:39 webroot
joaojose@bisorrao:~/projetos/cake$ cake bake project novo_projeto
               -skel /home/joaojose/projetos/cake/vendors/shells/templates/skel/
 
Welcome to CakePHP v1.2.0.7962 Console
---------------------------------------------------------------
App : app
Path: /home/joaojose/projetos/cake/app
---------------------------------------------------------------
Bake Project
Skel Directory: /home/joaojose/projetos/cake/vendors/shells/templates/skel/
Will be copied to: /home/joaojose/projetos/cake/app/novo_projeto
---------------------------------------------------------------
Look okay? (y/n/q)
[y] &gt; y
Do you want verbose output? (y/n)
[n] &gt; n
---------------------------------------------------------------
Created: novo_projeto in /home/joaojose/projetos/cake/app/novo_projeto
---------------------------------------------------------------
 
Creating file /home/joaojose/projetos/cake/app/novo_projeto/views/pages/home.ctp
Wrote /home/joaojose/projetos/cake/app/novo_projeto/views/pages/home.ctp
Welcome page created
Random hash key created for 'Security.salt'
CAKE_CORE_INCLUDE_PATH set to /home/joaojose/projetos/cake in webroot/index.php
CAKE_CORE_INCLUDE_PATH set to /home/joaojose/projetos/cake in webroot/test.php
Remember to check these value after moving to production server

O procedimento é simples, primeiro eu copiei o Skeleton comum do Cake para a pasta /vendors/shells/templates/skel (só para ter um poonto de partida). Fiz as alterações que queria, executei o ProjectTask do BakeShell passando o nome do meu novo projeto e o parâmetro “-skel” com o caminho onde armazenei meu Skeleton modificado. Depois disso é mamão com açúcar.

Tags: ,
Topo

2 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