Ir para o conteúdo

Plug-in CreateRowOnEmptyTablePlugin

Resumo

Cria uma nova linha e a salva no objeto de dados se o evento Filter não retornar nenhuma linha.

Cadastro

O CreateRowOnEmptyTablePlugin é um plugin de ação para um evento em nível de tabela. Deve ser cadastrado para rodar após o evento Filter. Como o plugin altera o comportamento do evento Filter intrínseco, ele deve ser registrado em um objeto de dados, não em uma tabela física.

Parâmetros

Nenhum

Observações

O CreateRowOnEmptyTablePlugin determina se o evento Filter retornou alguma linha. Se o evento Filter retornou uma ou mais linhas, o plug-in será encerrado sem processamento adicional. Caso contrário, o plugin cria uma nova linha e a persiste no objeto de dados.

O plugin cria uma linha invocando o evento New. O evento New é responsável por aplicar critérios de vinculação, bem como invocar as regras padrão do objeto de dados. No contexto de uma solicitação de página da Web, os critérios de vinculação são derivados de parâmetros de cadeia de caracteres de consultar de URL. Em outras palavras, a nova linha será preenchida com quaisquer valores de parâmetro de string de consultar com nomes que correspondam aos nomes das colunas do objeto de dados.

Após invocar o evento New, o plugin invoca o evento Insert do objeto de dados, persistindo a linha na tabela de destino. Os desenvolvedores podem registrar ações no evento Insert para realizar processamento adicional. Isso é importante porque o evento Filter é um evento em nível de tabela e, portanto, não oferece suporte a ações em nível de linha, como regras CRUD. Por outro lado, o evento Insert é um evento em nível de linha. Os eventos em nível de linha suportam a maioria das ações, incluindo regras CRUD.

Se o evento Insert não for bem-sucedido porque uma ou mais regras de validação falharam, o plug-in será encerrado sem processamento adicional. Caso contrário, o plugin verifica o escopo de alteração do evento Insert para determinar se a linha deve ser atualizada. Se o escopo de alteração for definido com um valor diferente de "Nenhum", o plug-in atualizará a linha. A operação de atualização invoca o evento Filter na nova linha.

Nota

Os desenvolvedores devem considerar definir o escopo de alteração do evento Insert para acionar uma atualização se o objeto de dados contiver colunas computadas ou for selecionado em diversas tabelas. Esses valores são resolvidos apenas pelo evento Filter.

Finalmente, o plugin é encerrado, retornando a linha recém-criada. Como tudo isso acontece no contexto do evento Filter, o cliente web não tem conhecimento. No que diz respeito ao cliente web, a linha sempre existiu.

Uso

Imagine que um desenvolvedor deseja criar um link que adicione automaticamente um produto ao carrinho de compras do usuário. O link pode ser algo assim:

https://example.com/Vinyl/app/Shop/AddToCart?ProductId=1234

Neste exemplo, o link abre a página AddToCart, passando ProductId como critério de vinculação.

Suponha que a fonte de dados contenha uma tabela Cart parecida com esta:

  • CartId – Identificador exclusivo, chave primária, gerado automaticamente.
  • ProductId - Número inteiro, chave estrangeira da tabela Produto.
  • Quantidade - Inteiro.
  • SessionId – Identificador único, identifica a sessão do usuário.

O desenvolvedor começa criando um objeto de dados AddToCart que tem como alvo a tabela Cart. O objeto de dados AddToCart seleciona todas as colunas da tabela Carrinho. Embora não seja tecnicamente necessário, o desenvolvedor sinaliza o ProductId como uma coluna vinculativa. Isso garante que o ProductId esteja presente antes de executar o evento. O objeto de dados AddToCart é restrito de forma que os usuários só possam visualizar itens do carrinho pertencentes à sua sessão atual. A instrução mvSQL resultante pode ser semelhante a esta:

SELECT 
    C.CartId AS CartId,
    C.ProductId AS ProductId,
    C.Quantity AS Quantity,
    C.SessionId AS SessionId
FROM Cart AS C 
WHERE C.SessionId = session()

Observe que o CartId não precisa ser explicitamente padronizado, pois é gerado automaticamente. ProductId não é padronizado porque será originado de critérios de vinculação.

