Instalando e usando o plugin paperClip no Ruby on Rails

O plugin paperClip do Ruby on Rails é muito bacana, agiliza muito a vida, com ele é possível fazer upload de imagens e já gerar várias dimensões da imagem.

Vou explicar como instalar e como usar.

Instalação:

Faça o download do paperClip: http://github.com/tarballs/thoughtbot-paperclip-18c0246c11c51dafa77b6367ddaf730684d0e752.zip

Descompacte o arquivo e renomei o diretório extraído para paperclip.

Coloque esse diretório dentro de seu_projeto/vendor/plugins/paperclip.

Você vai precisar do imageMagick, através dele o paperClip faz o resize nas imagens. Para instalar no Ubuntu 8.04 faça:

1
sudo apt-get install imagemagick

Configuração:

Para o paperClip funcionar você vai precisar que exista 4 novas colunas na sua tabela são elas: image_file_name, image_content_type, image_file_size, image_updated_at.

Esse nome image_… pode ser qualquer outra coisa por exemplo: avatar_…, foto_…

Seguindo essa ideia você poderia ter um migrate mais ou menos assim:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class CreateTournaments < ActiveRecord::Migration
  def self.up
    create_table :tournaments do |t|
      t.column :name,               :string,    :limit => 100, :null => false
      t.column :description,        :text,      :null => true
      t.column :image_file_name,    :string
      t.column :image_content_type, :string
      t.column :image_file_size,    :integer
      t.column :image_updated_at,   :timestamp
      t.column :is_active,          :boolean,   :null => false, :default => false
      t.column :start_date,         :date,      :null => false
      t.column :end_date,           :date,      :null => false
      t.column :created_at,         :timestamp, :null => false
      t.column :updated_at,         :timestamp, :null => false
    end
  end
 
  def self.down
    drop_table :tournaments
  end
end

Agora é necessário informar o seu model que existirá anexos e que formatos são aceitos:

1
2
3
4
5
6
7
8
9
10
class Tournament < ActiveRecord::Base
  has_attached_file :image,
                    :styles => {:large => '600x600>', :medium => '300x300>', :small => '150x150>', :thumb => '50x50>'},
                    :path => ":rails_root/public/images/:class/:id/:style_:basename.:extension",
                    :url => "/images/:class/:id/:style_:basename.:extension"
 
  validates_uniqueness_of :name
  validates_presence_of   :name, :start_date, :end_date
  validates_attachment_content_type :image, :content_type => ['image/jpeg', 'image/png', 'image/gif']
end

O interessante da linha abaixo, é que posso definir todos os tamanhos de imagens que desejo gerar em cima da imagem original, nesse trecho de código eu também alterei o path de armazenamento das imagens, fiz as imagens ficarem no diretório public_html/images/tournaments/id_do_banco_de_dados/imagens_em_diversas_dimensões:

1
2
3
4
  has_attached_file :image,
                    :styles => {:large => '600x600>', :medium => '300x300>', :small => '150x150>', :thumb => '50x50>'},
                    :path => ":rails_root/public/images/:class/:id/:style_:basename.:extension",
                    :url => "/images/:class/:id/:style_:basename.:extension"

A linha seguinte informa os tipos de mime types aceitos:

1
  validates_attachment_content_type :image, :content_type => ['image/jpeg', 'image/png', 'image/gif']

Agora você precisa definir que o seu formulário de cadastro trabalha com multipart, para enviar dados binários:

1
2
3
4
  <% form_for :tournament, @tournament, :url => { :action => 'create' }, :html => { :multipart => true } do |form| %>
    <%= render :partial => 'form', :locals => { :form => form } %>
    <div class="submit"><%= submit_tag "Criar" %></div>
  <% end %>

No código acima eu chamo o partial _form.rhtml, segue o seu conteúdo abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<%= error_messages_for 'tournament' %>
 
<!--[form:tournament]-->
<p><label for="tournament_name">Nome</label>
<%= form.text_field :name  %></p>
 
<p><label for="tournament_description">Descrição</label>
<%= form.text_area :description  %></p>
 
<p><label for="tournament_image">Imagem</label>
<%= form.file_field :image %></p>
 
<p><label for="tournament_is_active">Ativo</label>
<%= form.check_box :is_active  %>
 
<p><label for="tournament_start_date">Data de início</label>
<%= form.date_select(:start_date, :order => [:day, :month, :year], :use_month_numbers => true) %></p>
 
