Categorias
CakePHP Desenvolvimento Web PHP Projetos

Fulltext Search – Busca Textual com Postgres – Parte 3 (Final)

Encerramos aqui essa breve introdução sobre busca textual (fulltext search) com Postgres apresentando um plugin que pode te auxiliar (se você utiliza CakePHP + Postgres) na implementação da busca – caso não use, talvez sirva de inspiração para um fork.

A história desse plugin vem lá de 2015 quando precisei incluir em uma busca que desconsidera-se pequenos erros de grafia e permitia o uso de sinônimos. Tínhamos restrições de hardware para implementar a busca – era uma aplicação nova, com orçamento pequeno e dispunha de um servidor com apenas 2 GB de RAM. Usar Elasticsearch seria inviável.

Na época o CakePHP tinha recém chego a versão 3.0, com um ORM todo remodelado, muito mais flexível e extensível do que nas versões anteriores. Pensei: fácil, vou estender ele e adicionar suporte aos novos tipos de dados e índices (tsvector, GIN e GIST, como vimos anteriormente). Bom, na prática não era tão fácil, a extensibilidade ainda era pequena, havia muitas dependências acopladas. A solução foi criar um shell que era invocado a cada X minutos e regerava a tabela de buscas com os tipos e índices apropriados usando SQL puro. Funcionou e foi o suficiente porque o sistema era novo e não tinha milhões de registros – já prevíamos que escalando o banco de dados, precisaríamos de mais hardware para extrair a rotina de busca.

Mais de 6 anos se passaram, agora estamos com o CakePHP 4.2 e seu ORM muito mais flexível e desacoplado. E mais uma vez me foi dado o desafio de puxar uma busca que utilizava o Elasticsearch para dentro do banco de dados principal (Postgres). Assim nasceu o autopage/pg-search.

Considerando que você tenha uma instalação do CakePHP > 4.2.2, a instalação começa com composer:

$ composer require autopage/pg-search

Em seguida, carregue o plugin na sua aplicação:

$ bin/cake plugin load Autopage/PgSearch

Por último, precisamos configurar sua aplicação para utilizar o driver Postgres fornecido. Para isso, edite seu arquivo de configuração (ou variável de ambiente, se utilizar) config/app.php ou config/app_local.php:

// No ínicio do arquivo
use Autopage\PgSearch\Database\Driver\Postgres;
...