Além do objeto de dados AddToCart, o desenvolvedor cria um objeto de dados denominado MyCart. Assim como o objeto de dados AddToCart, o objeto de dados MyCart direciona e seleciona todas as colunas da tabela Carrinho. O objeto de dados MyCart também está restrito à sessão atual. No entanto, o objeto de dados MyCart não possui um evento Filter explícito. Ele não utiliza o plugin CreateRowOnEmptyTablePlugin.

O desenvolvedor então cria uma página chamada AddToCart, registrando o objeto de dados AddToCart como o objeto de dados da página. O desenvolvedor então adiciona um painel de várias linhas à página AddToCart. O painel de múltiplas linhas seleciona o objeto de dados MyCart. Além disso, o painel de múltiplas linhas está vinculado ao objeto de dados da página.

Nota

Vincular um painel à página garante que o painel não seja renderizado até que o evento Filter do objeto de dados da página seja executado.

O painel multilinha contém dois controles:

  • Produto - Lista, vinculada à coluna ProductId. Seleciona na tabela Produto usando ProductId como chave e ProductName como título.
  • Quantidade - Numérico, vinculado à coluna Quantidade.

Visualizar a página sem fornecer nenhum critério de vinculação resultará em erro.

Nota

No momento em que este artigo foi escrito, as exceções lançadas durante a execução do evento Filter do objeto de página não eram exibidas na tela. Eles, no entanto, aparecerão no log.

Se a string de consultar contiver um parâmetro chamado ProductId com um valor válido, o Vinyl executará o evento Filter do objeto de dados AddToCart, que por sua vez executa o plugin CreateRowOnEmptyTablePlugin. O plugin irá criar uma nova linha, inserindo-a na tabela Carrinho. Após a execução do evento Filter do objeto de dados AddToCart, o Vinyl executará o evento Filter do objeto de dados MyCart. Isso retornará a linha recém-criada, exibindo-a no painel de múltiplas linhas.

O desenvolvedor pode realizar ações adicionais quando a nova linha for inserida na tabela Carrinho. Por exemplo, o desenvolvedor poderia registrar uma regra CRAM que insere uma linha na tabela Session se ela não existir. A instrução mvSQL pode ser assim:

INSERT INTO Session
(
    SessionId,
    DateCreated
)
SELECT
    SessionId,
    DateCreated
FROM
(
    SELECT
    session() AS SessionId,
    now() AS DateCreated
) AS SOURCE
WHERE NOT EXISTS
(
    SELECT 1
    FROM Session
    WHERE
        Session.SessionId = SOURCE.SessionId
        AND Session.DateCreated = SOURCE.DateCreated
)

Isso permitiria ao desenvolvedor agendar um evento que excluísse carrinhos pertencentes a sessões expiradas.

Erros

Loops Infinitos

Como o CreateRowOnEmptyTablePlugin invoca o evento Filter recursivamente, há uma chance de um loop infinito. Isso ocorreria se o desenvolvedor projetasse o objeto de dados de forma que ele não retornasse a linha recém-criada. Por exemplo:

SELECT *
FROM Table
WHERE 1 = 0 

O Vinyl protege contra esta situação. Se o evento Filter não encontrar a nova linha ao realizar uma atualização, a seguinte exceção será lançada:

Loop infinito detectado ao criar uma linha em uma tabela vazia.

Segurança

Quando um navegador segue um link, ele faz uma solicitação HTTP GET. Como regra, uma solicitação HTTP GET deve ser segura e idempotente. Uma solicitação HTTP é segura se não tiver efeitos colaterais. Uma solicitação HTTP é idempotente se fazer a mesma solicitação uma segunda vez retornar o mesmo resultado.

O plugin CreateRowOnEmptyTablePlugin é idempotente: atualizar a janela do navegador não criará uma segunda linha. No entanto, o plugin CreateRowOnEmptyTablePlugin não é seguro: o plugin cria uma linha persistente no banco de dados. Este é, por definição, um efeito colateral.

Os desenvolvedores devem ter isso em mente, pois uma solicitação GET insegura pode ser explorada em um ataque de falsificação de solicitação entre sites. Por esse motivo, os desenvolvedores devem evitar qualquer operação potencialmente destrutiva, como registrar uma regra de exclusão que é executada quando a página é carregada. Os desenvolvedores também devem evitar registrar qualquer regra ou plug-in que interaja com sistemas externos, como enviar um email ou efetuar um pagamento.