<p><label for="tournament_end_date">Data de término</label>
<%= form.date_select(:end_date, :order => [:day, :month, :year], :use_month_numbers => true)  %></p>
<!--[eoform:tournament]-->

Nesse partial _form.rhtml é declarado o campo para upload da imagem:

1
<%= form.file_field :image %>

Perceba que na tabela do banco de dados não existe a coluna chamada image, mas sim aquelas 4 que criei no migrate. Aqui que entra a mágica do paperClip juntamente com o model, ele consegue fazer o upload da imagem e definir os valores para os 4 campos no model.

Para exibir as imagens e suas diversas proporções use:

1
2
3
4
<%= image_tag @tournament.image.url(:thumb)  %>
<%= image_tag @tournament.image.url(:small)  %>
<%= image_tag @tournament.image.url(:medium)  %>
<%= image_tag @tournament.image.url(:large)  %>

Pronto! Depois de pegar o jeito com o paperClip você vai dar risada a toa. ;-)

Se você gostou desse texto e acha que ajudou você, me recomende: Recommend Me.

Posted in Ruby on Rails at dezembro 17th, 2008. No Comments.

Ruby on Rails efeito zebra em tabelas com helper cycle

Uma coisa legal de fazer em tabelas e adicionar o efeito zebra. Isso consiste em fazer que as cores de fundo da tabela sejam alternadas, isso facilita a leitura dos dados em uma tabela.

No Ruby on Rails existe o helper cycle, que permite dizer quais classes css devem ser alternadas.

1
2
3
4
<tr class="<%= cycle("even", "odd") %>">
  <td><%= tournament.name %></td>
  ...
</tr>

Nesse exemplo a classe css da linha será alternada entre as classes even e odd<

Desta forma no seu arquivo css, você pode definir algo do tipo:

1
2
3
4
5
6
7
tr.even {
  background: #cccccc;
}
 
tr.odd {
  background: #fff6e3;
}

Se você gostou desse texto e acha que ajudou você, me recomende: Recommend Me.

Posted in Ruby on Rails at dezembro 11th, 2008. No Comments.

Ruby on Rails com PostgreSQL 8.3 no Ubuntu

Para usar o PostgreSQL 8.3 junto com o Ruby on Rails, é necessário instalar além dos pacotes normais de instalação do banco de dados, mais os pacotes de desenvolvimento do PostgreSQL 8.3.

Faça o seguinte, instale os pacotes do PostgreSQL 8.3:

1
sudo apt-get install postgresql-8.3 postgresql-client-8.3 postgresql-server-dev-8.3 libpq-dev

Agora instale o driver para o Ruby on Rails:

1
sudo gem install postgres

Pronto! Boa diversão! ;-)

Se você gostou desse texto e acha que ajudou você, me recomende: Recommend Me.

Posted in Ruby on Rails at dezembro 10th, 2008. No Comments.

Especificando relacionamento nos modelos em Ruby on Rails

O Active Record suporta três tipos de relacionamentos entre tabelas: de um para um, um para muitos e de muitos para muitos. Você indica esses relacionamentos ao adicionar declarações aos seus modelos: has_one, has_many, belongs_to e o chamado has_add_belongs_to_many.

Relacionamento de um para um

Uma associação de um para um (ou, mais exatamente, um relacionamento de um para zero e de zero para um) é implementado com uma chave estrangeira em uma linha de uma tabela para referenciar pelo menos uma única linha em outra tabela. Um relacionamento de um para um poderia existir entre pedidos e faturas: para cada pedido há pelo menos uma fatura.

Relacionamento de um para um

Relacionamento de um para um

1
2
3
4
class Invoice < ActiveRecord::Base
  belongs_to :order
 #...
end
1
2
3
4
class Order < ActiveRecord::Base
  has_one :invoice
  #...
end

Como o exemplo mostra, declaramos isso no Rails, adicionando uma declaração has_one ao modelo Order e acrescentando uma declaração belongs_to ao modelo Invoice.

Há uma regra importante ilustrada aqui: o modelo para a tabela que contém a chave estrangeira sempre tem a declaração belongs_to.

Relacionamento de um para muitos

Uma associação de um para muitos permite representar uma coleção de objetos. Por exemplo, um pedido poderia conter um número qualquer de itens de linha associados. No banco de dados, todas as linhas da tabela do item de linha para um pedido particular contêm uma coluna de chave estrangeira que referencia esse pedido.

Relacionamento de um para muitos