return [
...
// Dentro da configuração dos datasources
...
    'Datasources' => [
        'default' => [
            'driver' => Postgres::class,
...

Pronto, você já pode criar migrations (não depende disso), fixtures, Tables e querys com o CakePHP.

Uma migration poderia ser:

<?php
declare(strict_types=1);

use Migrations\AbstractMigration;
use Phinx\Util\Literal;

class CriaTabelaBuscas extends AbstractMigration
{
    /**
     * More information on this method is available here:
     * https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
     * @return void
     */
    public function change()
    {
        $tabela = $this->table('buscas');
        $tabela
            ->addColumn('original_id', 'integer', ['null' => false, 'default' => null])
            ->addColumn('nome', 'string', ['limit' => 255, 'null' => false, 'default' => null])
            ->addColumn('data', 'date', ['null' => false, 'default' => null])
            ->addColumn('conteudo', 'text', ['null' => false, 'default' => null])
            ->addColumn('conteudo_fts', Literal::from('tsvector'), ['null' => false, 'default' => null])
            ->addTimestamps('criado', 'modificado')
            ->create();
    }
}

Para salvar registros, basta popular a entidade. Repare que criei duas colunas conteudo, uma text e outra tsvector. A ideia é que a primeira contenha a forma original que exibiremos ao usuário, enquanto a tsvector é preparada para execução da busca. Na hora de popular a entidade, atribua o mesmo valor em conteudo e conteudo_fts, o driver cuidará da conversão necessária na hora de persistir.

Já uma consulta ao banco poderia ser feita com:

// Não esqueça de sanitizar antes de passar na query abaixo
$termo = $this->request->getQuery('q');
$resultados = $this->Buscas->find()
    ->where(["conteudo_fts @@ phraseto_tsquery('{$termo}')"])
    ->order(['data' => 'desc'])
    ->all();

Ainda tem um behavior que adiciona um finder especial para busca, você deve vincular ele a tabela associada com a tabela de buscas – nesse exemplo da migration, o behavior seria ligado na tabela OriginalsTable. Veja as configurações disponíveis na documentação do projeto.

É isso, com essa sequência de artigos espero ter mostrado que é possível oferecer uma busca boa o suficiente para a maioria das aplicações sem precisar estourar orçamento com hardware ou serviços externos. E claro, que essa implementação não precisa ser nenhum bicho de sete cabeças.

Se tiver alguma dúvida ou encontrar algum problema, pode usar tanto a caixa de comentários abaixo quanto o github.

Categorias
CakePHP Desenvolvimento Web Programação Projetos

Mantendo uma base de código organizada e documentada

Uma problemática comum de quem desenvolve sistemas é como manter a documentação em dia, se que isso comprometa os prazos de desenvolvimento.

Digamos que isso é um problema de otimização:

  1. um código bem documentado facilita e muito a sua manutenção;
  2. documentar código leva tempo;
  3. tempo é um recurso escasso em desenvolvimento de software;

Olha o problema… sem tempo, não há documentação e sem documentação você precisará de mais tempo para dar manutenção – oras, mas você já não tinha tempo para documentar, como vai ter mais tempo agora para dar manutenção?

Tentando equacionar esse problema surgiram várias ferramentas que visam facilitar todas as atividades relacionadas ao desenvolvimento.

Como a maior parte do meu tempo dedico ao PHP e CakePHP, tomarei estes como base para as ferramentas, porém várias delas podem ser utilizadas com outras linguagens/frameworks sem grandes problemas ou então possuem similares em outras linguagens.

Padrão de código

A primeira etapa, e talvez a mais importante, seja definir e disponibilizar um conjunto de regras explicando como o código foi escrito.

Este padrão envolve nome de classes, atributos, métodos, comentários, tabelas e colunas do banco de dados, organização de diretórios dentre outras coisas. Até coisas simples como a indentação deve ser padronizada.

Veja alguns guias de codificação para ter um exemplo do que quero dizer:

Versionamento de código

Um dos recursos mais importantes durante o desenvolvimento é a capacidade de se desfazer determinada alteração e manter um registro de todas as alterações feitas durante o desenvolvimento.

Atualmente, minha ferramenta favorita para versionamento é o Git um sistema distribuído de controle de versão. Porém existem vários outros que podem agradar, como o centralizador SVN e os também distribuídos Mercurial e Bazaar.

É muito fácil trabalhar com qualquer um destes sistemas e após conhecer as facilidades que o versionamento de código lhe proporcionam, você terá dificuldade em trabalhar com código sem controle de versão, pode apostar.

Caso você opte por um sistema distribuído, dê uma olhada neste modelo de organização para seu código: A successful Git branch model

Versionamento do Banco de Dados

Por melhor que seja o projeto do seu sistema uma coisa sempre ocorrerá: mudança. E isso envolve mais do que código, muitas vezes alterações na estrutura do banco são necessárias.

Como controlar essas alterações? A resposta é “Migrations”

No CakePHP precisamos de plugins para dar essa capacidade a aplicação, há dois largamente utilizados:

Outros frameworks fornecem suporte “nativo” ao recurso, como o Ruby on Rails e Doctrine para PHP em geral.

Testes unitários

Acredito que todos os frameworks modernos fornecem suporte a criação de testes unitários em seus projetos. Os testes são uma fase importante do design do software e fundamental para documentação de qualidade.

O CakePHP até sua versão 1.3 utiliza o framework de testes SimpleTest, porém passará a utilizar o PHPUnit em sua versão 2.0 (atualmente em desenvolvimento).

Não sabe o que são testes unitários? Bom, segue alguns links sobre o assunto:

Testes são como controle de versão… depois que você usa, não vive sem.

Documentação

Como comentado anteriormente, um passo importante para documentação são os testes unitários. Porém não devem ser o único.

Uma forma muito eficiente de documentação são os blocos de comentários, no PHP, o padrão PHPDoc é o mais utilizado.

Existem várias ferramentas que varrem o código de sua aplicação e identificam esses blocos para gerar a documentação, alguns deles são:

Quando sua documentação é concisa e completa, entender o funcionamento da aplicação passa a ser fácil, independente de quando ela foi criada. Quando isso é aliado aos testes unitários, fazer manutenção passa a ser uma atividade mais fácil e gratificante.

Por fim, é preciso saber o que/quando está errado e como/quando foi corrigido, ajudando na manutenção do histórico e acompanhamento da evolução do software. Para isso temos o tópico a seguir.

Controle de Bugs/Atividades

Como controlar o que, quando e por quem uma determinada atividade deve ser feita? E como verificar por quem e quando determinada funcionalidade foi implementada? O controle de versão pode fornecer parte destas respostas, mas ficar analisando logs normalmente não é muito comodo. A melhor maneira é utilizar uma ferramenta de controle de bugs/atividades.

Utilizo no meu dia-a-dia o excelente Redmine, um sistema simples porém poderoso para controle de tarefas. Suporta diferentes projetos, sub-projetos, integra-se com vários sistemas de versionamento de código, permite criação de wikis para documentação além de vários outros recursos.

Além deste, existem vários outros sistemas como o Bugzilla, Trac, Mantis e PHProjekt.

Conclusão?

Não, não tem conclusão. O texto visa apenas apresentar algumas atividades que juntas ao planejamento e desenvolvimento de software tendem a tornar a vida dos desenvolvedores melhor, seja diminuindo o stress causado por alterações dos requisitos ou manutenção de código mal projetado/escrito, seja tornando o desenvolvimento mais ágil, permitindo mais horas de lazer e descanso e menos fios de cabelo branco.

A intenção nunca foi cobrir todos os tópicos a exaustão, mas sim apresentar alguns exemplos e motivos para adoção de tais ferramentas/ideias. Caso tenha surgido dúvida a respeito de qualquer item, deixe um comentário =]

Sentiu a falta de algum item? Utiliza algo de forma diferente? Deixe um comentário também.

————-
Postado originalmente no blog da Radig

Categorias
CakePHP PHP Projetos

[Comitiva] Como utilizar controle de permissão no sistema – quase tudo mudou

No último post falei um pouco sobre o sistema de permissões que implantamos no Comitiva.

Acontece que após a adição de uma nova funcionalidade (submissão de trabalhos) aquele sistema de permissão começou a ficar ineficiente, e apesar de eu ter dito nos comentários que o ideal era implementar o Acl para este controle no sistema, acabei por implementar a solução sugerida pelo grande Humberto – que, tomando como ponto inicial o que tínhamos, era o jeito mais simples de solucionar os problemas.

Então o que mudou?

  • Os usuários não possuem mais um “tipo”, agora eles pertencem a “grupos” (um ou mais);
  • A verificação de permissão é feita na classe AppController, de forma genérica, o que elimina a necessidade de reescrever a função de autorização a cada controlador;
  • Os grupos que um usuário pertence ficam definidos em um campo “groups”, do tipo varchar e são guardados codificados no formato json
  • Defini um método protegido no AppController para verificar se o usuário logado pertence a um grupo qualquer, facilitando essa operação quando necessário. (o método chama-se AppController::__checkGroup($string) )

O que não mudou?

  • As ações continuam tendo como prefixo o grupo que pode acessa-la, sendo assim, a ação ProposalsController::participant_add() está disponível a todos os usuários que pertençam ao grupo “participant”
  • Todos os usuários registrados pertencem inicialmente ao grupo ‘participant’, porém podem vir a pertencer a outros grupos posteriormente (em adição ao grupo ‘participant’)
  • Continua sendo muito fácil saber se o  usuário logado pode ou não efetuar uma ação, basta usar o método supracitado __checkGroup.

Exemplo de como verificar se o usuário é administrador

if($this->__checkGroup('admin'))
    echo 'O usuário logado é administrador';
else
    echo 'O usuário logado não é administrador';
Categorias
CakePHP PHP Projetos

[Comitiva] Como utilizar controle de permissão no sistema

Como escrevi anteriormente, o PHPMS mantém um sistema para gerenciar eventos – desde a divulgação de informações, cadastro de eventos, inscrições, pagamentos, envio de mensagens para inscritos e check-in.

Mas o objetivo deste post não é dizer o que já foi dito, quero iniciar uma série de posts onde vou explicar o funcionamento de alguns recursos dentro do Comitiva, convidando todos os desenvolvedores a opinar sobre implementação e contribuir com o projeto.

O assunto de hoje é controle de acesso, então vamos ao que interessa.

Como funciona o controle de acesso no Comitiva?

Utilizamos o componente Auth do CakePHP para cuidar da autenticação (efetuar login, verificar se o usuário está logado e efetuar logout).
Mas precisávamos ir um pouco além da configuração básica, definindo diferentes opções para diferentes tipos de usuários. Para isso, consideramos duas opções iniciais: usar também o Acl Behavior ou ficar apenas com o AuthComponent e criar diferentes actions para diferentes tipos de usuários.

Como possuímos apenas dois tipos de usuários (admin e participant) decidimos pela que seria mais simples inicialmente – mesmo sabendo que a manutenção no futuro poderia ser mais complicada – que foi criar diferentes actions para os tipos de usuários.

Para criar essa funcionalidade, definimos inicialmente um prefixo para cada tipo de usuário (prefixos ‘admin‘ e ‘participant‘). Em seguida configuramos o AuthComponent desta maneira (código extraído do AppController):

//Configure AuthComponent
$this->Auth->authorize = 'controller';

if( !isset($this->params['prefix']) || !( in_array($this->params['prefix'], Configure::read('Routing.prefixes')) ) )
{
	// all non-prefixed actions are allowed
	$this->Auth->allow('*');
}

Ou seja, toda ação que não tiver prefixo ou o prefixo não estiver definido nas configurações de rota serão públicas (assim é possível, por exemplo, deixar uma página com instruções acessível à qualquer usuário).

Mas como fica essa verificação de tipos no controlador? Como definimos o método de autorização do Auth como ‘controller’, precisamos definir em todos os controladores o método isAuthorized que deve retornar true quando o acesso é aprovado e false caso contrário.
Seguindo a ideia de que cada tipo de usuário possui um prefixo próprio nas ações, nosso método isAuthorized fica desta forma:

if($this->userLogged == TRUE && $this->params['prefix'] == User::get('type'))
{
	return true;
}

return false;

Onde o atributo $this->userLogged pertence a classe AppController e sua função é reduzir chamada ao método $Auth->login() e na segunda parte da condição temos uma comparação entre o prefixo da url acessada e o tipo do usuário – se ambos forem iguais, então o acesso está liberado.

Por fim, devemos criar nossas ações para cada tipo de usuário, onde o prefixo será usado para validar o acesso à aquela ação, veja o exemplo da ação “listar pagamentos”:

// ação exclusiva para admin - tem acesso aos pagamentos de todos os outros usuários
public function admin_index($event_id = null)
{
	$this->Subscription->recursive = 0;
		
	if(is_numeric($event_id))
	{
		$this->paginate = array(
			'conditions' => array(
				'event_id' => $event_id
			)
		);
	}
		
	$this->set(compact('event_id'));
	$this->set('subscriptions', $this->paginate());
}

// ação exclusiva para participant - tem acesso somente aos próprios pagamentos
public function participant_index()
{
	$this->Subscription->recursive = 0;
	$this->set('subscriptions', $this->paginate(array('user_id' => User::get('id'))));
}

Como podem ver, isso permite a criação de diferentes regras de negócio para os diferentes tipos de usuários e embora aumente a quantidade de código “redundante” por repetir alguns procedimentos para todos os tipos de usuários, a leitura e entendimento do código é facilitada – basta ver o prefixo do método (ação) para saber quem terá acesso a ele.

Categorias
noticias Projetos

Delivery Fácil – Pedir comida na internet é muito fácil

Há mais ou menos três semanas foi lançado um novo serviço para o pessoal de Campo Grande/MS, o Delivery Fácil. Através dele você pode conferir o cardápio de diversos estabelecimentos em Campo Grande que fazem entrega de comida e pedir seu prato pelo próprio site – simples assim.
Você não se preocupa com telefone ocupado, não precisa ficar perguntando o que tem no cardápio, quanto custa cada prato nem se atendem em sua região.
Além disso, todo pedido realizado pelo sistema te da pontos para trocar por prêmios.

No lado do restaurante, o serviço cria mais um canal de venda sem a necessidade de se investir um grande volume de dinheiro para criar um e-commerce.

O serviço é resultado de uma parceria entre a Radig (onde tenho prazer em trabalhar) e dois grandes amigos, Samuel e Habib.

Banner de divulgação do Delivery FácilFaça uma visita, cadastre-se e peça sua comida preferida com todo o conforto que a internet lhe oferece.

Não deixe de seguir o Delivery Fácil no twitter para acompanhar as novidades e promoções que realizarmos: @deliveryfacil

Categorias
CakePHP Projetos

Comitiva – Cuide bem do seu evento

Na última semana o PHPMS realizou seu 3º Workshop, que mais uma vez, foi um sucesso.

Como comentei no post anterior, dessa vez minha participação foi focada em um ponto: criar uma solução para gerenciar as inscrições do evento.

Dessa necessidade surgiu o Comitiva, um sistema de gerenciamento de eventos, que nasceu a pouco e já passou pela sua primeira prova de teste (o próprio workshop).

No momento ele pode ser usado para cadastrar um evento, permitir que usuários se cadastrem e se inscrevam nos eventos cadastrados, mas vários outros recursos já estão em andamento.

A parte boa é que o sistema é livre – sim, open source, é gratuito e feito em CakePHP 1.3. Estamos utilizando o Github para gerenciar o código e hoje criei uma conta no LighthouseApp para gerenciar as tarefas. Aliás, queria agradecer e recomendar ambos os serviços que suportam, sem custos, projetos open source. Valeu =]

Veja um demo na versão instalada para o PHPMS: http://comitiva.phpms.org

Bom, como uma andorinha só não faz verão, o Comitiva contou com alguns colaboradores/apoiadores que gostaria de citar e agradecer aqui:

Zé Agripino: fez boa parte do trabalho sujo, hehehe. Criar as views, traduzir textos, corrigir bugs. Ajudou em todo o sistema, é O cara;
Felipe V. Rigo: começando a trabalhar com CakePHP agora, apanhou um pouco para conseguir rodar o sistema em sua máquina, mas graças a isso e sua persistência descobrimos e corrigimos vários bugs do sistema;
José Ricardo: estava ocupado no inicio do projeto, porém após o workshop ficou animadão e já fez algumas contribuições importantes, como a configuração do Migrations para o sistema;
Bruno Porkaria: também estava bem corrido e cuidando de outras coisas do workshop, mas testou bastante o sistema e teve a paciência de reportar problemas e sugestões sempre que estas apareciam.

É isso aí, espero trazer mais novidades em breve, principalmente sobre o projeto =]

