AttributesBackupBehavior extension for the Yii Framework

Sometimes, when using ActiveRecord, we need to know if some attribute value has changed after it was
loaded from the database. To make things easier and avoid duplicated code, I created this behavior
to help me with this task. Basically, what it does is to make a copy of all attributes values from
the object, right after it is loaded from the database, so the behavior can work with this original
values to know if any of them was changed.

Usage
The behavior usage is pretty simple. First, add the behavior to the ActiveRecord where you intend to
use it:

<?php 
class User extends ActiveRecord {
	...
	public function behaviors() {
		return array(
			'AttributesBackupBehavior' => 'ext.AttributesBackupBehavior',
		);
	}
	...
}
?>

After this, 3 new methods will be added to your User class: attributesChanged(), attributeChanged()
and getOldAttributeValue().

Below is a description of it:

attributesChanged()
With this method you can check if any of the object attributes was changed after it was loaded from
the database:

<?php
$user = User::model()->find(); // User->status == 'Active'
var_dump($user->attributesChanged()); // FALSE
$user->status = 'Inactive';
var_dump($user->attributesChanged()); // TRUE
?>

attributeChanged()
This should be used when you want to know if a specific attribute value was changed:

<?php
$user = User::model()->find(); // User->status == 'Active'
$user->status = 'Inactive';
var_dump($user->attributeChanged('email')); // FALSE
var_dump($user->attributeChanged('status')); // TRUE
?>

getOldAttributeValue()
In case you need to retrieve the original value loaded from the database, this is the method you gonna use:

<?php
$user = User::model()->find(); // User->status == 'Active'
$user->status = 'Inactive';
var_dump($user->getOldAttributeValue('status')); // 'Active'
?>

Besides those three methods, there’s also one property that can be configured in this behavior:
$reloadAfterSave.

By default, after you call the ActiveRecord save method, the old attributes will be erased and filled
with the just saved new ones, show it will reflect the stored record. If for some reason you want
to keep the original loaded values, you need to set $reloadAfterSave as false:

