PHPSupabase: um cliente php para o Supabase

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

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.

É formado em Sistemas de Informação, pós-graduado em Sistemas de Banco de Dados e mestre em Educação com foco em Tecnologias Sociocomunitárias. Trabalha como professor de ensino técnico e tecnológico no Instituto Federal de Educação, Ciência e Tecnologia de São Paulo ministrando disciplinas nas áreas de programação, banco de dados, desenvolvimento de projetos e engenharia de software.

Posts relacionados

Deixe um comentário

O seu endereço de e-mail não será publicado.