Categorias
Desenvolvimento Web PHP Projetos

Últimas Novidades #2

Vamos lá a mais notícias que talvez vos interessem ;]

  1. O PHPMS realizará no dia 11 de Julho seu primeiro workshop: PHP Extremo. A idéia é realizar um evento voltado a profissionais que trabalham com PHP, dando uma oportunidade para reciclar e expandir seus conhecimentos. Os temas ainda não foram decididos e a chamada de trabalhos deve ser iniciado até segunda feira próxima. Para mais informações, acesse: http://workshop.phpms.org;
  2. O PHP Mobile continua de vento em polpa, acompanhe as notícias sobre o projeto seguindo o twitter ( @phpmobile ) e site: www.phpmobile.com.br;
  3. Minha empresa está indo muito bem obrigado, pretendo disponibilizar alguns artigos e ferramentas de trabalho do dia-a-dia no site dela em breve. Se estiver precisando de algum serviço relacionado a TI, entre em contato. Será um prazer atende-lo;
  4. Atualizei o WordPress aqui do Blog (WP 2.8), de início a área administrativa ficou bugada, acusando falta de memória, tive de atualizar o php.ini aumentando o limite de memória de 32MiB para 64MiB. Achei um absurdo o sistema usar 64MiB (acho que 40MiB já seriam suficiente, mesmo assim achei muiita coisa);
  5. O Joomla lançou atualização do CMS, versão 1.5.11, possui 3 correções de segurança que devem ser aplicadas imediatamente (se você ainda não a fez);
  6. Ainda hoje atualizo o tema aqui do blog.
  7. Minha idéia dos scripts para administração está em andamento, infelizmente perdi acidentalmente uma das minhas classes principais e estou tendo de reescreve-la (como já havia comentado antes). Assim que tiver o mínimo de funcionalidade divulgo aqui.

