Firebase Database não é bagunça!
Sim, temos regras e validações e vc irá se surpreender
Psiu! Não conte a ninguém, mas até "foreign keys" tem, maluco né?!
A primeira impressão que se tem de uma base de dados não relacional como o Firebase, é de que ela simplesmente aceita tudo... na realidade aceita rsrs, mas podemos limitar colocando regras e é isso que iremos ver nesse artigo =D
A principal vantagem, que eu vejo numa base nosql, é não ter schema \o/, passei grande parte da minha vida escrevendo tabelas, tipando cada coluna, definindo PKs e FKs.... saudades #SQN!! kkk Vamos lembrar de como era, apenas para termos uma comparação Vamos criar uma tabela de estado, e uma tabela de pessoa, com suas devidas PKs e FKs
CREATE TABLE Estado ( EstadoId int NOT NULL PRIMARY KEY, Sigla varchar(2) NOT NULL ); CREATE TABLE Pessoa ( PessoaId int NOT NULL PRIMARY KEY, Nome varchar(50) NOT NULL, SobreNome varchar(50), email varchar(30) NOT NULL, Idade int, Cidade varchar(50), EstadoId int ); ALTER TABLE Pessoa ADD FOREIGN KEY (EstadoId) REFERENCES Estado(EstadoId);
Ficaram saudosistas né? rsrs Vamos fazer algo parecido com json, bom, como não temos como a definição iremos colocar os dados também "estados" : { "AC" : true, "AL" : true, "AM" : true, "AP" : true, "BA" : true, "CE" : true, "DF" : true, "ES" : true, "GO" : true, "MA" : true, "MG" : true, "MS" : true, "MT" : true, "PA" : true, "PB" : true, "PE" : true, "PI" : true, "PR" : true, "RJ" : true, "RN" : true, "RO" : true, "RR" : true, "RS" : true, "SC" : true, "SE" : true, "SP" : true, "TO" : true } "pessoas": [ { "nome": "Evelyn", "sobrenome": "Mendes", "email": "evelyn@transnerd.com", "idade": 35, "cidade": "Porto Alegre", "estado": "RS" } ]
Diferente né? Mas até ai são apenas dois json que não tem relação nenhnuma com o outro, e que no caso aceitam qualquer valor em suas chaves, um exemplo: Vamos imaginar que alguém ao invés de colocar 35, um número inteiro, coloque qualquer coisa no valor dessa chave "idade": "trinta e cinco" eu acho que não ficaria legal e prejudicaria muito uma pesquisa. Para resolver isso iremos usar as regras do banco de dados do firebase Ao entrar nas regras vc verá algo definido bem simples { "rules": { ".read": true, ".write": true } }
Aonde diz que toda a base de dados pode ser lida e gravada livremente por qualquer usuário. Mas isso não é nada legal, quem sabe a gente deixa apenas que leiam, mas não que escrevam? E como fazemos isso? O Firebase tem server variables, uma delas é a auth, que traz o usuário logado e com essa variável podemos fazer uma simples modificação. { "rules": { ".read": true, ".write": "auth != null" } }
Pronto, agora somente usuários logados podem escrever na base de dados =D Dai vc pensa, e dai, grandes coisas, quero mais, muiiito mais!!!! Sim, temos mais! Agora vamos voltar a situação da idade e validar para apenas receber inteiros. Regras novamente { "rules": { ".read": true, ".write": "auth !=null", "usuarios": { "$uid": { ".read": true, ".write": "auth.uid == $uid", ".validate": " newData.child('idade').isNumber()" } } } }
Perceba a condição: ".validate": " newData.child('idade').isNumber()"
Ela validará se somente números serão inputados para a chave idade Outra coisa que vc deve ter notado é o $uid, ele é um curinga (wild card), digo, nesse caso ele representa o id do usuário que gravou o registro, por isso que em .write, exite "auth.uid == $uid", somente o dono do registro poderá escrever nele, o auth, como eu já disse é um server variable Existem 6 server variables, vamos a elas Server Variables do Firebase auth É o usuário autenticado data São os dados existentes no caminho, exemplo, pessoa/idade newData São os dados postados no caminho, esse variável só está disponível em .write e .validate pq somente nesses momentos que novos dados serão postados now É uma marcação de data e hora de tempo real do servidor root Levará de volta a raiz do banco de dados $ Curinga, como usamos em $uid para pegar o id do usuário do registro Voltando a situação de validações, podemos definir tb que os campos textos somente receberão strings e terão um tamanho determinado, vamos ver como fica no campo nome que além de tudo não pode ser nulo? { "rules": { ".read": true, ".write": "auth !=null", "usuarios": { "$uid": { ".read": true, ".write": "auth.uid == $uid", ".validate": "newData.child('idade').isNumber() && newData.child('nome').exists() && newData.child('nome').isString() && newData.child('nome').val().length < 51 " } } } }
Podemos repetir isso para os demais campos { "rules": { ".read": true, ".write": "auth !=null", "usuarios": { "$uid": { ".read": true, ".write": "auth.uid == $uid", ".validate": "newData.child('idade').isNumber() && newData.child('nome').exists() && newData.child('nome').isString() && newData.child('nome').val().length < 51 && newData.child('sobrenome').isString() && newData.child('sobrenome').val().length < 51 && newData.child('cidade').isString() && newData.child('cidade').val().length < 51" } } } }
A partir daqui a brincadeira começa a ficar mais interessante. Vcs viram que tem um campo de email não é? Quem sabe a gente não valida se é um endereço de email válido?????? Vamos, vamos?!?! =D Para isso usaremos as famigeradas expressões regulares uhuhuh { "rules": { ".read": true, ".write": "auth !=null", "usuarios": { "$uid": { ".read": true, ".write": "auth.uid == $uid", ".validate": "newData.child('idade').isNumber() && newData.child('nome').exists() && newData.child('nome').isString() && newData.child('nome').val().length < 51 && newData.child('sobrenome').isString() && newData.child('sobrenome').val().length < 51 && newData.child('cidade').isString() && newData.child('cidade').val().length < 51 && newData.child('email').isString() && newData.child('email').val().matches(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$/i)" } } } }
Viram que lindo????? Pequeno ponto de observação, regExp para emails nem sempre conseguem suprir todas as necessidades, portanto cautela ao usar. E o estado??? Até agora apenas temos aquela lista json separada lá, mas sei que ele tb deve entrar no nosso objeto pessoa, mas só se existir na tabela estados... hummm vc quer dizer uma FK? Nãooo, quero apenas validar se existe o valor, coisa simples, nada de mais, mas como fazemos isso? Iremos acessar a tabela estados usando a server variable root, e com ela verificar se o dado inputado está contido nela. Vamos lá!? Antes de colocarmos as regras é necessário dizer que o estado pode ser uma tabela que não deverá ser modificada de fora da base de dados, pois seus dados quase nunca são alterados, o que se pode fazer é deixar .read e .write como false, assim somente entrando no console>realtimedatabase vc poder alterar os valores, adicionar e excluir. { "rules": { ".read": true, ".write": "auth !=null", "estados" :{ ".read": false, ".write": false, }, "usuarios": { "$uid": { ".read": true, ".write": "auth.uid == $uid", ".validate": "newData.child('idade').isNumber() && newData.child('nome').exists() && newData.child('nome').isString() && newData.child('nome').val().length < 51 && newData.child('sobrenome').isString() && newData.child('sobrenome').val().length < 51 && newData.child('cidade').isString() && newData.child('cidade').val().length < 51 && newData.child('email').isString() && newData.child('email').val().matches(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$/i) && root.child('estados').child(newData.child('estado').val()).exists()" } } } }
Olhem como é fácil entender o que ele faz, ele vai até a tabela estados root.child('estados')
referencia os filhos passando o valor que veio objeto newData .child(newData.child('estado').val())
e por fim pergunta se esse valor existe na coleção .exists()
Não é simples?? root.child('estados').child(newData.child('estado').val()).exists()
Para entender melhor root.child('estados').child('RS').exists()
Existe o estado!!!!!!!!!! "estados" : { "RS" : true, }
E como vou saber se isso tudo está certo? Sim, eu sei que é um pouco complexo e subjetivo em alguns momentos, é por isso que se usa o simulador das regras. Exemplo de gravação com tudo ok Exemplo de gravação com idade errada, como string Também podemos testar a leitura dos dados sem autenticar O Firebase database é uma ferramenta muito poderosa, esses exemplo do artigo chegam a ser simples para tanta coisa que ele pode oferecer. Aos poucos vamos falando mais sobre tudo que o BAAS (backend as a service) firebase pode nos oferecer. Espero muito que tenham gostado Obrigada!!! <3 Atualização Firebase Firestore 4/10/2017 11:48 Ontem lançaram a nova base de dados nosql do firebase, o firestore, dai resolvi dar uma pequena amostra de como funcionam as validações dentro dele, um pouco diferente, mas o conceito é o mesmo. O legal, tem funções nas validações :) O não legal, não é mais json e não tem simulador, pelo menos até agora dentro console web não tinha :( service cloud.firestore { match /databases/{database}/documents { // match /{document=**} { // allow read, write: if true; //} match /contatos/{contatos}{ allow read; allow write: if request.resource.telefone is number && isAuthenticated() && isEmail(request.resource.email) && request.resource.size() == 6 && exists(/databases/$(database)/documents/estados/$(request.resource.estado)); } } function isAuthenticated() { return request.auth != null; } function isEmail(email){ return email.matches('\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}\\b'); } }