Tenho dedicado minhas últimas semanas ao desenvolvimento de uma biblioteca php para integração de projetos escritos nesta linguagem com um projeto criado/hospedado no Supabase (supabase.io).
Felizmente nos últimos dias consegui finalizar a primeira versão (v0.0.1), e também consegui concluir a documentação do projeto e disponibilizá-lo no GitHub (https://github.com/rafaelwendel/phpsupabase) e também no Packagist (para que possa ser utilizado/instalado via Composer).
Para ter um alcance maior, a documentação disponibilizada no GitHub está em inglês, logo o objetivo do presente post é justamente disponibilizar o mesmo também em português. Sendo assim, nos tópicos a seguir eu vou mostrar de forma geral como usar a biblioteca PHPSupabase, desde sua instalação, a parte de criação/autenticação de usuários e os recursos para trabalhar com tabelas criadas no Supabase.
PHPSupabase
PHPSupabase é uma biblioteca escrita em linguagem php, que permite a utilização dos recursos de um projeto criado no Supabase (supabase.io), através da integração com sua API Rest.
Conteúdo
- Sobre o Supabase
- Recursos do PHPSupabase
- Instalação
- Como usar
Sobre o Supabase
Supabase é uma “alternativa open source ao Firebase”. Através dele, é possível criar um backend em menos de 2 minutos. Inicie o seu projeto com um banco de dados Postgre, Autenticação, APIs instantâneas, recursos em realtime e storage.
Recursos do PHPSupabase
- Criar e gerenciar usuários de um projeto do Supabase
- Gerenciar a autenticação de usuários (via “email” e “senha”, links mágicos, dentre outros)
- Inserir, editar, excluir e pesquisar dados no banco de dados Postgre (pela API Rest do projeto no Supabase)
- Uma classe “QueryBuilder” para filtrar dados de forma descomplicada
Instalação
PHPSupabase está disponível no Packagist, e a instalação via Composer é forma recomendável de instalá-lo. Adicione a seguinte linha no seu arquivo “composer.json”:
"rafaelwendel/phpsupabase" : "^0.0.1" |
Ou execute no seu terminal:
composer require rafaelwendel/phpsupabase |
Como usar
Para usar a biblioteca PHPSupabase você ter uma conta e um projeto criados no Supabase. Nas configurações (settings) do seu projeto (seção API), você deve anotar a API key e a URL. (OBS: Basicamente nós temos 2 sufixos para ser usado juntamente com a url: /rest/v1 e /auth/v1).
Para começar, vamos instanciar a classe Service(). Nós devemos passar no construtor dessa classe, a API key e a URL do nosso projeto.
<?php require "vendor/autoload.php"; $service = new PHPSupabase\Service( "YOUR_API_KEY", "https://aaabbbccc.supabase.co/auth/v1/" ); |
A classe Service abstrai as ações na API do projeto, e ainda provê as instâncias das outras classes (Auth, Database e QueryBuilder).
Classe “Auth”
Vamos instanciar um objeto da classe Auth
$auth = $service->createAuth(); |
O objeto $auth oferece diversos métodos para gerenciar os usuários do projeto. Através dele é possível, por exemplo, criar novos usuários ou ainda validar a autenticação de um usuário existente.
Criando um novo usuário com “email” e “senha”
Veja como criar um novo usuário com “email” e “senha”
$auth = $service->createAuth(); try{ $auth->createUserWithEmailAndPassword('newuser@email.com', 'NewUserPassword'); $data = $auth->data(); // get the returned data generated by request echo 'User has been created! A confirmation link has been sent to the '. $data->email; } catch(Exception $e){ echo $auth->getError(); } |
Esse novo usuário criado está agora na tabela de usuários do projeto e pode ser visto na seção “Authentication” do painel do Supabase. Para ser ativado, o usuário deve acessar o link de confirmação enviado para o seu email.
No terceiro parâmetro do método “createUserWithEmailAndPassword” você pode passar um array contendo os user_metadata para serem salvos (ex: name e age)
$user_metadata = [ 'name' => 'Lebron James', 'age' => '34' ]; $auth->createUserWithEmailAndPassword('lebron@email.com', 'LebronPassword', $user_metadata); |
Autenticação com “email” e “senha”
Agora vamos ver como autenticar (efetuar login) um usuário. A requisição de autenticação retorna um access_token (Bearer Token), que poderá ser utilizado posteriormente para outras ações e para verificar o tempo de expiração. Além disso, outras informações como refresh_token e dados do usuário também são retornadas. Credenciais de login inválidas resultam no disparo de uma nova exceção
$auth = $service->createAuth(); try{ $auth->signInUserWithEmailAndPassword('user@email.com', 'UserPassword'); $data = $auth->data(); // get the returned data generated by request if(isset($data->access_token)){ $userData = $data->user; //get the user data echo 'Login successfully for user ' . $userData->email; //save the $data->acess_token in Session, Cookie or other for future requests. } } catch(Exception $e){ echo $auth->getError(); } |
Recuperando os dados de um usuário autenticado
Para obter os dados do usuário, você precisa ter o access_token (Bearer Token), que foi retornado na ação de login.
$auth = $service->createAuth(); $bearerToken = 'THE_ACCESS_TOKEN'; try{ $data = $auth->getUser($bearerToken); print_r($data); // show all user data returned } catch(Exception $e){ echo $auth->getError(); } |
Atualizar (update) os dados do usuário
É possível atualizar os dados do usuário (como email e senha) e também criar/atualizar metadados, que são dados adicionais que podemos criar (como first_name, last_name, instagram_account ou qualquer outro).
O método updateUser deve ter o bearerToken como argumento. Além disso, temos mais três parâmetros opcionais, que são: email, senha e dados (array). Se você não quiser alterar alguns desses dados, apenas defina-os como nulos.
Um exemplo de como salvar/atualizar dois novos metadados (first_name e last_name) para o usuário.
$auth = $service->createAuth(); $bearerToken = 'THE_ACCESS_TOKEN'; $newUserMetaData = [ 'first_name' => 'Michael', 'last_name' => 'Jordan' ]; try{ //the parameters 2 (email) and 3(password) are null because this data will not be changed $data = $auth->updateUser($bearerToken, null, null, $newUserMetaData); print_r($data); // show all user data returned } catch(Exception $e){ echo $auth->getError(); } |
Observe que no array retornado agora, as chaves first_name e last_name foram adicionadas a user_metadata.
Classe “Database”
A classe Database fornece recursos para realizar ações (inserir, editar, excluir e pesquisar) nas tabelas do banco de dados Postgre fornecidas pelo projeto Supabase.
Para os exemplos abaixo, considere a seguinte estrutura de banco de dados:
categories (id INT AUTO_INCREMENT, categoryname VARCHAR(32)) products (id INT AUTO_INCREMENT, productname VARCHAR(32), price FLOAT, categoryid INT) |
A classe Database também é instanciada a partir do objeto $service. Você deve passar a tabela que será utilizada e sua respectiva chave primária (geralmente id).
Vamos criar um objeto para trabalhar com a tabela de categorias:
$db = $service->initializeDatabase('categories', 'id'); |
Através da variável $db é possível realizar as ações na tabela de categorias.
OBS: Se a opção de segurança Row Level Security (RLS) estiver ativada na tabela usada, passe o bearerToken para o objeto $service:
$bearerToken = 'THE_ACCESS_TOKEN'; //returned in the login action. $db = $service->setBearerToken($bearerToken)->initializeDatabase('categories', 'id'); |
Inserir (insert) dados
Inserindo um novo registro na tabela de categorias:
$db = $service->initializeDatabase('categories', 'id'); $newCategory = [ 'categoryname' => 'Video Games' ]; try{ $data = $db->insert($newCategory); print_r($data); //returns an array with the new register data /* Array ( [0] => stdClass Object ( [id] => 1 [categoryname] => Video Games ) ) */ } catch(Exception $e){ echo $e->getMessage(); } |
Agora vamos inserir um novo produto da categoria 1 – Video Games
$db = $service->initializeDatabase('products', 'id'); $newProduct = [ 'productname' => 'XBOX Series S', 'price' => '299.99', 'categoryid' => '1' //Category "Video Games" ]; try{ $data = $db->insert($newProduct); print_r($data); //returns an array with the new register data /* Array ( [0] => stdClass Object ( [id] => 1 [productname] => XBOX Series S [price] => 299.99 [categoryid] => 1 ) ) */ } catch(Exception $e){ echo $e->getMessage(); } |
Atualizar (update) dados
Para atualizar um registro no banco de dados, utilizamos o método update, passando como parâmetro o id (PK) do registro a ser atualizado e um array contendo os novos dados (OBS: Por enquanto não é possível realizar uma atualização utilizando um parâmetro (condição) diferente da chave primária).
No exemplo abaixo, atualizaremos o nome do produto e o preço do produto com id = 1 (“Xbox Series S” para “XBOX Series S 512GB” e “299,99” para “319,99”):
$db = $service->initializeDatabase('products', 'id'); $updateProduct = [ 'productname' => 'XBOX Series S 512GB', 'price' => '319.99' ]; try{ $data = $db->update('1', $updateProduct); //the first parameter ('1') is the product id print_r($data); //returns an array with the product data (updated) /* Array ( [0] => stdClass Object ( [id] => 1 [productname] => XBOX Series S 512GB [price] => 319.99 [categoryid] => 1 ) ) */ } catch(Exception $e){ echo $e->getMessage(); } |
Excluir (delete) dados
Para excluir um registro da tabela, basta chamar o método delete e passar como parâmetro o id (PK) do registro a ser deletado.
O código a seguir exclui o produto de id = 1 na tabela de produtos:
$db = $service->initializeDatabase('products', 'id'); try{ $data = $db->delete('1'); //the parameter ('1') is the product id echo 'Product deleted successfully'; } catch(Exception $e){ echo $e->getMessage(); } |
Pesquisar dados
Os métodos a seguir estão disponíveis na classe Database:
- fetchAll(): buscar todos os registros da tabela
- findBy(string $column, string $value): buscar os dados, filtrados por coluna/valor (utilizando o operador igual(=) )
- findByLike(string $column, string $value): buscar os dados, filtrados por coluna/valor (utilizando o operador LIKE)
- join(string $foreignTable, string $foreignKey): aplicar um join entre a tabela setada e uma outra tabela relacionada, e então buscar os dados
- createCustomQuery(array $args): construir uma consulta SQL customizada. As seguintes chaves são válidas para o parâmetro $args
- select
- from
- join
- where
- range
Todos os métodos mencionados retornam a própria instância da classe Database. Para acessar os dados buscados, chame o método getResult.
Veja alguns exemplos:
$db = $service->initializeDatabase('products', 'id'); try{ $listProducts = $db->fetchAll()->getResult(); //fetch all products foreach ($listProducts as $product){ echo $product->id . ' - ' . $product->productname . '($' . $product->price . ') <br />'; } } catch(Exception $e){ echo $e->getMessage(); } |
Agora, um exemplo utilizando o método findBy
$db = $service->initializeDatabase('products', 'id'); try{ $listProducts = $db->findBy('productname', 'PlayStation 5')->getResult(); //Searches for products that have the value "PlayStation 5" in the "productname" column foreach ($listProducts as $product){ echo $product->id . ' - ' . $product->productname . '($' . $product->price . ') <br />'; } } catch(Exception $e){ echo $e->getMessage(); } |
Buscando por produtos e adicionando um join com a tabela de categorias
$db = $service->initializeDatabase('products', 'id'); try{ $listProducts = $db->join('categories', 'id')->getResult(); //fetch data from "products" JOIN "categories" foreach ($listProducts as $product){ //SHOW "productname" - "categoryname" echo $product->productname . ' - ' . $product->categories->categoryname . '<br />'; } } catch(Exception $e){ echo $e->getMessage(); } |
Um exemplo de query customizada para buscar id,productname,price de todos os produtos “JOIN” categorias filtrando pelo preço (preço maior que 200.00)
$db = $service->initializeDatabase('products', 'id'); $query = [ 'select' => 'id,productname,price', 'from' => 'products', 'join' => [ [ 'table' => 'categories', 'tablekey' => 'id' ] ], 'where' => [ 'price' => 'gt.200' //"gt" means "greater than" (>) ] ]; try{ $listProducts = $db->createCustomQuery($query)->getResult(); foreach ($listProducts as $product){ echo $product->id . ' - ' . $product->productname . '($' . $product->price . ') <br />'; } } catch(Exception $e){ echo $e->getMessage(); } |
Outro exemplo de query customizada
//products with price > 200 AND productname LIKE '%n%' $query = [ 'select' => 'id,productname,price', 'from' => 'products', 'where' => [ 'price' => 'gt.200', //"gt" means "greater than" (>) 'productname' => 'like.%n%' //like operator ] ]; //products with categoryid = 1 $query = [ 'select' => 'id,productname,price', 'from' => 'products', 'where' => [ 'categoryid' => 'eq.1', //"eq" means "equal" (=) ] ]; //products with price < 1000 LIMIT 4 results $query = [ 'select' => 'id,productname,price', 'from' => 'products', 'where' => [ 'price' => 'lt.1000', //"lt" means "less than" (<) ], 'range' => '0-3' //4 first rows ]; |
Operadores de comparação
Alguns operadores disponíveis para a cláusula where
- eq: igual
- neq: não igual (diferente)
- gt: maior que
- gte: maior ou igual
- lt: menor que
- lte: menor ou igual
- like: busca por um padrão específico na coluna
- ilike: busca por um padrão específico na coluna (sem distinção de maiúsculo e minúsculo)
Classe “QueryBuilder”
A classe QueryBuilder fornece métodos para construir consultas SQL dinamicamente. Ele é instanciado a partir do objeto $service
$query = $service->initializeQueryBuilder(); |
OBS: Se a opção de segurança Row Level Security (RLS) estiver habilitado em qualquer uma das tabelas usadas, passe o bearerToken para o objeto $service:
$bearerToken = 'THE_ACCESS_TOKEN'; //returned in the login action. $query = $service->setBearerToken($bearerToken)->initializeQueryBuilder(); |
Métodos disponíveis:
- select(string $select): os campos (separados por vírgula) ou *
- from(string $from): tabela
- join(string $table, string $tableKey, string $select = null): tabela relacionada
- where(string $column, string $value): condições
- range(string $range): intervalo dos resultados
Todos os métodos mencionados retornam a própria instância da classe QueryBuilder. Para executar a consulta montada, chame o método execute. Em seguida, para acessar os dados buscados, chame o método getResult.
Um exemplo de busca de todos os dados da tabela de produtos:
$query = $service->initializeQueryBuilder(); try{ $listProducts = $query->select('*') ->from('products') ->execute() ->getResult(); foreach ($listProducts as $product){ echo $product->id . ' - ' . $product->productname . '($' . $product->price . ') <br />'; } } catch(Exception $e){ echo $e->getMessage(); } |
Um exemplo de busca de todos os dados da tabela de produtos “JOIN” categorias:
$query = $service->initializeQueryBuilder(); try{ $listProducts = $query->select('*') ->from('products') ->join('categories', 'id') ->execute() ->getResult(); foreach ($listProducts as $product){ echo $product->id . ' - ' . $product->productname . '($' . $product->price . ') - '. $product->categories->categoryname .' <br />'; } } catch(Exception $e){ echo $e->getMessage(); } |
Buscar produtos da categoria=1 e preço maior que 200
$query = $service->initializeQueryBuilder(); try{ $listProducts = $query->select('*') ->from('products') ->join('categories', 'id') ->where('categoryid', 'eq.1') //eq -> equal ->where('price', 'gt.200') // gt -> greater than ->execute() ->getResult(); foreach ($listProducts as $product){ echo $product->id . ' - ' . $product->productname . '($' . $product->price . ') - '. $product->categories->categoryname .' <br />'; } } catch(Exception $e){ echo $e->getMessage(); } |
Alguns dos operadores a serem usados no método where podem ser vistos na seção Operadores de comparação.