Relacionamento de um para muitos

1
2
3
4
class LineItem < ActiveRecord::Base
  belongs_to :order
  #...
end
1
2
3
4
class Order < ActiveRecord::Base
  has_many :line_items
  #...
end

No Active Record, o objeto pai (o que contém logicamente uma coleção de objetos filhos) utiliza has_many para declarar seu relacionamento com a tabela filha, e esta utiliza belongs_to para indicar seu pai. No nosso exemplo, a classe LineItem belongs_to :order e a tabela orders has_many :line_items.

Observe mais uma vez que, como o item de linha contém a chave estrangeira, ele tem a declaração belongs_to.

Relacionamento de muitos para muitos

Por fim, poderíamos categorizar nossos produtos. Um produto pode pertencer a muitas categorias e cada categoria pode conter múltiplos produtos. Esse é um exemplo de um relacionamento de muitos para muitos. É como se cada lado do relacionamento contivesse uma coleção de itens no outro lado.

Relacionamento de muitos para muitos

Relacionamento de muitos para muitos

1
2
3
4
class Category < ActiveRecord::Base
  has_and_belongs_to_many :products
  #...
end
1
2
3
4
class Produt < ActiveRecord::Base
  has_and_belongs_to_many :categories
  #...
end

No Rails expressamos isso adicionando a declaração has_and_belongs_to_many aos dois modelos. De agora em diante, só utilizarei a forma abreviada, habtm, dessa declaração.

Associações de muitos para muitos são simétricas – as duas tabelas unidas declaram a associação entre uma e a outra utilizando habtm.

Dentro do banco de dados, associações de muitos para muitos são implementadas com a utilização de uma tabela de junção intermediária. Essa tabela contém pares de chave estrangeira que vinculam as duas tabelas alvo. O Active Record supõe que o nome dessa tabela de junção seja a concatenação dos dois nomes da tabela alvo em ordem alfabética. No nosso exemplo, associamos a tabela categories à tabela products e o Active Record irá procurar uma tabela de junção chamada categories_products.

Fonte: Thomas, Dave; Hansson, David Heinemeier. Desenvolvimento Web Ágil com Rails.

Se você gostou desse texto e acha que ajudou você, me recomende: Recommend Me.

Posted in Ruby on Rails at dezembro 6th, 2008. 1 Comment.

Uma breve reflexão sobre o PHP, Ruby e os respectivos frameworks symfony e Ruby on Rails

Eu trabalho com o framework PHP symfony, mas faço frees em Ruby on Rails. A diferença de tecnologia é imensa, ainda mais quando se leva em conta a produtividade, padronização e resultados.

O PHP ainda tem uma longa evolução a ser feita, a sua orientação a objetos tem vários problemas de design, e nem todos os padrões foram implementados ainda. O framework symfony por usa vez está em um momento de definição de padrão, existe muitos bugs sérios e algumas tecnologias empregadas em sua estrutura agregam burocracia demais para um framework focado em desenvolvimento ágil.

Eu ficou com o Ruby on Rails, estudo a fundo a sua estrutura e até agora só fiquei feliz com tudo que vi.

Se você gostou desse texto e acha que ajudou você, me recomende: Recommend Me.

Posted in PHP, Reflexões, Ruby, Ruby on Rails at dezembro 6th, 2008. No Comments.

A Falta de padronização dos retornos dos métodos de consulta do ORM Doctrine são irritantes

A Falta de padronização dos retornos dos métodos de consulta do Doctrine são irritantes, no ORM Doctrine as consultas realizadas com Doctrine_Query e o método findByDql retornam valores diferentes.

O Doctrine_Query retorna false, caso não satisfaça a condição where, já o método findByDql retorna um array vazio.

Essa falta de padronização é muito ruim.

Por exemplo:

1
2
3
4
$q = new Doctrine_Query();
$q->from('Status')->where('name = ?', 'exemplo');
$r = $q->execute();
var_dump($r);

Essa forma de consulta retorna false, caso name = ‘exemplo’ não exista. Eu acho esse retorno correto, ou null.

Mas o método findByDql tem um retorno fora de padrão, retornando um array vazio.

Por exemplo:

1
2
$status = Doctrine::getTable('Status')->findByDql("name = 'exemplo'");
var_dump($status);

