Como trabalhar com array de checkboxes opcionais
Imagine a seguinte situação: você precisa fazer um sistema de cadastro de veículos para uma vitrine virtual.
O nosso sistema venderá “carros famosos”, por isso cada carro possui um nome (Batmóvel 1941, Tumbler, Herbie, Dick Vigarista, Penélope Charmosa).. um ano de fabricação (1941, 2008..), e um modelo (esporte, clássico, corrida, tanque de guerra)..
Um das primeiras coisas que vem a nossa mente, é modelar os atributos dessa entidade veículo.
mysql>DESCRIBE vehicles_wrong;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(50) | YES | | NULL | |
| year | char(4) | YES | | NULL | |
| model | varchar(30) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
Tranquilo, certo ?
Um id para identificarmos cada veículo como único, o nome, ano e modelo.
Mas, o Batmóvel de 1941 tem acessórios diferentes do Tumbler de 2008 (o tanque de guerra do filme Batman Begins), certo ?
E precisamos mostrar isso para nossos usuários, dizendo se o veículo possui ou não: ar condicionado, travas elétricas, tiro de canhão, moto acoplada para fuga.. no caso do carro do Dick Vigarista: turbinas propulsoras!
O problema
Poderíamos adicionar esses itens na entidade veículos:
mysql> DESCRIBE vehicles_wrong;
+-----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(50) | YES | | NULL | |
| year | char(4) | YES | | NULL | |
| model | varchar(30) | YES | | NULL | |
| air_con | bit(1) | YES | | NULL | |
| bluetooth | bit(1) | YES | | NULL | |
+-----------+-------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
Só que ai precisamos ainda colocar:
mysql>DESCRIBE vehicles_wrong;
+------------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(50) | YES | | NULL | |
| year | char(4) | YES | | NULL | |
| model | varchar(30) | YES | | NULL | |
| air_con | bit(1) | YES | | NULL | |
| bluetooth | bit(1) | YES | | NULL | |
| parking_sensors | bit(1) | YES | | NULL | |
| escape_motorcyle | bit(1) | YES | | NULL | |
| plasma_cannon | bit(1) | YES | | NULL | |
| cruise_control | bit(1) | YES | | NULL | |
| leather | bit(1) | YES | | NULL | |
| metallic_paint | bit(1) | YES | | NULL | |
| electric_locks | bit(1) | YES | | NULL | |
| xenon_lights | bit(1) | YES | | NULL | |
| nitro | bit(1) | YES | | NULL | |
| wings | bit(1) | YES | | NULL | |
+------------------+-------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
E para cada opcional extra que precisamos cadastrar iríamos adicionando uma nova coluna na tabela veículos, mesmo que o Motor de Propulsão seja um item específico do carro do Dick Vigarista, e os demais carros irão sempre deixar essa coluna em branco (como false).
Isso por si só, já mostra um grave problema de modelagem: não está escalável!
Ter que alterar o modelo e possuir diversas colunas sem valores no banco de dados é um exemplo de modelagem desnormalizada que devemos evitar.
A solução
Em vez de ficarmos criando novas colunas a cada opcional novo que precisamos listar, temos que modelar melhor nossa entidade e dividir a entidade veículos em duas: veículos e opcionais, veja:
mysql>DESCRIBE optional; DESCRIBE vehicle_optional; DESCRIBE vehicles;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(50) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
+-------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------+------+-----+---------+-------+
| id_vehicle | int(11) | YES | | NULL | |
| id_optional | int(11) | YES | | NULL | |
+-------------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(50) | YES | | NULL | |
| year | char(4) | YES | | NULL | |
| model | varchar(30) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
Ficando assim, a cargo de uma terceira tabela de relacionamento o cruzamento da informação, sobre quais opcionais cada carro tem.
E para cada novo opcional extra que tivermos que cadastrar no sistema, apenas adicionamos um linha (registro) na tabela optional, em vez de uma coluna na tabela veículos. Entendeu a diferença ?
-> Não precisamos alterar nada no banco de dados para disponibilizar um novo opcional.
-> Não precisamos mexer nos nossos códigos backend;
-> Nem na nossa query SQL!
O impacto de adicionar opcionais (mostrar mais checkboxes) é apenas cadastrar um novo registro na tabela.
mysql>SELECT * FROM optional;
+----+------------------+
| id | name |
+----+------------------+
| 1 | air_con |
| 2 | bluetooth |
| 3 | parking_sensors |
| 4 | escape_motorcyle |
| 5 | plasma_cannon |
| 6 | cruise_control |
| 7 | leather |
| 8 | metallic_paint |
| 9 | electric_locks |
| 10 | xenon_lights |
| 11 | nitro |
| 12 | wings |
+----+------------------+
12 rows in set (0.00 sec)
No próximo artigo irei mostrar como listar dessas entidades e fazer o CRUD dos opcionais como checkboxes. Também colocarei todos os códigos, inclusive o DUMP de criação das tabelas no github: https://github.com/wbruno/examples/tree/gh-pages/checkboxes-opcionais.
Parte 2
http://wbruno.com.br/php/como-trabalhar-com-array-de-checkboxes-opcionais-2/