Por último, meu notebook chegou (no dia 10 de Junho de 2009) exatos 30 dias após o início da produção por parte da Dell – apesar de antes de fazer a compra ver estampado no site que notebooks montados no Brasil tinham prazo de 10 dias úteis para entrega (14 dias corridos). Não bastace a grande demora na entrega, o notebook veio faltando uma das peças que pedi e paguei. Entrei imediatamente em contato com a Dell, pediram que enviasse um email para <pessoa>@dell.com.br para que resolvessem o problema. Mais uma vez demora, mandei o email no dia 11 de Junho, hoje, dia 13 não recebi resposta alguma. O notebook possui desempenho satisfatório mas o antedimento da Dell me decepcionou, dúvido que a Dell norte-americana trate seus clientes assim.

Enfim, se for comprar um computador, escolha um HP, Apple, Acer ou Sony, possuem qualidade igual ou melhor a da Dell e respeitam seus clientes.

Categorias
noticias Projetos

Últimas novidades

Faz um tempinho que não posto nada, vida corrida como sempre e alguns imprevistos…

Primeiro, estou sem meu velho companheiro que me acompanhou nestes quase 3 anos: um Acer Aspire 5610. Precisava de uma máquina um pouco mais rápida e quando surgiu a oportunidade de vender meu velho companheiro, tratei logo de procurar um substituto.
Escolhi um Dell Vostro 1510, C2D T8100, 4GiB de RAM, HD 250GiB 7200RPM, nVidia 8400GS e algumas outras coisinhas. O único problema é que a entrega está demorando um pouco mais do que planejei (recebi a confirmação de compra na segunda-feira 11/05 e até agora o produto está em produção). Paciência =]