<?php 
class User extends ActiveRecord {
	...
	public function behaviors() {
		return array(
			'AttributesBackupBehavior' => array(
				'class' => 'ext.AttributesBackupBehavior',
				'reloadAfterSave' => false,
		);
	}
	...
}
?>

You can donwload the extension from its page at the Yii Framework site: http://www.yiiframework.com/extension/attributesbackupbehavior/

You can also fork the projet at Github: https://github.com/davialexandre/AttributesBackupBehavior

Trabalhando com contadores utilizando o ActiveRecord no Yii Framework

Vamos supor, por exemplo, que você está desenvolvendo um blog ou algum outro tipo de CMS e quer registrar o número de vezes que cada post foi acessado (talvez para mostrar uma lista dos mais visitados).

A maneira mais fácil de fazer isso é adicionar uma coluna na tabela de posts, que será utilizada para armazenar o número de visitas para aquele item. Cada vez que um post for exibido o valor dessa coluna será aumentado em 1. O código para fazer isso é algo parecido com isso:

public function actionView($id) {
    $post = Post::model()->findByPk($id);
    $post->visitas += 1;
    $post->save();
    $this->render('view', array('post' => $post));
}

Essa abordagem tem dois problemas. Queremos atualizar apenas a coluna visitas, mas todo o registro do post será atualizado. Além disse, caso o argumento false não seja passado para o método save, todo o processo de validação será executado.

Desde a versão 1.1.8, a classe CActiveRecord tem um método que pode nos ajudar com isso. Trata-se do método CActiveRecord::saveCounters() e a sua utilização é muito simples:

public function actionView($id) {
    $post = Post::model()->findByPk($id);
    $post->saveCounters(array('visitas'=>1));
    $this->render('view', array('post' => $post));
}

Com isso, somente a coluna visitas será atualizada e a validação não será disparada.

Caso você ainda não utilize a versão 1.1.8, você pode utilizar o método CActiveRecord::updateCounters():

public function actionView($id) {
    $post = Post::model()->findByPk($id);
    $post->updateCounters(
        array('visitas'=>1),
        array('condition' => "id = :id"),
        array(':id' => $this->id),
    );
    $this->render('view', array('post' => $post));
}

O trecho de código acima faz exatamente o mesmo que o saveCounters. Note que o método updateCounters tem dois parâmetros a mais. Depois de informar a coluna utilizada para contar, é preciso passar uma condição que diz qual registro deverá ser atualizado. Se você utilizou algum parâmetro nessa condição (como o “:id”), você também deve informar o valor dele, utilizando o terceiro argumento do método.

Importante: Se você não informar nenhuma condição, todos os registros serão atualizados.

Working with counters using the ActiveRecord in Yii Framework

Let’s say, for example, that you are developing a blog or some kind of CMS and you want to track the number of times each post was viewed (maybe to show a list of the most viewed ones).

The easier way to do that is by adding a column to the post table, which will be used to store the number of visits of that item. Each time the post is displayed the value of this column will be increased by 1. The code for this would be something like:

public function actionView($id) {
    $post = Post::model()->findByPk($id);
    $post->visits += 1;
    $post->save();
    $this->render('view', array('post' => $post));
}

We have two problems with this approach. All we want is to update the visits column, but the entire record will be updated. Also, if we didn’t pass the false argument to the save method, all the validation process will be executed.

Since 1.1.8, the CActiveRecord class has a method that can help us with this task. It’s the CActiveRecord::saveCounters() method and its usage is pretty simple:

public function actionView($id) {
    $post = Post::model()->findByPk($id);
    $post->saveCounters(array('visits'=>1));
    $this->render('view', array('post' => $post));
}

With this, only the visits column will be updated and the validation will not be triggered.

If you’re not using the 1.1.8 yet, there’s the CActiveRecord::updateCounters() method:

public function actionView($id) {
    $post = Post::model()->findByPk($id);
    $post->updateCounters(
        array('visits'=>1),
        array('condition' => "id = :id"),
        array(':id' => $this->id),
    );
    $this->render('view', array('post' => $post));
}

The piece of code above does exactly the same that saveCounters does. Note that updateCounters has two additional parameters. After the column used to count, we pass a condition to tell which record(s) should be updated. If you used any paramater in this condition (like the “:id”), you should pass the parameter value in the third argument of the method.

Important: If you do not pass a condition, all records will be updated.

Backup simples com Incron+rsync+Dropbox

Eu ja perdi um HD cheio de coisas importantes. Foi assim que aprendi a importância do backup; o que não quer dizer que eu o faça.

Contudo, recentemente o disco do meu notebook começou a dar sinais de que está agonizando. Na verdade, não sei se trata-se apenas de problemas no sistema de arquivos ou um falha de hardware iminente, mas os constantes erros me trouxeram más lembranças e me deixaram com medo.

Desde a perda do primeiro HD, tenho procurado uma solução simples e eficiente para backups. Tenho pensando bastante em um NAS, com suporte a RAID ou um PC Mini-ITX. Enquanto o dinheiro não vem, e os erros aumentam, encontrei uma solução mais simples para o backup de meus arquivos, composta dos seguintes itens:

Continue reading

Plugin Musicbrainz para o Exaile

Por muito tempo eu fui usuário do Amarok. Para mim ele era O music player. Mas um dia resolveram atualiza-lo e lançaram a versão 2.0, uma bela porcaria! Desde então testei diversos tocadores: Listen, RhythmBox e Banshee e nenhum me agradou. Foi então que encontrei o Exaile e gostei.

Trata-se de um player leve, escrito em python e utilizando GTK. Sem frescuras, tem poucos recursos, praticamente todos os que eu utilizava no Amarok. Até a interface lembra bastante a versão antiga do meu último player. Entretanto senti falta de uma funcionalidade: Preencher as tags do arquivo com informações do Musicbrainz. É comum ter arquivos com tags fora de padrão, ou até mesmo sem nenhuma informação nelas e arrumar isso na mão é um saco. Para resolver o problema, criei um plugin!

Continue reading

Let’s GO! (Instalando o GO)

O Google acaba de lançar sua própria linguagem de programação, a GO.

Trata-se de uma liguagem “meio frankestein”, com uma sintaxe com características de C/C++, Python, Ruby e alguma outra coisa que eu não consigo identificar.

GO é uma linguagem compilada e, até o momento, existem compiladores apenas para ambientes *nix. Acabo de instalar aqui no meu Xubuntu e o processo foi bastante simples.

Continue reading

Instalando o Ubuntu 8.10 no notebook Kennex U50SA

Acabo de comprar um notebook. O modelo escolhido foi o Kennex U50SA.

Como a Kennex, em um raro momento de sabedoria, não considera a troca de sistema operacional um procedimento que viola a garantia do produto, a primeira coisa que fiz foi remover o Insigne Linux que vem instalado no notebook e instalar o Windows 7 em dual boot com o Ubuntu 8.10.

A instalação do Windows 7 ocorreu sem nenhum problema. Tudo funcionando perfeitamente nesse sistema. Ao término da instalação somente foi necessário instalar os drivers da placa de rede com e sem fio.

Já para a instalação do Ubuntu são necessárias algumas configurações.

Continue reading

Apostilas de Gimp, OpenOffice e outros

Desde 1997 o Metrô de São Paulo, gradativamente, vem substituindo softwares proprietários em suas estações de trabalho por equivalentes livres. Segundo informações da Companhia, apenas a substituição do Ms Office pelo OpenOffice gerou uma economia de aproximadamente R$ 1,4 milhões por ano.

Durante esse período de transição, a equipe de TI do Metrô desenvolveu algumas apostilas utilizadas para treinamentos dos funcionários da empresa. Seguindo a filosofia do software livre, as apostilas desenvolvidas foram disponibilizadas livremente para download no seguinte endereço:

http://www.metro.sp.gov.br/diversos/download/tedownload.asp

Na página acima é possível encontrar apostilas de GIMP, Gantt Project, Firefox, Thunderbird e OpenOffice.

Uma lista, muitas opções

Procurando uma solução para um menu com listas aninhadas, acabei encontrando o site da Maxdesign e sua seção de CSS.

Lá você encontra o Listutorial, um tutorial mostrando o básico sobre formatação de listas com CSS. Além disso temos o Listmatic e o Listmatic2. Nessas seções o pessoal da maxdesign parte da idéia de “Uma lista, muitas opções” e mostram como é possível, a partir de uma simples lista, criar os mais diversos tipos de menus. Em Listmatic, existem vários exemplos utilizando um lista não ordenada simples, já em Listmatic2 os exemplos utilizam uma lista não ordenada aninhada.

Todos os exemplos utilizam apenas CSS e HTML e possuem uma tabela de compatibilidade com navegadores.

Evitando a fadiga com Python

Desde pequeno aprendi com Jaiminho à evitar a fadiga. Faz bem para a saúde.

Foi assim que resolvi desenvolver um script para baixar imagens de sites. Várias vezes gastei meu dedo, clicando com o botão direito do mouse sobre imagens e mais imagens e escolhendo “salvar como”. Agora, com o script, basta apenas informar a url da página com as imagens e todo o trabalho é feito em alguns segundos (ou minutos, dependendo do tamanho da imagem):

import urllib
import re

for img in re.findall("<img .*?src=\"(.+?)\"", urllib.urlopen("http://www.uol.com.br").read()):
    arq_imagem = open(img[img.rfind('/') + 1:], 'wb')
    arq_imagem.write(urllib.urlopen(img).read())
    arq_imagem.close()

Continue reading