O ruim é ter que usar essas tecnologias despadronizadas. :-(
Mais um ponto negativo para o Doctrine.

Posted in Doctrine at dezembro 5th, 2008. No Comments.

Populando um combobox através de um resultado em JSON usando o framework javascript Prototype

Vou mostrar como popular um combobox de cidades através de um resultado em JSON, usando o framework javascript Prototype.

Eu tenho um formulário com dois comboboxs um de estado e outro de cidade, quando o usuário selecionar o estado é buscada as cidades através de um webservice que retorna um JSON.

Meu formulário:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<form name="exemplo" id="exemplo" method="post">
  <table>
    <tbody>
      <tr>
        <th><label for="state">Estado</label></th>
        <td>
          <select id="state" name="state">
            <option value="0"/>
            <option value="1">AC</option>
            <option value="2">AL</option>
            <option value="3">AM</option>
            <option value="4">AP</option>
            <option value="5">BA</option>
            <option value="6">CE</option>
            <option value="7">DF</option>
            <option value="8">ES</option>
            <option value="9">GO</option>
            <option value="10">MA</option>
            <option value="11">MG</option>
            <option value="12">MS</option>
            <option value="13">MT</option>
            <option value="14">PA</option>
            <option value="15">PB</option>
            <option value="16">PE</option>
            <option value="17">PI</option>
            <option value="18">PR</option>
            <option value="19">RJ</option>
            <option value="20">RN</option>
            <option value="21">RO</option>
            <option value="22">RR</option>
            <option value="23">RS</option>
            <option value="24">SC</option>
            <option value="25">SE</option>
            <option value="26">SP</option>
            <option value="27">TO</option>
          </select>
        </td>
      </tr>
      <tr>
        <th><label for="city">Cidade</label></th>
        <td>
          <select id="city" name="city"></select>
        </td>
      </tr>
    </tbody>
  </table>
</form>

Para todo o código javascript abaixo funcionar, é necessário ter as bibliotecas javascripts do Prototype incluídas na página.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<script language="JavaScript">
// Quando ocorrer o evento onchange no combobox chama a função getCitiesByStateId
$('state').observe('change', function() { getCitiesByStateId() });
 
/**
 * Busca todas cidades de um estado
 * @param object obj
 *  Informações do elemento
 */
function getCitiesByStateId()
{
  var state = $('state');
 
  if (state.value > 0)
  {
    var url = '/url_do_seu_webservice_que_vai_retornar_o_json/state_id/' + state.value;
    makeRequest(url, 'city');
  }
}
 
/**
 * Realiza a requisição ajax para o webservice
 * obtém os resultados e preenche o combobox
 * @param strung url
 *  Url da requisição
 * @param string combobox_fill_id
 *  Combobox para ser preenchida
 */
function makeRequest(url, combobox_fill_id)
{
  new Ajax.Request(url,
  {
    method: 'get',
    onLoading: function() { loadingComboBox(combobox_fill_id) },
    onSuccess: function(transport)
    {
      if (200 == transport.status)
      {
        var response = eval('(' + transport.responseText + ')');
        fillComboBox(combobox_fill_id, response);
      }
    }
  });
}
 
/**
 * Preenche a combobox com os valores de retorno
 * @param string name
 *  Combobox a ser preenchida
 * @param object response
 *  Objeto de resposta
 */
function fillComboBox(name, response)
{
  cleanComboBox(name);
 
  $(name).insert(new Element('option', {value: ''}).insert(''));
  response.each
  (
    function(pair)
    {
      $(name).insert(new Element('option', {value: pair.id}).insert(pair.name));
    }
  );
}
 
/**
 * Remove todos os elementos da combobox
 * @param string name
 *  Id da combobox para limpar
 */
function cleanComboBox(name)
{
  $(name).descendants().invoke('remove');
}
 
/**
 * Exibe a mensagem de aguarde... na combobox enquanto os dados estão sendo requisitados
 */
function loadingComboBox(name)
{
  cleanComboBox(name);
  $(name).insert(new Element('option', {value: ''}).insert('Aguarde...'));
}
</script>

O código acima não precisa de muitas explicações, já está bem dividido em funções e comentado.

O meu webservice retorna um JSON no seguinte formato:

1
2
[[3037,"Abati\u00e1"],[3038,"Adrian\u00f3polis"],[3039,"Agudos do Sul"],[160,"Almirante Tamandar\u00e9"],[3040,"Altamira do Paran\u00e1"],[3043,"Alt\u00f4nia"],[3041,"Alto Paran\u00e1"],[3042,"Alto Piquiri"],[3044,"Alvorada do Sul"],[3045,"Amapor\u00e3"],[3046,"Amp\u00e9re"],[3047,"Anahy"],[3048,"Andir\u00e1"],[3049,"\u00c2ngulo"],[3050,"Antonina"],[3051,"Ant\u00f4nio Olinto"],[174,"Apucarana"],[163,"Arapongas"],[3052,"Arapoti"],[3053,"Arapu\u00e3"],[3054,"Araruna"],[173,"Arauc\u00e1ria"],[3055,"Ariranha do Iva\u00ed"],[3056,"Assa\u00ed"],[3057,"Assis Chateaubriand"],[3058,"Astorga"],[3059,"Atalaia"],[3060,"Balsa Nova"],[3061,"Bandeirantes"],[3062,"Barbosa Ferraz"],[3064,"Barrac\u00e3o"],[3063,"Barra do Jacar\u00e9"],[3065,"Bela Vista da Caroba"],[3066,"Bela Vista do Para\u00edso"],[3067,"Bituruna"],[3068,"Boa Esperan\u00e7a"],[3069,"Boa Esperan\u00e7a do Igua\u00e7u"],[3070,"Boa Ventura de S\u00e3o Roque"],[3071,"Boa Vista da Aparecida"],[3072,"Bocai\u00fava do Sul"],[3073,"Bom Jesus do Sul"],[3074,"Bom Sucesso"],[3075,"Bom Sucesso do Sul"],[3076,"Borraz\u00f3polis"],[3077,"Braganey"],[3078,"Brasil\u00e2ndia do Sul"],[3079,"Cafeara"],[3080,"Cafel\u00e2ndia"],[3081,"Cafezal do Sul"],[3082,"Calif\u00f3rnia"],[3083,"Cambar\u00e1"],[170,"Camb\u00e9"],[3084,"Cambira"],[3085,"Campina da Lagoa"],[3086,"Campina do Sim\u00e3o"],[3087,"Campina Grande do Sul"],[3088,"Campo Bonito"],[3089,"Campo do Tenente"],[159,"Campo Largo"],[3090,"Campo Magro"],[162,"Campo Mour\u00e3o"],[3091,"C\u00e2ndido de Abreu"],[3092,"Cand\u00f3i"],[3093,"Cantagalo"],[3094,"Capanema"],[3095,"Capit\u00e3o Le\u00f4nidas Marques"],[3096,"Carambe\u00ed"],[3097,"Carl\u00f3polis"],[166,"Cascavel"],[161,"Castro"],[3098,"Catanduvas"],[3099,"Centen\u00e1rio do Sul"],[3100,"Cerro Azul"],[3101,"C\u00e9u Azul"],[3102,"Chopinzinho"],[3103,"Cianorte"],[3104,"Cidade Ga\u00facha"],[3105,"Clevel\u00e2ndia"],[171,"Colombo"],[3106,"Colorado"],[3107,"Congonhinhas"],[3108,"Conselheiro Mairinck"],[3109,"Contenda"],[3110,"Corb\u00e9lia"],[3111,"Corn\u00e9lio Proc\u00f3pio"],[3112,"Coronel Domingos Soares"],[3113,"Coronel Vivida"],[3114,"Corumbata\u00ed do Sul"],[3116,"Cruzeiro do Igua\u00e7u"],[3117,"Cruzeiro do Oeste"],[3118,"Cruzeiro do Sul"],[3115,"Cruz Machado"],[3119,"Cruzmaltina"],[164,"Curitiba"],[3120,"Curi\u00fava"],[3121,"Diamante d'Oeste"],[3122,"Diamante do Norte"],[3123,"Diamante do Sul"],[3124,"Dois Vizinhos"],[3125,"Douradina"],[3126,"Doutor Camargo"],[3127,"Doutor Ulysses"],[3128,"En\u00e9as Marques"],[3129,"Engenheiro Beltr\u00e3o"],[3130,"Entre Rios do Oeste"],[3131,"Esperan\u00e7a Nova"],[3132,"Espig\u00e3o Alto do Igua\u00e7u"],[3133,"Farol"],[3134,"Faxinal"],[3135,"Fazenda Rio Grande"],[3136,"F\u00eanix"],[3137,"Fernandes Pinheiro"],[3138,"Figueira"],[3140,"Flora\u00ed"],[3139,"Flor da Serra do Sul"],[3141,"Floresta"],[3142,"Florest\u00f3polis"],[3143,"Fl\u00f3rida"],[3144,"Formosa do Oeste"],[152,"Foz do Igua\u00e7u"],[3145,"Foz do Jord\u00e3o"],[3146,"Francisco Alves"],[153,"Francisco Beltr\u00e3o"],[3147,"General Carneiro"],[3148,"Godoy Moreira"],[3149,"Goioer\u00ea"],[3150,"Goioxim"],[3151,"Grandes Rios"],[3152,"Gua\u00edra"],[3153,"Guaira\u00e7\u00e1"],[3154,"Guamiranga"],[3155,"Guapirama"],[3156,"Guaporema"],[3157,"Guaraci"],[3158,"Guarania\u00e7u"],[168,"Guarapuava"],[3159,"Guaraque\u00e7aba"],[3160,"Guaratuba"],[3161,"Hon\u00f3rio Serpa"],[3162,"Ibaiti"],[3163,"Ibema"],[3164,"Ibipor\u00e3"],[3165,"Icara\u00edma"],[3166,"Iguara\u00e7u"],[3167,"Iguatu"],[3168,"Imba\u00fa"],[3169,"Imbituva"],[3170,"In\u00e1cio Martins"],[3171,"Inaj\u00e1"],[3172,"Indian\u00f3polis"],[3173,"Ipiranga"],[3174,"Ipor\u00e3"],[3175,"Iracema do Oeste"],[3176,"Irati"],[3177,"Iretama"],[3178,"Itaguaj\u00e9"],[3179,"Itaipul\u00e2ndia"],[3180,"Itambarac\u00e1"],[3181,"Itamb\u00e9"],[3182,"Itapejara d'Oeste"],[3183,"Itaperu\u00e7u"],[3184,"Ita\u00fana do Sul"],[3185,"Iva\u00ed"],[3186,"Ivaipor\u00e3"],[3187,"Ivat\u00e9"],[3188,"Ivatuba"],[3189,"Jaboti"],[3190,"Jacarezinho"],[3191,"Jaguapit\u00e3"],[3192,"Jaguaria\u00edva"],[3193,"Jandaia do Sul"],[3194,"Jani\u00f3polis"],[3195,"Japira"],[3196,"Japur\u00e1"],[3197,"Jardim Alegre"],[3198,"Jardim Olinda"],[3199,"Jataizinho"],[3200,"Jesu\u00edtas"],[3201,"Joaquim T\u00e1vora"],[3202,"Jundia\u00ed do Sul"],[3203,"Juranda"],[3204,"Jussara"],[3205,"Kalor\u00e9"],[3206,"Lapa"],[3207,"Laranjal"],[177,"Laranjeiras do Sul"],[3208,"Le\u00f3polis"],[3209,"Lidian\u00f3polis"],[3210,"Lindoeste"],[3211,"Loanda"],[3212,"Lobato"],[165,"Londrina"],[3213,"Luiziana"],[3214,"Lunardelli"],[3215,"Lupion\u00f3polis"],[3216,"Mallet"],[3217,"Mambor\u00ea"],[3218,"Mandagua\u00e7u"],[3219,"Mandaguari"],[3220,"Mandirituba"],[3221,"Manfrin\u00f3polis"],[3222,"Mangueirinha"],[3223,"Manoel Ribas"],[3224,"Marechal C\u00e2ndido Rondon"],[3225,"Maria Helena"],[3226,"Marialva"],[3227,"Maril\u00e2ndia do Sul"],[3228,"Marilena"],[3229,"Mariluz"],[151,"Maring\u00e1"],[3230,"Mari\u00f3polis"],[3231,"Marip\u00e1"],[3232,"Marmeleiro"],[3233,"Marquinho"],[3234,"Marumbi"],[3235,"Matel\u00e2ndia"],[3236,"Matinhos"],[3237,"Mato Rico"],[3238,"Mau\u00e1 da Serra"],[3239,"Medianeira"],[3240,"Mercedes"],[3241,"Mirador"],[3242,"Miraselva"],[3243,"Missal"],[3244,"Moreira Sales"],[3245,"Morretes"],[3246,"Munhoz de Melo"],[3247,"Nossa Senhora das Gra\u00e7as"],[3248,"Nova Alian\u00e7a do Iva\u00ed"],[3249,"Nova Am\u00e9rica da Colina"],[3250,"Nova Aurora"],[3251,"Nova Cantu"],[3252,"Nova Esperan\u00e7a"],[3253,"Nova Esperan\u00e7a do Sudoeste"],[3254,"Nova F\u00e1tima"],[3255,"Nova Laranjeiras"],[3256,"Nova Londrina"],[3257,"Nova Ol\u00edmpia"],[3258,"Nova Prata do Igua\u00e7u"],[3259,"Nova Santa B\u00e1rbara"],[3260,"Nova Santa Rosa"],[3261,"Nova Tebas"],[3262,"Novo Itacolomi"],[3263,"Ortigueira"],[3264,"Ourizona"],[3265,"Ouro Verde do Oeste"],[3266,"Pai\u00e7andu"],[3267,"Palmas"],[3268,"Palmeira"],[3269,"Palmital"],[3270,"Palotina"],[3271,"Para\u00edso do Norte"],[3272,"Paranacity"],[167,"Paranagu\u00e1"],[3273,"Paranapoema"],[155,"Paranava\u00ed"],[3274,"Pato Bragado"],[169,"Pato Branco"],[3275,"Paula Freitas"],[3276,"Paulo Frontin"],[3277,"Peabiru"],[3278,"Perobal"],[3279,"P\u00e9rola"],[3280,"P\u00e9rola d'Oeste"],[3281,"Pi\u00ean"],[176,"Pinhais"],[3283,"Pinhal\u00e3o"],[3282,"Pinhal de S\u00e3o Bento"],[3284,"Pinh\u00e3o"],[3285,"Pira\u00ed do Sul"],[156,"Piraquara"],[3286,"Pitanga"],[3287,"Pitangueiras"],[3288,"Planaltina do Paran\u00e1"],[3289,"Planalto"],[154,"Ponta Grossa"],[3290,"Pontal do Paran\u00e1"],[3291,"Porecatu"],[3292,"Porto Amazonas"],[3293,"Porto Barreiro"],[3294,"Porto Rico"],[3295,"Porto Vit\u00f3ria"],[3296,"Prado Ferreira"],[3297,"Pranchita"],[3298,"Presidente Castelo Branco"],[3299,"Primeiro de Maio"],[3300,"Prudent\u00f3polis"],[3301,"Quarto Centen\u00e1rio"],[3302,"Quatigu\u00e1"],[3303,"Quatro Barras"],[3304,"Quatro Pontes"],[3305,"Quedas do Igua\u00e7u"],[3306,"Quer\u00eancia do Norte"],[3307,"Quinta do Sol"],[3308,"Quitandinha"],[3309,"Ramil\u00e2ndia"],[3310,"Rancho Alegre"],[3311,"Rancho Alegre d'Oeste"],[3312,"Realeza"],[3313,"Rebou\u00e7as"],[3314,"Renascen\u00e7a"],[3315,"Reserva"],[3316,"Reserva do Igua\u00e7u"],[3317,"Ribeir\u00e3o Claro"],[3318,"Ribeir\u00e3o do Pinhal"],[3319,"Rio Azul"],[3320,"Rio Bom"],[3321,"Rio Bonito do Igua\u00e7u"],[3322,"Rio Branco do Iva\u00ed"],[3323,"Rio Branco do Sul"],[3324,"Rio Negro"],[3325,"Rol\u00e2ndia"],[3326,"Roncador"],[3327,"Rondon"],[3328,"Ros\u00e1rio do Iva\u00ed"],[3329,"Sab\u00e1udia"],[3330,"Salgado Filho"],[3331,"Salto do Itarar\u00e9"],[3332,"Salto do Lontra"],[3333,"Santa Am\u00e9lia"],[3334,"Santa Cec\u00edlia do Pav\u00e3o"],[3335,"Santa Cruz de Monte Castelo"],[3336,"Santa F\u00e9"],[3337,"Santa Helena"],[3338,"Santa In\u00eas"],[3339,"Santa Isabel do Iva\u00ed"],[3340,"Santa Izabel do Oeste"],[3341,"Santa L\u00facia"],[3342,"Santa Maria do Oeste"],[3343,"Santa Mariana"],[3344,"Santa M\u00f4nica"],[3347,"Santana do Itarar\u00e9"],[3345,"Santa Tereza do Oeste"],[3346,"Santa Terezinha de Itaipu"],[3348,"Santo Ant\u00f4nio da Platina"],[3349,"Santo Ant\u00f4nio do Caiu\u00e1"],[3350,"Santo Ant\u00f4nio do Para\u00edso"],[3351,"Santo Ant\u00f4nio do Sudoeste"],[3352,"Santo In\u00e1cio"],[3353,"S\u00e3o Carlos d
o Iva\u00ed"],[3354,"S\u00e3o Jer\u00f4nimo da Serra"],[3355,"S\u00e3o Jo\u00e3o"],[3356,"S\u00e3o Jo\u00e3o do Caiu\u00e1"],[3357,"S\u00e3o Jo\u00e3o do Iva\u00ed"],[3358,"S\u00e3o Jo\u00e3o do Triunfo"],[3359,"S\u00e3o Jorge d'Oeste"],[3360,"S\u00e3o Jorge do Iva\u00ed"],[3361,"S\u00e3o Jorge do Patroc\u00ednio"],[3362,"S\u00e3o Jos\u00e9 da Boa Vista"],[3363,"S\u00e3o Jos\u00e9 das Palmeiras"],[175,"S\u00e3o Jos\u00e9 dos Pinhais"],[3364,"S\u00e3o Manoel do Paran\u00e1"],[3365,"S\u00e3o Mateus do Sul"],[3366,"S\u00e3o Miguel do Igua\u00e7u"],[3367,"S\u00e3o Pedro do Igua\u00e7u"],[3368,"S\u00e3o Pedro do Iva\u00ed"],[3369,"S\u00e3o Pedro do Paran\u00e1"],[3370,"S\u00e3o Sebasti\u00e3o da Amoreira"],[3371,"S\u00e3o Tom\u00e9"],[3372,"Sapopema"],[150,"Sarandi"],[3373,"Saudade do Igua\u00e7u"],[3374,"Seng\u00e9s"],[3375,"Serran\u00f3polis do Igua\u00e7u"],[3376,"Sertaneja"],[3377,"Sertan\u00f3polis"],[3378,"Siqueira Campos"],[3379,"Sulina"],[3380,"Tamarana"],[3381,"Tamboara"],[3382,"Tapejara"],[3383,"Tapira"],[3384,"Teixeira Soares"],[158,"Tel\u00eamaco Borba"],[3385,"Terra Boa"],[3386,"Terra Rica"],[3387,"Terra Roxa"],[3388,"Tibagi"],[3389,"Tijucas do Sul"],[172,"Toledo"],[3390,"Tomazina"],[3391,"Tr\u00eas Barras do Paran\u00e1"],[3392,"Tunas do Paran\u00e1"],[3393,"Tuneiras do Oeste"],[3394,"Tup\u00e3ssi"],[3395,"Turvo"],[3396,"Ubirat\u00e3"],[157,"Umuarama"],[3397,"Uni\u00e3o da Vit\u00f3ria"],[3398,"Uniflor"],[3399,"Ura\u00ed"],[3400,"Ventania"],[3401,"Vera Cruz do Oeste"],[3402,"Ver\u00ea"],[3403,"Vila Alta"],[3404,"Virmond"],[3405,"Vitorino"],[3406,"Wenceslau Braz"],[3407,"Xambr\u00ea"]]
Posted in Javascript at dezembro 4th, 2008. No Comments.

symfony usar o object_select_tag para retornar os valores ordenados

Esses tempos estava usando o object_select_tag (é uma mão na roda), mas precisava trazer os resultados em ordem crescente. Foi ae que eu descobri que existe uma opção peer_method onde é possível definir o método no model para retornar a lista de resultados.

Eu não curto o Propel, prefiro bem mais o Doctrine. Desta forma esse exemplo é baseado usando o Doctrine:

No template:

1
2
3
4
5
6
7
<?php echo object_select_tag(isset($filters['Status']) ? $filters['Status'] : null, null, array (
  'include_blank' => true,
  'related_class' => 'Status',
  'text_method' => '__toString',
  'control_name' => 'filters[Status]',
  'peer_method' => 'getSorted'
)) ?>

No model StatusTable.class.php:

1
2
3
4
5
6
7
public static function getSorted()
{
  $q = new Doctrine_Query();
  $q->from('Status')->orderBy('name ASC');
 
  return $q->execute();
}

No código acima que é possível definir a forma de ordenação e qual coluna que deve ser usada para ordenar.

Agora falta criar o método __toString no model Status.class.php:

1
2
3
4
public function __toString()
{
  return $this->name;
}

O método __toString e usado para imprimir os valores no combo box.

Pronto! ;-)

Posted in Symfony at dezembro 2nd, 2008. No Comments.

 Assinar RSS Feed