Segundo: a Radig está a todo vapor, estamos com vários projetos em andamento e as perspectivas são muito boas.

Terceiro: estou participando de um projeto do meu amigo Bruno Porkaria, o PHP Mobile e ele também está a todo vapor. Confira a página do projeto para entender mais sobre.

Por último, tive a idéia de condensar todas as pequenas classses e scripts que utilizo na manutenção de páginas e seus hosts em uma única interface, porém descobri que esqueci de fazer backup de algumas delas, então terei de reescrever boa parte. Em breve falo mais a respeito, a idéia é incluir métodos para migração de uma página de um provedor para outro, sem precisar de acesso SSH (somente PHP e FTP) e de forma automática, corrigir nomes de arquivos enviados pelo usuário (que possuem acentuação ou espaços), fazer backup de diretórios e recuperar estes backups. Enfim, as idéias são muitas, falta colocar tudo em prática.

Pretendo também alterar o layout do blog, não estou satisfeito com a formatação do conteúdo, acho que a leitura dos textos está sendo prejudicada pela fonte e espaçamentos.

Em breve dou mais detalhes sobre tudo que citei.

Categorias
Desenvolvimento Web Projetos

Projetos – começando

Gerenciar projetos não é uma tarefa fácil, em qualquer área é assim, e não poderia ser diferente no desenvolvimento de softwares.

Com este post, iniciarei uma nova série sobre o tema. Minha intenção é aprofundar os meus conhecimentos no assunto, enquanto ajudo outros, que como eu, estão começando sua carreira nesta área.

Como “aperitivo”, indico a leitura do texto: EXPECTATIVA: o segredo de sucesso em projetos.

Em breve darei minhas primeiras palavras.